本記事について
当サイトを閲覧いただきありがとうございます。 本記事はシリーズ『生成AI時代のアーキテクチャ超入門』の「ソフトウェアアーキテクチャ」カテゴリ第3弾として、アプリ内部のモジュール設計について解説する記事です。
アプリ内部の部屋割りをどう引くかが、そのままコードの寿命を決めます。本記事ではレイヤード/ヘキサゴナル/オニオン/クリーンの4パターンを比較し、ドメインの複雑さ・チームスキル・プロジェクトの寿命という3軸での選び方と、「パターン名ではなく依存の矢印を見る」という実務的な指針を示します。
本記事のテーマについてさらに詳しく知りたい方は『ソフトウェアアーキテクチャの基礎』も参考にしてみてください。
この記事の結論
- CRUD中心はレイヤード、中規模・長期運用はヘキサゴナル、複雑ドメインはクリーン+DDD
- パターン名を目的化しない。本質は依存方向を一方通行に保つこと
- 1ファイル300行を超えたら分割の合図(Lintで自動検出する)
この記事を読む前に
本記事はプログラムやAPIといった開発寄りの話が中心です。IT用語にあまり馴染みがない方は、基礎編の「プログラムとAPIの基本」を先に読んでおくと格段に分かりやすくなると思います。また、読んでいて分からない用語が出てきたときは用語集で調べながら読み進められます。
そもそもモジュール設計とは何か
モジュール設計とは、ざっくり言えば「アプリ内部のコードをどの部屋に入れるかの間取りを決めること」です。
オフィスのフロア設計を想像してください。営業部・開発部・経理部をワンフロアに混在させると、電話の音や会話が干渉して全員の生産性が落ちます。部屋を区切り、窓口(インターフェース)を通じてやり取りすれば、各部門が独立して効率よく動けます。ソフトウェアも同じで、UI層・ビジネスロジック層・データ層といった「部屋割り」を明確にし、変更が他の部屋に波及しにくい構造を作ります。
なぜモジュール設計が重要なのか
もしモジュール設計を曖昧にしたらどうなるか。モジュール分割が下手だと、どの全体構造を採用しても破綻します。モノリスなら巨大なスパゲッティコードに、モジュラーモノリスなら境界が名ばかりの状態に、マイクロサービスなら各サービス内部がカオスになります。
後から直そうとすると実質的に書き直しに近い大工事になります。機能開発のたびに「ここに書いていいのか」と議論が発生するようなコードベースは、数年で保守不能になり、新規機能の開発速度が半減します。そこで長年の経験から整備されてきた「このパターンに従えば破綻しにくい」という設計の型がアーキテクチャパターンです。ただし、パターンの採用自体が目的化すると過剰設計で生産性を落とす罠に落ちます。
代表的な4パターン
実務で広く使われているのは以下の4つです。いずれも「依存関係の方向を制御する」点で共通しています。
| パターン | ざっくりした特徴 |
|---|---|
| レイヤード | UI・ビジネス・データの水平分割。最も古典的 |
| ヘキサゴナル | ドメインを中心に、外部接続点をポート・アダプタで隔離 |
| オニオン | 同心円状に層を配置。ドメインが最内層 |
| クリーン | オニオンの発展形。依存を内側に一方通行で固定 |
ヘキサゴナル・オニオン・クリーンは「思想的にほぼ同じ」で、本質は「ドメイン中心・依存方向の制御」に尽きます。宗派論争は時間の無駄です。
レイヤード ― 小〜中規模の第一候補
UI・ビジネスロジック・データアクセスの3層を水平に積む古典パターンです。WEBのMVCもこの亜種で、書籍やフレームワーク入門書はほぼここから始まるため、「どのチームでも共通認識がある」のが最大の強みです。弱点はビジネスロジック層がデータアクセス層に直接依存しやすいことで、ORマッパーの型やテーブル構造がアプリ全体に染み出し、DBを変えたくなった時の工数が膨大になる失敗がよく見られます。それでも学習コストの低さから、小〜中規模では今も第一候補として通用します。
ヘキサゴナル ― 中規模以上の本命
アプリケーションの中心にドメイン(業務ロジック)を据え、外部との接続点にポート(インターフェース)とアダプタ(実装)を挟むパターンです。UI・DB・外部APIといった「外の世界」は全てアダプタ経由でドメインにアクセスします。最大の利点は「外部依存を自由に差し替えられる」ことで、テスト時にDBをメモリ実装に置き換える・本番のメッセージキューを変更する、といった変更がドメインロジックに全く影響しません。テストが書きやすく長期運用に耐える、DDDと組み合わせて威力を発揮する中規模以上の本命です。
オニオン / クリーン ― 複雑ドメインへの長期投資
クリーンアーキテクチャはRobert C. Martinが提唱した「依存関係を内側に一方通行で固定する」設計です。外から順にFrameworks & Drivers・Interface Adapters・Use Cases・Entitiesの4層を同心円状に配置し、「外側の層は内側に依存してよいが、内側は外側を知らない」というルールを徹底します。これによりEntities(業務ルール)は最も安定した核になり、UIフレームワークやDBの変更が業務ロジックに波及しません。鍵は依存性逆転の原則(DIP)で、内側がインターフェースを定義し外側がその実装を提供することで、依存方向を実質的に反転させます。オニオンアーキテクチャはクリーンの原型に近く、本質は全く同じです。
4パターンの比較
どう選べばいいのか ― 3つの軸で決める
軸1: ドメインの複雑さ
最も重要な軸です。CRUDが主体のシンプルな業務(社内の申請管理・シンプルなブログ等)では、複雑な依存制御を導入しても見返りが小さく過剰設計になります。逆に業務ルールが多い領域(保険・金融・医療・EC・物流等)では、業務ロジックを外部依存から切り離す価値が大きい。フレームワークやDBに業務ルールが染み出ると、「業務の変更がインフラ変更に化ける」事態が頻発します。「複雑さ」は機能数ではなく「業務ルールの多さ・変更頻度」で判断します。
軸2: チームのスキル
パターンは「型」である以上、チームが正しく使えることが前提です。クリーンアーキテクチャを学習中のチームに導入すると、パターン名だけが先行して実態はレイヤードを分厚く書いただけ、という残念な結果になりがちです。全員がレイヤードに慣れている小〜中規模チームなら、「レイヤードを綺麗に書く方が遥かに生産性が高い」と言えます。「優れたパターン」より「チームが運用できるパターン」が常に正解です。
軸3: プロジェクトの寿命
数年で作り直す前提のプロトタイプ・MVPなら最短距離のレイヤードで十分です。10年以上動かす基幹システムや長期SaaSでは、フレームワークやDBの世代交代に耐える必要があり、ヘキサゴナルやクリーンの投資が後から効いてきます。
規模×パターンの実務段階表
※ 2026年4月時点の業界相場値です。
| コードベース規模 | チーム / 運用年数 | 推奨パターン |
|---|---|---|
| 〜1万行 | 1〜3人 / 〜3年 | 素のMVC / レイヤード |
| 〜5万行 | 3〜10人 / 3〜10年 | レイヤード or ヘキサゴナル |
| 〜20万行 | 10〜30人 / 5〜15年 | ヘキサゴナル or クリーン |
| 20万行〜 | 30人〜 / 10年〜 | クリーン+DDD |
「1ファイル300行・1メソッド50行・ネスト3段・循環的複雑度10」が多くのプロジェクトで採用される定量ガードレールです。これを超えたら分割の合図で、ESLint・SonarQubeで自動検出するのが現代流です。
3つのシナリオで考える
個人開発・スタートアップの場合
素のMVCかレイヤードで最速リリースを目指しましょう。数年で作り直す前提のMVPに、クリーンアーキテクチャの層構造を持ち込むのは過剰設計の典型です。定量ガードレール(1ファイル300行など)をESLintで自動チェックするだけでも、十分に秩序は保ててしまいます。
中小SaaSの場合
コードベースが5万行、チームが3〜10人を超えてくるあたりで、ヘキサゴナル(ポート&アダプタ)への段階移行を検討する時期になります。業務ロジックを外部依存から切り離しておくことで、決済プロバイダの乗り換えやDB変更に耐えられる変更耐性を確保できるためです。
大企業の場合
保険・金融・物流のような複雑なドメインの基幹系であれば、クリーンアーキテクチャとDDDへの投資を回収できると思います。ただし、チームの理解度が前提条件です。学習中のチームに導入してもパターン名だけが先行してしまいますので、教育計画とセットで導入することをお勧めします。
AI判断軸 ― モジュールはAIへのコンテキスト単位
AI駆動開発が前提になると、モジュール設計は「AIに渡せるコンテキストの単位」として重要性が増します。
モジュールサイズとAIのコンテキスト窓
1ファイル3,000行のGod Moduleを丸ごと渡しても、AIは全体を均等に理解できません。逆に300行以内で1責務が完結しているモジュールなら、そのファイル単体で修正指示が出せます。つまりモジュール分割の粒度がそのまま「AIに1回で正確に渡せる作業単位」になります。人間のレビューでも300行単位は理解しやすいので、この方向は人間にとっても良い設計です。
依存方向が明示されていないとAIは暴走する
AIにコード生成を任せると、明示的なインターフェースがない場合に勝手に依存先のモジュールを直接参照するコードを書きます。ドメイン層から直接DB接続ライブラリをimportする、UIコンポーネントからリポジトリを直接呼ぶ、といった境界破壊です。依存方向をインターフェースで明示していれば、AIは与えられた型を満たすコードを書くことに集中し、循環依存もLintで検知できるため、AI生成コードの品質ゲートとして機能します。
定番パターンを選ぶとAIの生成精度が段違いになる
クリーンやレイヤードは学習データに大量の実装例があり、AIに「レイヤードでUserServiceを書いて」と指示すればController→Service→Repositoryの流れに沿ったコードが高確率で出てきます。一方、独自の変則構造(CQRSとヘキサゴナルを社内流に混ぜたパターン等)は学習データがほぼないため、プロジェクト固有のルールを毎回コンテキストとして添える必要があり、この手間が積み重なるとAI活用のコストが跳ね返ってきます。
やってはいけないこと
パターンを採用しても、以下を踏むと「パターン名だけのスパゲッティ」が完成します。特に危険な6つに絞ります。
| 禁じ手 | なぜダメか → どうするか |
|---|---|
| Godクラス / God Module(数千行・十数責務) | 単一責任違反の典型 → 300行を超えたら分割する |
| 循環依存(A→B→A) | 必ずバグの温床になる → ESLint(import/no-cycle)等で自動検出する |
| ORM生成型(Entity)を全層で使い回す | DBスキーマ変更がUIまで波及する → 境界ごとにDTOで変換する |
| Fat Service(ロジックを全部Serviceに詰める) | Entityが単なるデータ構造になる → ロジックはEntity / 値オブジェクトに寄せる |
| テストで自分のコードをモックする | 結合のバグが検出できない → モックは外部世界(DB・外部API)だけにする |
| CRUDアプリにクリーンを適用する | 業務価値に対して工数が見合わない → レイヤードで十分 |
原典(FowlerのPofEAA、Martinの『Clean Architecture』)を読まずにパターン名だけ借用すると、原典の意図と違う実装になりがちです。どのパターンを選んでも、依存関係は一方向・ドメインをUIとDBから独立・責務を明確にという共通原則が守れていればパターン名は二の次です。
筆者メモ ― 「これを7画面分書くの?」
クリーンアーキテクチャの本を読んだ直後の若手エンジニアが、単純な入力フォーム1画面のためにEntity・UseCase・Repository・Controller・Presenterの5ファイルを作った、という事例があります。先輩が「これを7画面分書くの?」と冷静に問い直したところ、本人も我に返り、結局レイヤード寄りの簡素な構造に落ち着いた、という話が聞かれます。
パターンの採用は「ドメインの複雑さに見合うか」で判断するもので、読んだ本の熱量で決めるものではありません。「複雑さに見合った設計」という感覚は、失敗を経由して身につくものかもしれません。
決めるべきこと — 自分のプロジェクトでの答えは?
以下の項目について、自分のプロジェクトの答えを1〜2文で言語化してみてください。曖昧なまま着手すると、必ず後から「なぜそう決めたんだっけ」が問われます。
- どのパターンを採用するか(レイヤード/ヘキサゴナル/クリーン)
- モジュールの境界をどこに引くか(機能別/業務領域別)
- 依存方向のルール(内側は外側を知らない、等)のチーム合意
- 定量ガードレール(ファイル行数・複雑度のLint設定)
- テスト戦略(単体・結合・E2Eの比率)
言語化した答えはADRとして残します。書き方の具体例は以下の記事で解説しています。
この記事に関連する記事
まとめ
本記事はモジュール設計について、レイヤード・ヘキサゴナル・オニオン・クリーンの4パターンを規模・複雑度・寿命の観点から解説しました。如何だったでしょうか。
CRUD中心はレイヤード、中規模・長期運用はヘキサゴナル、複雑ドメインはクリーン+DDD。パターン名を目的化せず、依存方向を一方通行に保つという本質を外さなければ、どのパターンでも実用に耐えます。
次回は「API設計」(REST/GraphQL/gRPC/WebSocket)について解説します。
シリーズ目次に戻る → 『生成AI時代のアーキテクチャ超入門』の歩き方
本記事で扱った内容の詳細は Node.js 公式ドキュメント も合わせて参考にしてください。
それでは次の記事も閲覧いただけると幸いです。
📚 シリーズ:生成AI時代のアーキテクチャ超入門(26/95)
