
「コードがぐちゃぐちゃしてきた…」「ちょっとした変更で全体が壊れる…」そんな経験、ありませんか?
それ、設計の“守るべき中身”がちゃんと守られていないのかもしれません。
この記事では、そんな悩みを解決するヒントになる「オニオンアーキテクチャ」をやさしく解説します。「オニオンアーキテクチャ」とはビジネスロジックを中心に守り、外側(UIやDBなど)との依存を明確に分ける設計手法です。この設計手法について図解つきで、初心者でもすっと理解できるように解説します。
はじめに:なぜ“中身”を守る設計が重要なのか
アプリケーションが小さいうちは、コードの構造が多少乱れていても問題が表面化しにくいものです。しかし、機能が増え、チームメンバーが増え、利用される期間が長くなるにつれて、「ちょっとした修正を加えただけなのに、思わぬところに影響が出てしまう」といった状況が頻発するようになります。
こうした“壊れやすさ”の原因の多くは、**本来守るべき中核である「ドメインロジック」**が、他の技術要素(UIやDB、フレームワークなど)と密接に絡み合ってしまっていることにあります。
ドメインロジックは、システムが解決しようとするビジネス上の課題や企業独自のルールを体現した部分です。言い換えれば、「そのシステムで最も変わっては困る部分」なのです。
この中核をきちんと守り、技術的な変更やUIの刷新があっても影響を受けにくい構造にしておくこと。それが、長く使える設計を実現する第一歩なのです。
2. オニオンアーキテクチャとは?
オニオンアーキテクチャは、その名の通り「玉ねぎ」のように層を重ねた構造を持つアーキテクチャ設計の考え方です。最大の特徴は、ビジネスロジック(ドメイン)を中心に据え、外側の層が内側の層に依存するという明確な依存関係にあります。
この構造により、「ビジネスルールを変更せずに、UIやデータベースなど技術的な要素を変更できる」柔軟性が生まれます。たとえばデータベースをMySQLからPostgreSQLに変更する場合でも、ドメイン層がインフラ層に依存していないため、ビジネスロジックに手を加える必要がありません。
また、ユニットテストのしやすさも大きな利点です。UIやインフラの具体実装を除外して、ドメインやユースケース単位でテストできるため、品質を高く保ちつつ開発が可能になります。
以下の図は、オニオンアーキテクチャの全体構造と依存関係の方向を示したものです。

