本記事について
当サイトを閲覧いただきありがとうございます。 本記事はシリーズ『生成AI時代のアーキテクチャ超入門』の「ソフトウェアアーキテクチャ」カテゴリ第3弾として、アプリ内部のモジュール設計について解説する記事です。
アプリ内部の部屋割りをどう引くかが、そのままコードの寿命を決めます。本記事ではレイヤード/ヘキサゴナル/オニオン/クリーンの4パターンを比較し、ドメインの複雑さ・チームスキル・プロジェクトの寿命という3軸での選び方と、「パターン名ではなく依存の矢印を見る」という実務的な指針を示します。
このカテゴリの他の記事
「内側の部屋割り」で寿命が決まる
モジュール分割が下手だと、どの全体構造を採用しても破綻します。モノリスなら巨大なスパゲッティコードに、モジュラーモノリスなら境界が名ばかりの状態に、マイクロサービスなら各サービス内部がカオスになります。モジュール設計は全体構造の土台となる基礎体力のようなものです。
後から直そうとすると、実質的に書き直しに近い大工事になります。機能開発のたびに「ここに書いていいのか」と議論が発生するようなコードベースは、数年で保守不能になり、新規機能の開発速度が半減します。最初に腰を据えて決めるのが、長期的には最も安上がりです。
なぜ型(パターン)が必要か
多人数開発の現場では、コードをどこに書くかを毎回議論していては生産性が出ません。そこで長年の経験から「このパターンに従えば破綻しにくい」という設計の型が整備されてきました。それがアーキテクチャパターンです。
パターンに従うことで、新しく参加したエンジニアも「ここはUI層」「ここはビジネスロジック層」と役割を即座に理解でき、変更時の影響範囲も予測しやすくなります。ただしパターンを採用すること自体が目的化すると、「過剰設計で生産性を落とす罠」に落ちます。
| 目的 | 効果 |
|---|---|
| コードの置き場所を規格化する | 新メンバーが迷わない |
| 依存関係を整理する | 変更の影響範囲が読める |
| テストしやすい構造にする | バグの早期発見 |
| 将来の変更に耐える | 技術スタック入れ替えが可能 |
代表的な4パターン
実務で広く使われているのは以下の4つです。いずれも「依存関係の方向を制御する」点で共通しています。
| パターン | ざっくりした特徴 |
|---|---|
| レイヤードアーキテクチャ | UI・ビジネス・データの水平分割。最も古典的 |
| ヘキサゴナルアーキテクチャ | ドメインを中心に、外部接続点をポート・アダプタで隔離 |
| オニオンアーキテクチャ | 同心円状に層を配置。ドメインが最内層 |
| クリーンアーキテクチャ | オニオンの発展形。依存を内側に一方通行で固定 |
ヘキサゴナル・オニオン・クリーンは「思想的にほぼ同じ」で、細部の違いはあっても本質は「ドメイン中心・依存方向の制御」に尽きます。宗派論争は時間の無駄です。
レイヤードアーキテクチャ
レイヤードアーキテクチャは、UI・ビジネスロジック・データアクセスの3層を水平方向に積む古典パターンです。WEBのMVC(Model-View-Controller)や、デスクトップのMVP(Model-View-Presenter)・MVVM(Model-View-ViewModel)もレイヤードの亜種です。書籍やフレームワーク入門書はほぼこのパターンから始まるため、「どのチームでも共通認識がある」のが最大の強みです。
一方、ビジネスロジック層がデータアクセス層に直接依存しやすく、DB実装の都合がビジネスロジックに漏れる弱点があります。ORマッパー(OR Mapping、DBのテーブルをオブジェクトに対応付けるライブラリ)の型やテーブル構造がアプリ全体に染み出し、DBを変えたくなった時の工数が膨大になる、という失敗はよく見られます。
| 強み | 弱み |
|---|---|
| 学習コストが低い | ビジネス層がDB実装に依存しがち |
| どのチームも慣れている | 層の数が増えるほど薄い中継が増える |
| フレームワーク標準で自然に書ける | テストでDBが必要になりやすい |
MVC等の一般的なフレームワークはこの形です。小〜中規模では今も第一候補として通用します。
ヘキサゴナルアーキテクチャ
ヘキサゴナルアーキテクチャは、アプリケーションの中心にドメイン(業務ロジック)を据え、外部との接続点にポート(インターフェース)とアダプタ(実装)を挟むパターンです。UI・DB・外部APIといった「外の世界」は全てアダプタ経由でドメインにアクセスします。
最大の利点は「外部依存を自由に差し替えられる」ことです。テスト時にDBをメモリ実装に置き換える、本番のメッセージキューを変更する、UIをWebからCLIに差し替える。こうした変更がドメインロジックに全く影響しません。テストが書きやすく、長期運用に耐える構造です。
| 強み | 弱み |
|---|---|
| テストが書きやすい | 小規模だとオーバースペック |
| 外部依存を差し替えやすい | ポート・アダプタの設計スキルが必要 |
| ドメインが独立して保護される | クラス数が増える |
| 長期運用に強い | 学習コストが中〜高 |
DDD(ドメイン駆動設計)と組み合わせると威力を発揮します。中規模以上の本命です。
オニオン / クリーンアーキテクチャ
クリーンアーキテクチャは、Robert C. Martinが提唱した「依存関係を内側に一方通行で固定する」設計です。外から順に「Frameworks & Drivers」「Interface Adapters」「Use Cases」「Entities」の4層を同心円状に配置し、「外側の層は内側に依存してよいが、内側は外側を知らない」というルールを徹底します。
これにより Entities(業務ルール)は最も安定した核になり、UIフレームワークやDBの変更が業務ロジックに波及しません。依存性逆転の原則(DIP)が鍵で、内側がインターフェースを定義し、外側がその実装を提供することで、実質的に依存方向を反転させます。
flowchart LR
subgraph FW["Frameworks & Drivers (最外層)"]
FW1[DB / Web / UI / 外部API]
end
subgraph IA["Interface Adapters"]
IA1[Controller / Gateway / DTO変換]
end
subgraph UC["Use Cases"]
UC1[アプリ固有の業務フロー]
end
subgraph EN["Entities (最内層)"]
EN1[業務ルール<br/>最も安定]
end
FW -->|依存OK| IA -->|依存OK| UC -->|依存OK| EN
EN -.|逆方向は禁止|.-> FW
classDef outer fill:#fae8ff,stroke:#a21caf;
classDef mid1 fill:#fef3c7,stroke:#d97706;
classDef mid2 fill:#f0f9ff,stroke:#0369a1;
classDef core fill:#dbeafe,stroke:#2563eb,stroke-width:3px;
class FW outer;
class IA mid1;
class UC mid2;
class EN core;
| 層 | 役割 |
|---|---|
| Entities(最内層) | 業務ルール・最も安定 |
| Use Cases | 業務フロー・アプリ固有のロジック |
| Interface Adapters | DTO(Data Transfer Object、層間でデータを受け渡す単純な構造体)変換・Controller・Presenter |
| Frameworks & Drivers(最外層) | DB・Web・UIフレームワーク |
オニオンアーキテクチャはクリーンの原型に近く、本質は全く同じです。
4パターンの比較
4つのパターンは「学習コストとテスト容易性のトレードオフ」で整理できます。レイヤードは学びやすいが長期運用に弱く、クリーンは長期運用に強いが初期コストが高い。という関係です。
| 観点 | レイヤード | ヘキサゴナル | オニオン | クリーン |
|---|---|---|---|---|
| 学習コスト | ◎ 低 | ◯ 中 | △ 中〜高 | △ 高 |
| 小規模適性 | ◎ | ◯ | △ | × |
| テスト容易性 | △ | ◎ | ◎ | ◎ |
| 変更耐性 | △ | ◎ | ◎ | ◎ |
| 初期コスト | 低 | 中 | 中〜高 | 高 |
| 向くチーム | 初学者〜 | 中堅〜 | 中堅〜熟練 | 熟練 |
一般にはレイヤード → ヘキサゴナル → クリーンの順に難度とメリットが増します。
判断基準
① ドメインの複雑さ
モジュール設計選定で最も重要なのは、扱うドメイン(業務領域)の複雑さです。CRUD(Create/Read/Update/Delete)が主体のシンプルな業務(社内の申請管理、シンプルなブログ等)では、複雑な依存制御を導入しても見返りが小さく、過剰設計になります。
逆に、ドメインロジックが複雑で業務ルールが多い領域(保険・金融・医療・EC・物流等)では、業務ロジックを外部依存から切り離す価値が跳ね上がります。フレームワークやDBに業務ルールが染み出ると、「業務の変更がインフラ変更に化ける」事態が頻発します。
| ドメインの複雑さ | 向く設計 |
|---|---|
| 単純(CRUD中心) | レイヤード |
| 中程度 | ヘキサゴナル |
| 高(業務ルールが多い) | クリーン + DDD |
「複雑さ」は機能数ではなく「業務ルールの多さ・変更頻度」で判断します。
② チームのスキル
パターンは「型」である以上、チームが正しく使えることが前提です。クリーンアーキテクチャを学習中のチームに導入すると、パターン名だけが先行して、実態はレイヤードを分厚く書いただけ。という残念な結果になりがちです。
逆に、全員がレイヤードに慣れている小〜中規模のチームなら、無理にクリーンを採用せず、「レイヤードを綺麗に書く方が遥かに生産性が高い」設計パターンはチームの成熟度に合わせて段階的に導入するのが現実的です。
- 新卒中心 / スタートアップ初期 → レイヤード
- 中堅中心 / プロダクトが安定期に入ってきた → ヘキサゴナル
- 熟練揃い / ドメインが複雑 → クリーン + DDD
「優れたパターン」より「チームが運用できるパターン」が常に正解です。
③ プロジェクトの寿命
プロジェクトの想定寿命もモジュール設計の選定に響きます。数年で作り直す前提のプロトタイプやMVPなら、複雑な依存制御を導入する価値は小さく、最短距離で作るレイヤードで十分です。
逆に10年以上動かす前提の基幹システムや長期SaaS製品では、フレームワークやDBの世代交代に耐える必要があり、ヘキサゴナルやクリーンの投資が後から効いてきます。5〜10年後にフレームワークをアップデートできるかどうかは、モジュール設計の質に直結します。
| プロジェクトの寿命 | 推奨 |
|---|---|
| 〜3年(MVP・プロトタイプ) | レイヤード |
| 3〜10年(一般的なSaaS・業務アプリ) | ヘキサゴナル |
| 10年以上(基幹システム・長期SaaS) | クリーン + DDD |
ケース別の選び方
スタートアップMVP・プロトタイプ
レイヤード。最短で動くものを作る段階では、依存方向の厳密な制御よりリリース速度が優先です。後から作り直す前提で割り切ります。
中〜長期運用を想定した業務アプリ・SaaS
ヘキサゴナル。テスト容易性と変更耐性のバランスが良く、多くのプロジェクトで現実的な最適解になります。
ドメインが複雑なエンタープライズ
クリーン + DDD。業務ルールが多く変更も頻繁な領域では、クリーンアーキテクチャの投資が長期的に回収できます。
CRUDが主体のシンプルなアプリ
レイヤード。業務ロジックが薄い領域で複雑な構造を導入しても、ただクラスが増えるだけで見返りがありません。
プロジェクト規模×パターンの実務段階表
※ 2026年4月時点の業界相場値です。テクノロジー・人材市場の変化で陳腐化するため、定期的にアップデートが必要です。
モジュール設計パターンは「どれが正しいか」ではなく「何行・何人・何年続くか」で決まります。経験則として以下の段階が目安です。
| コードベース規模 | チーム人数 | 運用年数 | 推奨パターン | 1ファイル行数目安 |
|---|---|---|---|---|
| 〜1万行 | 1〜3人 | 〜3年 | 素のMVC / レイヤード | 〜300行 |
| 〜5万行 | 3〜10人 | 3〜10年 | レイヤード or ヘキサゴナル | 〜300行 |
| 〜20万行 | 10〜30人 | 5〜15年 | ヘキサゴナル or クリーン | 〜300行 |
| 20万行〜 | 30人〜 | 10年〜 | クリーン + DDD | 〜300行 |
「1ファイル300行・1メソッド50行・ネスト3段・循環的複雑度10」が多くのプロジェクトで採用される定量ガードレールです。これを超えたら分割の合図で、ESLint・SonarQube で自動検出するのが現代流です。「1メソッド1画面(80行)に収まらないコードは、設計が間違っている」というのが経験則です。
パターンは規模に合わせて段階的に昇格。MVPにクリーンを入れるのは過剰、巨大プロジェクトでレイヤード止まりは破綻の元です。
モジュール設計の鬼門・禁じ手
パターンを採用しても、以下を踏むと「パターン名だけのスパゲッティ」が完成します。
| 禁じ手 | なぜダメか |
|---|---|
| God クラス / God Module(数千行・十数責務) | SRP違反の典型。300行を超えた時点で分割の合図 |
| 循環依存(A→B→A) | パッケージ間循環は必ずバグの温床。ESLint(import/no-cycle)で検出 |
| ORM 生成型(Entity)を全層で使い回し | DBスキーマ変更がUIまで波及。境界ごとにDTOで変換するのが鉄則 |
| Fat Service(ビジネスロジックを全部Serviceに詰める) | 貧血ドメインモデルに陥り、Entityが単なるデータ構造化。ロジックはEntity / 値オブジェクトに寄せる |
| インターフェース(ポート)を実装クラス1対1で切る | 差し替えの見込みがない抽象化はオーバースペック。実際の差し替え候補が2つ以上ある時だけ抽象化 |
| テストで自分のコードをモック | リポジトリ層・ユースケース層をモックすると、結合のバグが検出できない。外部世界だけモック |
| ADR(Architecture Decision Record)を残さない | 5年後に「なぜこのパターンを選んだか」が失われ、正しくない方向への「改善」が入る |
| パターン本を読まずにパターン名だけ借用 | 原典の意図と違う実装になり、パターン名を冠したスパゲッティが完成 |
2006年に発表された Martin Fowler の “Patterns of Enterprise Application Architecture” や 2017年の Robert C. Martin “Clean Architecture” は、「原典を読まずにパターン名だけ真似る」のが最悪の失敗を生みます。パターンは「書く側の規律」であり、書き方のルールを徹底できないチームには効きません。
循環依存・God Module・全層ORM貫通はどのパターンでも即死する三大禁じ手です。
共通する原則
どのパターンを選んでも、以下の原則は常に守ること。これらは特定のパターンの話ではなく、良い設計全般に共通する基礎で、パターン名を借りているだけで原則が守られていないコードは、結局スパゲッティコードと同じ末路をたどります。
原則を意識しないコードは「触ると壊れる」状態に必ず陥ります。循環依存があれば1クラスの修正が連鎖的に他クラスに波及して影響範囲が読めなくなり、ドメインとインフラが混ざると「DBを変えたい」が「業務ロジックの書き直し」に化け、責務が曖昧だと同じロジックが3箇所に散らばります。
- 依存関係は一方向にする(循環依存を作らない)
- ドメインロジックをUI・DBから独立させる
- 各層・各モジュールの責務を明確にする
- テストは単体・結合・E2Eの3段構えで書く
原則が守れていればパターン名は二の次です。パターン名にこだわって実装が歪むのが最悪です。
AI時代の視点
AI駆動開発が前提になると、モジュール設計は「AIに渡せるコンテキストの単位」として重要性が増します。AIは一度に読めるコード量に限りがあるため、境界が明確で自己完結したモジュールはAIが正確に理解・修正できます。
逆に、関心事が絡み合った巨大モジュールはAIでも精度が落ちる。モジュール境界の明示は「AIへのプロンプトの設計」そのものです。
| AI時代に有利 | AI時代に不利 |
|---|---|
| 小さく自己完結したモジュール | God Module(巨大で多責務) |
| インターフェースで依存方向を明示 | 暗黙的な依存・スコープ跨ぎ |
| 型定義ファイル(.d.ts等)の整備 | 型なし動的呼び出し |
| レイヤード・クリーン等の定番パターン | 独自の変則的な構造 |
ヘキサゴナル・クリーンのような定番パターンは、AIが学習済みでテンプレ生成できる利点があります。独自の変則構造はAIもハルシネーションしやすく、保守性を損ないます。
AI時代は「モジュール境界=AIへの文脈提供」小さく・明確に・インターフェース明示が基本です。
よくある勘違い
- 「クリーンアーキテクチャは常に正しい」 → 規模とドメイン複雑度に見合わなければ、ただの過剰設計。CRUD中心の小規模アプリにクリーンは重すぎる
- 「ヘキサゴナルとクリーンは別物」 → 細部は違うが本質は同じ「ドメイン中心・依存方向制御」宗派論争は時間の無駄
- 「DDDはクラス設計のパターン」 → DDDの本質は業務の言葉でモデルを育てる設計思想。リポジトリや集約といったパターンだけ真似ても、業務用語を整理しなければ意味がない
- 「パターンを使えば自然に綺麗なコードになる」 → パターンは型に過ぎません。原則(依存方向・責務分離)を理解しないまま型だけ真似ると、パターン名を冠したスパゲッティが出来上がる
「これを7画面分書くの?」(業界事例)
クリーンアーキテクチャの本を読んだ直後の若手エンジニアが、単純な入力フォーム1画面のために Entity・UseCase・Repository・Controller・Presenter の5ファイルを作った、という事例があります。先輩が「これを7画面分書くの?」と冷静に問い直したところ、本人も我に返り、結局レイヤード寄りの簡素な構造に落ち着いた、という話が聞かれます。
学び始めの頃は同じ罠に落ちて、レビューで「まず1画面1ファイルでいいから書いてみて」と戻された、という経験談もよくあります。パターンの採用は「ドメインの複雑さに見合うか」で判断するもので、読んだ本の熱量で決めるものではありません。
CRUD画面に4層構造を適用すると、似たようなDTOが大量に並び、業務価値に対して工数が見合わなくなります。「複雑さに見合った設計」という感覚は、失敗を経由して身につくものかもしれません。
設計の意図をADRに残すと、なぜこのパターンを選んだかを後任が追えます。
決めるべきこと — あなたのプロジェクトでの答えは?
以下の項目について、あなたのプロジェクトの答えを1〜2文で言語化してみてください。曖昧なまま着手すると、必ず後から「なぜそう決めたんだっけ」が問われます。
- どのパターンを採用するか(レイヤード/ヘキサゴナル/クリーン)
- モジュールの境界をどこに引くか(機能別/業務領域別)
- 依存方向のルール(内側は外側を知らない、等)をチームで合意するか
- テスト戦略(単体・結合・E2Eの比率)
- 採用の判断をADRとして残すか
- 将来パターンを変えるシナリオを想定するか
よくある失敗
- 「クリーン」採用が目的化してクラス爆発 ― CRUDだけの画面に4層構造を適用し、似たようなDTOが大量に並ぶ。業務価値に対して工数が見合わない
- Fat Service(肥大化サービス) ― ビジネスロジックを全部 Service 層に詰め込み、Entity が単なるデータ構造になる「貧血ドメインモデル」に陥る
- DBスキーマに引きずられる設計 ― ORM が生成する型をそのまま全層で使い回し、DBの都合が業務ロジックに染み出す
- パターンの原典を無視した自己流 ― 本を読まずにパターン名だけ借用し、本来の意図とは違う実装になる
最終的な判断の仕方
モジュール設計で最も陥りやすい罠は「パターン名を目的化する」ことです。クリーンアーキテクチャを採用すること自体が目的になると、CRUD画面に4層構造が適用されてクラスが爆発し、業務価値に対して工数が見合わなくなります。
選定の核は「ドメインの複雑さ × チームの成熟度 × プロジェクトの寿命」の3軸の積で、シンプルな業務には軽いパターン、複雑な長期運用には重いパターンを当てるのが鉄則です。
どのパターンを選んでも、「本質は依存方向を一方通行に保つこと」に尽きます。パターンの違いは「どこまで厳密に制御するか」の度合いで、思想は同じ。
AI駆動開発の観点では、境界が明確で自己完結したモジュールがAIの文脈提供単位として重要になり、小さく明確に切るのが新しい基準です。
選定の優先順位をまとめると次の通りです。
- ドメインの複雑さ(CRUD中心か業務ルール多数か)で候補を絞る
- チームの成熟度に合うパターンを選ぶ(運用できないパターンは無価値)
- プロジェクトの寿命で初期投資の妥当性を判断
- モジュール境界はAIへのコンテキスト単位として設計
「チームが運用できるパターン」が常に正解です。優れたパターンでも、チームが使いこなせなければ形骸化します。
まとめ
本記事はモジュール設計について、レイヤード・ヘキサゴナル・オニオン・クリーンの4パターンを規模・複雑度・寿命の観点から解説しました。如何だったでしょうか。
CRUD中心はレイヤード、中規模・長期運用はヘキサゴナル、複雑ドメインはクリーン+DDD。パターン名を目的化せず、依存方向を一方通行に保つという本質を外さなければ、どのパターンでも実用に耐えます。
次回は「API設計」(REST/GraphQL/gRPC/WebSocket)について解説します。
シリーズ目次に戻る → 『生成AI時代のアーキテクチャ超入門』の歩き方
それでは次の記事も閲覧いただけると幸いです。
📚 シリーズ:生成AI時代のアーキテクチャ超入門(20/89)