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

大規模モノレポの秩序管理 失速しない多言語化フロントエンドの運用

Avatar for shoota shoota
November 16, 2025

大規模モノレポの秩序管理 失速しない多言語化フロントエンドの運用

JSConf 2025 / Findy Sponsor Key note

Avatar for shoota

shoota

November 16, 2025
Tweet

More Decks by shoota

Other Decks in Technology

Transcript

  1. © Findy Inc. ⼤規模モノレポの秩序管理 1 トラックD 熊野 修太 / Shuta

    Kumano 失速しない多⾔語化フロントエンドの運⽤ 2025.11.16
  2. © Findy Inc. 2 @shoota 熊野 修太 [くまの しゅうた] ファインディ株式会社

    Team+開発部 Enablementチーム / フロントエンドリード フロントエンドリード、スクラムマスター、エンジニア リングマネージャーを経験 Findy Team+に魅⼒を感じ2023年1⽉よりジョイン 同2023下期に全社MVP受賞 フルリモート in ⻘森
  3. © 2024 Findy Inc. 挑戦するエンジニアの プラットフォームをつくる。 ビジョン つくる⼈がもっとかがやけば、 世界はきっと豊かになる。 経営理念

    会社概要 会社名 ファインディ株式会社 / Findy Inc. 代表取締役 ⼭⽥ 裕⼀朗 設⽴ 2014 年 2 ⽉ ※ 本格的な事業開始は2016年7⽉ 社員数 297 名 資本⾦ 18 億 5,043 万円 ※ 資本準備⾦含む 住所 東京都品川区大崎1-2-2 アートヴィレッジ大崎セントラルタワー 5階 事業許可番号 13-ユ-308478 サービス ‧ スカウト型リクルーティングサービス「Findy」 ‧ ハイスキルな業務委託エンジニア紹介サービス「Findy Freelance」 ‧ エンジニア組織⽀援SaaS「Findy Team+」 ‧ 開発ツールに特化したレビューサイト「Findy Tools」 投資家 グローバル‧ブレイン、ユナイテッド、SMBCベンチャーキャピタル、KDDI、JA三 井リース、みずほキャピタル、博報堂DYベンチャーズ、Carbide Ventures、等 3
  4. © Findy Inc. 11 Introduction LeanとDevOpsの科学 注⼒すべきはデプロイとテストの容易性 ❏ ハイパフォーマンスな組織を作るうえで重要 >

    テストの⼤半を、統合環境を⽤意せずに実施できる > アプリケーションを、それが依存するほかのアプリ ケーションやサービスから独⽴した形で、デプロイま たはリリースできる
  5. © Findy Inc. 12 Introduction つまり、 ❏ 階層化されたテストによってソフトウェアの品質を担保しなさい ❏ それぞれのテスト階層は独⽴させ、閉じた状態で品質を作り込みなさい

    ❏ すべてのテストは10分以内で終わらせなさい ❏ テストの容易性を保ち、独⽴したデプロイを実現しなさい _(´ཀ`」 ∠)_
  6. © Findy Inc. 16 技術スタック ❏ graphql-codegen / Apollo client

    ❏ eslint / prettier ❏ Storybook ❏ Playwright (E2E) ❏ i18next / react-i18next ❏ React / TypeScript ❏ Vite / Vitest リポジトリ構造
  7. © Findy Inc. 17 技術スタック ❏ graphql-codegen / Apollo client

    ❏ eslint / prettier ❏ Storybook ❏ Playwright (E2E) ❏ i18next / react-i18next ❏ React / TypeScript ❏ Vite / Vitest with リポジトリ構造
  8. © Findy Inc. 19 Nxの機能と恩恵 1. Code generatorの標準装備 2. モノレポ内の依存関係の⾃動検出と変更検知

    3. モノレポ内の依存関係の制約と管理 4. コマンド結果のキャッシュ 5. ライブラリマイグレーション リポジトリ構造 https://tech.findy.co.jp/entry/2024/08/05/090000
  9. © Findy Inc. 20 Nxの機能と恩恵 1. Code generatorの標準装備 2. モノレポ内の依存関係の⾃動検出と変更検知

    3. モノレポ内の依存関係の制約と管理 4. コマンド結果のキャッシュ 5. ライブラリマイグレーション リポジトリ構造 https://tech.findy.co.jp/entry/2024/08/05/090000
  10. © Findy Inc. 24 Nxの機能と恩恵 リポジトリ構造 ❏ モノレポ内の依存関係の制約と管理 ❏ Nx独⾃のeslint

    ruleを採⽤することで実現 ❏ moduleにtagを指定し、tag間の依存可否をeslint ruleに記述する。 例1) feature から ui には依存できるが、 ui から feature は依存できない 例1) feature 同⼠では依存できない UI shared feature feature feature app app
  11. © Findy Inc. ❏ モノレポのもつmoduleが125個 ❏ 機能(画⾯)moduleが112 個 ❏ UI

    やデザインを内包したComponent moduleが3個 ❏ 通信関連、ドメイン共通定義、その他ワークアラウンドが 7個 ❏ ユーティリティ関数関連が 2 個 ❏ 多⾔語化モジュールが 1個 26 現時点のコード規模 リポジトリ構造
  12. © Findy Inc. 30 多⾔語化プロジェクト 多⾔語化モジュールの構築 ❏ 多⾔語化の同時並列作業&確認作業に全振り ❏ Pull

    Request数が当時⽐で2.5倍以上 ❏ 3ヶ⽉弱で3000以上のセンテンスの対象抽出と⽇英翻訳処理が完了
  13. © Findy Inc. 31 初期の翻訳アーキテクチャ 多⾔語化モジュールの構築 ❏ i18nモジュールが全ての翻訳リソースを持つ ❏ 翻訳リソースはモジュール名ごとに名前空間とファイルを分離

    ❏ 機能モジュールごとにファイル分割されているため変更範囲が理解しやすい ❏ i18nモジュールが⼀元管理することで翻訳リソースの管理がシンプル
  14. © Findy Inc. 凝集度の7段階 ❏ 機能的凝集(Functional Cohesion) ❏ 1つの明確な機能のみにまとまっている ❏

    逐次的凝集(Sequential Cohesion) ❏ ⼀⽅の出⼒がもう⼀⽅の⼊⼒になる。メソッドチェーン。 ❏ 通信的凝集(Communicational Cohesion) ❏ 同じデータを扱う部分を集めている ❏ ⼿続き的凝集(Procedural Cohesion) ❏ 時間的凝集(Temporal Cohesion) ❏ 論理的凝集(Logical Cohesion) ❏ 偶発的凝集(Coincidental Cohesion) 32 多⾔語化モジュールの構築 アーキテクチャと凝集度
  15. © Findy Inc. 32章 凝集 34 多⾔語化モジュールの構築 Tidy First? >

    結合した要素は同じ親要素の⼦要素同⼠であるべきだ。 > これが凝集(Cohesion)の1つめの意味合いだ。
  16. © Findy Inc. 36 初期アーキテクチャの問題点 - CI速度の劣化 多⾔語化モジュールの構築 ❏ i18n

    moduleの変更頻度が想定よりも多い ❏ 辞書jsonの変更がi18n moduleの変更として認知される(考慮もれ) ❏ 機能変更の多くが辞書とセットなので、i18nが定常的に影響範囲になる nx affected の理解不⾜ 変更単位
  17. © Findy Inc. 37 初期アーキテクチャの問題点 - CI速度の劣化 多⾔語化モジュールの構築 ❏ i18n

    moduleに依存が集中 ❏ ほぼすべてのmoduleがi18nの影響範囲になった ❏ i18nの変更が発⽣するとほぼすべてのmoduleのCIが必要 nx affected が無⼒に
  18. © Findy Inc. ❏ 急激な機能追加で毎⽉、複数の新しいmoduleが増え、 影響範囲と変更頻度が同時に成⻑ 38 初期アーキテクチャの問題点 - CI速度の劣化

    多⾔語化モジュールの構築 moduleが 増える CI対象が 増える 変更頻度が 増える 実際には影響がないのに、 ⼤量のテストを⾼頻度に実⾏
  19. © Findy Inc. 40 初期アーキテクチャの問題点 - ビルドサイズの肥⼤化 多⾔語化モジュールの構築 ❏ 他のmoduleと同様、i18n

    moduleは独⽴したモジュールとしてビルド ❏ i18nクライアント+翻訳リソース(JSON) ❏ どのSPA画⾯を開いても数MBの翻訳モジュールをロード アプリケーションを、それが依存するほかのアプリケーション やサービスから独⽴した形で、デプロイまたはリリースできる ‧すでに6000以上のセンテンスが登録済み ‧各センテンスが2⾔語で登録されており、3⾔語⽬の適⽤を⽬指してい る
  20. © Findy Inc. 凝集度の7段階(の良い⽅) ❏ 機能的凝集(Functional Cohesion) ❏ 1つの明確な機能のみにまとまっている ❏

    逐次的凝集(Sequential Cohesion) ❏ ⼀⽅の出⼒がもう⼀⽅の⼊⼒になる。メソッドチェーン。 ❏ 通信的凝集(Communicational Cohesion) ❏ 同じデータを扱う部分を集めている 44 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ
  21. © Findy Inc. 凝集度の7段階(の良い⽅) ❏ 機能的凝集(Functional Cohesion) ❏ 1つの明確な機能のみにまとまっている ❏

    逐次的凝集(Sequential Cohesion) ❏ ⼀⽅の出⼒がもう⼀⽅の⼊⼒になる。メソッドチェーン。 ❏ 通信的凝集(Communicational Cohesion) ❏ 同じデータを扱う部分を集めている 45 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ 通信(graphql)モジュールと同じデザインパターンが 適⽤できるのでは?
  22. © Findy Inc. graphql モジュールの設計 ❏ 実⾏インスタンスはSingletonでmoduleにカプセル化 ❏ 各々のmoduleがDocument(静的リソース)を内包する ❏

    Documentを利⽤する⼿段(Provider)をアプリケーションルートがもつ 46 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ
  23. © Findy Inc. i18nモジュールの設計草案 ❏ module内に翻訳リソースが閉じるのでPull Requestの変更がmodule内に閉じる ❏ 変更が閉じることで機能moduleの増加は無関係になり、翻訳リソースも機能単位でビルドされる ❏

    AppsはRouteingなど全体を知る必要があるのでもともと機能moduleには依存している ❏ リソースを利⽤する⼿段をAppsから提供できれば実現できる 48 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ いけそう 変更単位
  24. © Findy Inc. 翻訳リソースを動的にロードする仕組みの作成 ❏ ライブラリにはi18n clientインスタンスを切り替える仕組み(Provider)はあった ❏ 画⾯ごとに切り替えるというより翻訳処理の設定を特定条件で切り替えるのが⽬的 ❏

    ユーザーが画⾯を開くとその機能moduleのもつ辞書をロードさせたい ❏ 辞書⾃体はclientがキャッシュするのでReact Providerは不要 49 リアーキテクチャ 多⾔語化のリアーキテクチャ
  25. © Findy Inc. 翻訳リソースを動的にロードする仕組みの作成 50 リアーキテクチャ 多⾔語化のリアーキテクチャ import { TeamContainer,

    translateResources } from '@feature/team'; export const TeamPage = () => { const { params } = useParams(); return ( <TranslateResourceManager resourceBundles={[{ nameSpace: 'team', resources: translateResources, }]} > <TeamContainer params={params} />; </TranslateResourceManager> ); }; 機能のRoot Component 翻訳リソースを動的にロードして管理する 翻訳リソースとその名前空間を受ける feature moduleで名前解決に利⽤
  26. © Findy Inc. • keyの型をfeature moduleごとに定義しなおし • 実体と同期しているので、keyの増減とあわせて Union型が持つ数が増減する •

    module : 名前空間 : 型定義 = 1 : 1 : 1 型定義の再構築 ❏ 翻訳センテンスを識別するkeyを各featureで型に変換 ❏ コード補完 & 存在しないkeyは型エラー 52 リアーキテクチャ 多⾔語化のリアーキテクチャ /** team/i18n.d.ts */ declare module 'i18next' { interface CustomTypeOptions { resources: { team: (typeof resources)['en']; }; } } export const TeamLabel = () => { const { t } = useTranslation('team'); return <label>{t('teamMembers')}</label>; }; 名前空間の指定 keyの指定
  27. © Findy Inc. 共通moduleの翻訳配置 ❏ UI や共有機能など、複数の機能moduleが利⽤するmoduleの辞書 ❏ これらは分散配置の対象から外し、i18n moduleに持たせる

    53 リアーキテクチャ 多⾔語化のリアーキテクチャ export const SubmitButton = ( { label }: { label?: string }) => { const { t } = useTranslation('ui'); const defaultLabel = useTranslation('submit'); return <button>{label ?? defaultLabel}</button>; };
  28. © Findy Inc. コードベースとCI時間⽐較 リアーキテクチャ 多⾔語化のリアーキテクチャ 54 Before After Now

    TypeScript 353,578 ⾏ 552,539 ⾏ 613,432 ⾏ JSON 51,694 ⾏ 66,888⾏ 74,210 ⾏ 対応⾔語 2 3 3 +α 平均CI時間 13分 7分 6〜7分 ❏ コードベースが1.5倍以上でCI時間が約50% ❏ その後もCI時間7分の⽔準を維持 ❏ デプロイは変更範囲によって変動。4〜15分、稀に20分弱。
  29. © Findy Inc. 結論 ❏ 凝集の設計判断が間違っていた ❏ moduleがどのように利⽤されるかで凝集設計をすべきだった 55 リアーキテクチャ

    多⾔語化のリアーキテクチャ 結合した要素は同じ親要素の⼦要素同⼠であるべきだ。 これが凝集(Cohesion)の1つめの意味合いだ。 凝集の2つめの意味合いは、結合していない要素は別の場所 に移すべきということだ。
  30. © Findy Inc. ❏ CI時間を1つの指標としてmodule 設計を⾒直し ❏ 画⾯レイアウトを共有モジュールから分離 ❏ 共有モジュールの分割

    ❏ 変更頻度と成熟度 ❏ UI モジュールの分割 ❏ プリミティブとセマンティックの分割 ❏ Atomic Designではない 58 その他の取り組み その他の取り組みや課題 https://tech.findy.co.jp/entry/2025/06/30/070000
  31. © Findy Inc. ❏ 巨⼤なStorybookの運⽤ ❏ すべてのUIを提供するサーバー ❏ 全⾔語の全センテンス =

    全翻訳リソースが必要 ❏ VRT [Visual Regression Test] ❏ Storybookベースで実施していたが、上記理由で厳しい状況 ❏ Vitest v4の toMatchScreeenshot に期待 ❏ ESLint ❏ モノレポ対応が弱く、バージョンアップ (v10.x.x)に期待 ❏ --flag v10_config_lookup_from_file モノレポサポートが弱いエコシステムに課題 59 その他の取り組みや課題
  32. © Findy Inc. ❏ CIをショートカット ❏ 依存関係の⾃動検出 ❏ nx affected

    [command] で必要なテストを実⾏する ❏ モジュールの依存関係の設計と管理 ❏ Nx独⾃のeslint rule ❏ 責務をeslintのtagで表現 Nxによる効率化と管理 61 まとめ PullRequestのCI時間は10分以内
  33. © Findy Inc. moduleと凝集度設計 62 まとめ ❏ 機能的凝集(Functional Cohesion) ❏

    1つの明確な機能のみにまとまっている ❏ 通信的凝集(Communicational Cohesion) ❏ 同じデータを扱う部分を集めている 機能的凝集 通信的凝集
  34. © Findy Inc. 63 まとめ Nxはmoduleそれぞれが全体の与える影響度を教えてくれる CI時間を指標として定量的に凝集を評価する ❏ CIが遅い =

    凝集の設計が悪い ❏ 凝集の設計が悪い = 結合の⽅法が悪い ❏ 結合の⽅法が悪い = 与えている責務に誤解がある