オニオン構造の中心から外へ向かう4つの層
- Domain Model(ドメインモデル)
システムの中心となる層です。業務ルールや振る舞いを定義するエンティティ(例:顧客、注文など)や値オブジェクトなどが含まれます。この層は純粋なビジネスロジックのみを扱い、他の技術的要素には一切依存しません。テストしやすく、最も守るべき領域です。 - Domain Service(ドメインサービス)
ドメインモデル同士の関係や、1つのエンティティでは表現しきれないビジネスルールを定義します。たとえば「注文可能かどうかの判定」など、複数モデルを横断して行う処理がここにあたります。ここもインフラや外部技術に依存せず、ドメインモデルを補完する役割です。 - Application Service(アプリケーションサービス)
UI層やインフラ層とドメイン層をつなぐ調整役です。ユースケースに基づいてドメインサービスを呼び出し、結果を返します。また、インターフェース(ポート)を定義し、実装はインフラ層に任せます。ドメインに対しては操作の流れを制御する存在です。 - Infrastructure / UI(インフラ・UI層)
最も外側にある技術的な層です。データベースや外部APIとの通信、Web画面やREST APIなど、ユーザーや他システムとの接点を担います。アプリケーションサービスが定義したインターフェースを実装(アダプタ)し、内側の処理に必要な情報を橋渡しします。ドメインには依存せず、変更の影響を最小限に保つ設計が可能です。
依存の方向性:一方通行のルール
重要なのは、外側の層が内側の層に依存し、内側は外側に依存しないというルールです。 これにより、ビジネスロジックが他の技術要素から守られ、設計の柔軟性と保守性が大きく向上します。
従来の「三層アーキテクチャ」とは異なり、オニオンアーキテクチャではドメインロジックがシステムの中心に位置する点が最大の特徴です。
どんな場面でオニオンアーキテクチャが有効?
オニオンアーキテクチャは、すべてのプロジェクトに適しているわけではありませんが、以下のようなケースでは非常に有効です。
- 中長期で運用される業務システム:要件変更や機能追加が繰り返される環境で、ドメインを守る構造が効果を発揮します。
- 技術的な変更が想定される開発:たとえばデータベースやUI技術の変更などに柔軟に対応できます。
- ドメイン知識が重要なシステム:業務ルールが複雑な場合、ロジックを集中管理できる設計は保守性に優れます。
- チームでの開発:役割が明確に分かれるため、複数人での並行開発や責務分離がしやすくなります。
- ドメイン駆動設計(DDD)と併用する場合:DDDとオニオンアーキテクチャは思想的に非常に親和性が高く、組み合わせて使うことでより強力な設計になります。
小規模開発や初心者が取り入れるポイント
「オニオンアーキテクチャって大掛かりなシステムだけの話でしょ?」と思われがちですが、実は小さなアプリケーションや初心者にも十分取り入れられます。
- まずは層を意識してコードを整理してみる:ドメイン、ユースケース、インフラ(DB)を分けるだけでも理解が深まります。
- RepositoryやServiceにインターフェースを導入してみる:具象に依存しないコードを書く練習になります。
- テストしやすい構造を目指す:インフラに依存しない層を切り出すことで、テストしやすさを実感できます。
- 「インフラやUIは後から変えられるもの」として捉える:中心となるビジネスロジックを先にしっかり作る習慣が身に付きます。
最初は完全なオニオン構造を目指す必要はありません。重要なのは、「何を守るべきか」を意識した設計を始めることです。重要なのは、外側の層が内側の層に依存し、内側は外側に依存しないというルールです。 これにより、ビジネスロジックが他の技術要素から守られ、設計の柔軟性と保守性が大きく向上します。
Javaによるオニオンアーキテクチャ構成例(例:ECサイト)
ここでは「ECサイトで商品を購入する」ユースケースを例に、オニオンアーキテクチャをJavaでどう設計するかの構成例を紹介します。
📁 ファイル構成例
src/
├── domain/
│ ├── model/
│ │ ├── product/
│ │ │ ├── Product.java
│ │ │ ├── ProductId.java
│ │ ├── order/
│ │ │ ├── Order.java
│ │ │ ├── OrderItem.java
│ │ │ ├── OrderId.java
│ ├── service/
│ │ └── OrderPolicyService.java
│ └── repository/
│ ├── ProductRepository.java
│ └── OrderRepository.java
├── application/
│ ├── service/
│ │ └── PlaceOrderService.java
│ └── dto/
│ ├── OrderRequest.java
│ └── OrderResponse.java
├── infrastructure/
│ ├── repository/
│ │ ├── JpaProductRepository.java
│ │ └── JpaOrderRepository.java
│ └── notification/
│ └── EmailNotifier.java
├── interface/
│ ├── controller/
│ │ └── OrderController.java
│ └── view/
│ └── OrderView.java
各層の責務
- ドメイン層:
Order
やProduct
といったエンティティ、ビジネスルール(例:在庫チェック、割引計算)を実装 - ドメインサービス:エンティティ間をまたぐロジック(例:注文可否判定)を
OrderPolicyService
に分離 - アプリケーション層:ユーザーの購入処理を
PlaceOrderService
で組み立てる(注文作成 → 永続化 → 通知) - インフラ層:DBとのやりとりやメール送信を担うリポジトリ・通知実装
- UI層:ユーザーが「商品を購入する」操作に対応する
OrderController
この構成にすることで、たとえば「通知方法をメールからLINEに変える」ような変更も、インフラ層の差し替えだけで済み、ドメインやアプリケーション層は一切変更不要です。
まとめ:設計の“芯”を守るという考え方を持とう
オニオンアーキテクチャは、ドメインロジックを中心に据え、それを技術的な変化や複雑さから守るための強力な設計アプローチです。依存関係の流れを内側へ統一し、「変わりにくい中身」と「変わりやすい外側」を明確に分けることで、保守しやすく柔軟なシステムが実現できます。
この記事を通して、以下のようなことが実践できるようになるはずです:
- 設計時に「どこが本質か」を意識するようになる
- ビジネスロジックと技術的な処理を自然に分離できる
- コードの責務が明確になり、チームでの連携がしやすくなる
- テストしやすく、安心してリファクタリングができる
まずはシンプルな層の分離から始めてみてください。小さな意識の変化が、長く使えるコードへの第一歩になります。