オニオンアーキテクチャ

英語名 Onion Architecture
読み方 オニオン アーキテクチャ
難易度
所要時間 30分〜1時間
提唱者 Jeffrey Palermo (2008)
目次

ひとことで言うと
#

アプリケーションを同心円状のレイヤーに分割し、依存の方向を「外側→内側」に限定することで、ドメインロジックを外部技術(DB・フレームワーク等)から独立させるアーキテクチャパターン。

押さえておきたい用語
#

押さえておきたい用語
Domain Model(ドメインモデル)
ビジネスルールとロジックを表現するアプリケーションの中核のこと。最内周に位置する。
Domain Service(ドメインサービス)
単一のエンティティに属さないドメイン固有のビジネスロジックを担うサービス。
Application Service(アプリケーション サービス)
ユースケースの調整役を指す。ドメインモデルのメソッドを呼び出し、トランザクション制御を行う。
Infrastructure Layer(インフラストラクチャ レイヤー)
DB接続・外部API呼び出し・ファイルI/Oなど技術的な実装の詳細を担う最外周レイヤー。
Dependency Inversion(依存性逆転)
上位レイヤーが下位レイヤーに依存するのではなく、インターフェースを通じて依存方向を逆転させる原則である。

オニオンアーキテクチャの全体像
#

オニオンアーキテクチャ:依存は外から内へ一方向
DomainModelApplication ServiceInfrastructure / UI依存方向外→内のみDB / ORM交換可能Web Framework交換可能ドメインが外部技術に依存しない = テスト容易・技術変更に強いDependency Inversionで依存方向を制御する
オニオンアーキテクチャ適用の進め方フロー
1
ドメインモデル定義
ビジネスルールを最内周に配置する
2
インターフェース設計
リポジトリ等の抽象を内側レイヤーに定義
3
外周の実装
DB・API等の実装をInfrastructureレイヤーに配置
依存方向の維持
CIで依存違反を検出し構造を守り続ける

こんな悩みに効く
#

  • ドメインロジックとDBアクセスが混在して、ビジネスルールの変更が怖い
  • フレームワークのバージョンアップでアプリケーション全体に影響が出る
  • ドメインロジックの単体テストが書けない(DBが必要になる)

基本の使い方
#

ドメインモデルを最内周に配置する
ビジネスルール・バリデーション・ドメインイベントを含むモデルを作成し、外部技術への依存をゼロにする。ドメインモデルはPure Objectsとして実装し、フレームワークのアノテーションやDBライブラリのインポートを含めない。
リポジトリインターフェースを内側に、実装を外側に置く
OrderRepositoryインターフェースをDomain層に定義し、PostgresOrderRepository実装をInfrastructure層に配置する。Dependency Inversionにより、ドメインはDBの種類を知らない状態にする。
Application Serviceでユースケースを調整する
Application Serviceはドメインモデルのメソッドを呼び出し、トランザクション制御と外部サービス連携を行う。ビジネスルールはApplication Serviceに書かず、必ずドメインモデルに委譲する。

具体例
#

例1:EC企業がドメインロジックをDB依存から解放する

エンジニア45名のEC。注文処理のバリデーションがSQLクエリの中に埋め込まれており、「在庫引当ルール」の変更に毎回3日以上かかっていた。テストも本番DBのコピーが必要で、CI実行に 25分 を要していた。

オニオンアーキテクチャを導入し、注文ドメインを3層に再設計。

レイヤー内容
DomainOrder, OrderItem, 在庫引当ルール, 割引計算
ApplicationCreateOrderUseCase, CancelOrderUseCase
InfrastructurePostgresOrderRepository, StripePaymentGateway

ドメインモデルのユニットテストがDBなしで実行可能に。テスト時間が 25分 → 3分 に短縮。在庫引当ルールの変更は 3日 → 半日 で完了するようになった。

例2:SaaS企業がフレームワーク移行をドメインに影響なく実行する

エンジニア30名のBtoB SaaS。Expressで構築したAPIをNestJSに移行する計画があったが、ドメインロジックがExpressのミドルウェアに散在しており、移行すると全機能のテストが必要だった。

まずオニオンアーキテクチャにリファクタリングし、ドメインロジックをフレームワーク非依存のTypeScriptクラスに抽出。その後、InfrastructureレイヤーのみをNestJSに差し替え。

ドメインモデルのコードは 1行も変更せず にフレームワーク移行を完了。移行にかかった期間は当初見積もりの 6ヶ月 → 2ヶ月 に短縮された。

例3:医療系スタートアップがテスタビリティを確保する

エンジニア12名の医療系スタートアップ。診療報酬計算のロジックが複雑で、バグが見つかるたびに「テストを書こうとするとDB・外部APIのモックが大量に必要」で断念していた。テストカバレッジは 18%

診療報酬計算をDomain層のPure Functionとして再実装。外部依存をすべてインターフェースに切り出し、テスト時はインメモリ実装を注入する設計に変更。

3ヶ月でDomain層のテストカバレッジが 18% → 92% に向上。診療報酬の計算ミスが 月4件 → 月0.3件 に減少し、保険請求の差し戻し率も大幅に下がった。

やりがちな失敗パターン
#

  1. ドメインモデルがアネミック(貧血)になる — Domainにデータだけ置いてロジックをApplication Serviceに書くのは本末転倒。ビジネスルールは必ずDomain層に実装する
  2. レイヤーが多すぎて開発速度が落ちる — 小規模なCRUDアプリに4層のオニオンは過剰。ドメインロジックが複雑な場合にのみ適用する
  3. 依存方向の違反を許容する — 「この1箇所だけ」が積み重なると構造が崩壊する。ArchUnit等で自動検出する
  4. インターフェースを作りすぎる — 実装が1つしかないインターフェースを大量に作るのはボイラープレート。テスト時に差し替えが必要な箇所だけにインターフェースを切る

まとめ
#

オニオンアーキテクチャは同心円状のレイヤーで依存方向を 「外→内」 に限定し、ドメインロジックを外部技術から独立させる設計パターン。Dependency Inversionを使ってリポジトリのインターフェースを内側に、実装を外側に配置する。テスタビリティの向上・フレームワーク移行の容易さ・ビジネスルール変更の安全性が主な利点であり、ドメインロジックが複雑なアプリケーションで特に効果を発揮する。