Upgrade to Pro — share decks privately, control downloads, hide ads and more …

継続的な進化を見据えた、3年越しのレポート機能リファクタリング

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →
Avatar for freee freee PRO
October 20, 2026

 継続的な進化を見据えた、3年越しのレポート機能リファクタリング

freee 技術の日 2026のセッション資料です。

Avatar for freee

freee PRO

October 20, 2026

More Decks by freee

Other Decks in Technology

Transcript

  1. 背景と課題:顕在化していた2つの⼤きな課題 ①競合劣後と機能追加の限界 ②パフォーマンスの悪化 • 他社の会計ソフトには当たり前にあ る機能がfreeeには⽋けている ◦ 税込税抜切り替え ◦ タグ(補助科⽬)並び替え

    • 画⾯の表⽰速度や挙動の重さがユー ザー体験を著しく損ねている ◦ 本来提供すべき価値を届けら れない状態 • しかし、コードが複雑化しすぎてい たため、安易に機能を追加して改善 を図ることすら困難 • 今後も開発が続けられる最重要画⾯ の⼀つであるため、根本的な基盤の 刷新が必要不可⽋
  2. パフォーマンス改善の⼤枠 Controller / Serializer ①リファクタリング QueryService AggregateService 集計基盤サービス • •

    複雑になった依存関係の解きほぐし 集計基盤サービスの活⽤ ・バリデーション ・行ごとの親子階層づけ ・集計結果の行変換
  3. 集計基盤サービス 売上⾼クロス集計:「今年の」「売上⾼」を「品⽬と部⾨」で「SUM」したい 集計基盤サービス GroupedAggregateValueRepository.new( company: company, fiscal_years: [fy], grouping_elements: ['取引先

    ', '品目 '], aggregate_types: ['発生額'], start_date: fy.start_date, end_date: fy.end_date, account_item_ids: [売上高 ], ).fetch_grouped_aggregate_values (相当する)SQL SELECT item_id, section_id, SUM(amount) AS sales_amount, FROM journal_lines WHERE company_id = 123 AND account_item = '売上高' AND booked_on BETWEEN … GROUP BY item_id, section_id
  4. 集計基盤サービス 集計は考えることが多い! • 検索期間に合わせて、⽉毎の集計テーブルと、取引テーブルから効率よくデータを 取る必要がある • • • ⽉毎の集計テーブルが使えない検索条件がある •

    • 税込経理と税抜経理では、期⾸消費税額の繰り越し⽅が違う うるう⽇始まりの会計年度や、年度をまたいだ期⾸残⾼の引き継ぎの考慮が必要 収益は貸⽅、費⽤は借⽅が正、といったレポートごとの符号⽅針を切り替える必要 がある etc etc …
  5. 集計基盤サービス 集計基盤サービス 書き方 隠蔽するもの SQL GroupedAggregateValueRepository.new( company: company, fiscal_years: [fy],

    grouping_elements: ['取引先 ', '品目 '], aggregate_types: ['発生額'], start_date: fy.start_date, end_date: fy.end_date, account_item_ids: [売上高 ], ).fetch_grouped_aggregate_values SELECT partner_id, item_id, SUM(amount) AS sales_amount, FROM journal_lines WHERE company_id = 123 AND account_item = '売上高' AND booked_on BETWEEN … GROUP BY partner_id, item_id 検索条件ごとのデータ取得テーブルの違い 閏日の考慮 勘定科目ごとの正負反転 期の締め状態や決算整理仕訳の扱いの違い 経理方式ごとの消費税の繰り越し方の違い クエリごとの実行計画の違い テーブルの物理配置(ディスク・ページ) インデックスの選択 join の順序と方式 並行制御(ロック・MVCC) など など
  6. パフォーマンス改善の⼤枠 Controller / Serializer • 複雑な集計ロジックは集計基盤サービスに ⼀任 • 上層のサービスは、本レポートのドメイン ロジックに専念できるようになった

    • 散逸していたクエリが集計基盤サービスに 集約されたことで、結果としてクエリの⾒ 直しも⾏われた QueryService AggregateService 集計基盤サービス 🎉複雑度の低減&パフォーマンスの向上を達成🎉 指標 OLD NEW メソッド数 164 95 差分 -69 総循環的複雑度 334 256 -78 (-23%) ※月次推移 PL部分だけで算出 循環的複雑度は RubocopのMethodComplexity#complexity相当のロジックで計算 ・バリデーション ・行ごとの親子階層づけ ・集計結果の行変換
  7. パフォーマンスの課題解決 「仮想化」を導⼊ 行DOM非表示 • • 表示 行DOM非表示 表ライブラリに導⼊されている「仮想化」機能を 有効化 ◦

    スクロールに応じて、⾒えている コンポーネントだけ表⽰する リレンダー対象のコンポーネントが減ることによ り、インタラクションにかかる時間も⼤幅に低減 された