DDDで用いられることが多いオニオンアーキテクチャの基礎知識を備忘録としてまとめました。
オニオンアーキテクチャとは?
レイヤードアーキテクチャから、依存関係逆転の原則を用いてドメイン層とインフラ層の依存関係を逆転させたのがオニオンアーキテクチャです。
レイヤードアーキテクチャや依存関係逆転の原則を理解しないままオニオンアーキテクチャを学習しても理解が追い付かないと思ったので、まずは基礎知識を整理します。
前提知識
オニオンアーキテクチャが提唱された背景を理解するのに必要な知識を整理します。
- 3層アーキテクチャ: 「プレゼンテーション層」「ビジネスロジック層」「データアクセス層」から成るアーキテクチャです。
Ruby On RailsやLaravel等のFWで見られる構成です。3層アーキテクチャには**「ユースケースとドメイン知識がビジネスロジック層に詰め込まれるため、責務過剰になる」(低凝集になる)、「ドメイン知識がモデルクラスに定義されるとドメイン知識がデータアクセス層に漏れ出す」(密結合になる)** という2つの欠点があります。
3層アーキテクチャの問題を解消するために次のレイヤードアーキテクチャが生まれました。
- レイヤードアーキテクチャ: ビジネスロジック層をアプリケーション(ユースケース)層とドメイン層に分けたアーキテクチャです。
ユースケースとドメイン知識という性質が異なるものを別々のレイヤーに配置することで、各レイヤーの凝集度が高くなりました。しかし、レイヤードアーキテクチャではドメイン層がインフラ層に依存しており、レイヤー間の結合度が高くなっています。
DDDではドメイン層を他の層から独立させることが望ましいとされます。(インフラ層の特定の技術に依存させない、モデルを継続的に改善する等の目的があります)
ドメイン層をインフラ層から独立させるためにオニオンアーキテクチャが提唱されます。
オニオンアーキテクチャを理解するために依存関係逆転の原則を確認します。
- 依存関係逆転の原則
依存関係逆転の原則とは以下のように定義されます。
- 上位レベルのモジュールは下位レベルのモジュールに依存してはならない。どちらのモジュールも抽象に依存すべきである
- 抽象は実装に依存してはならない。 実装が抽象に依存すべきである
『実践ドメイン駆動設計』P.119
レイヤードアーキテクチャは上位レベルのモジュール(ドメイン層)が下位レベルのモジュール(インフラ層)に依存しているため、この原則に反しています。
オニオンアーキテクチャの概要
リポジトリのインターフェースをドメイン層に、実装クラスをインフラ層に定義 します。
ドメイン層⇒インフラ層への依存関係がなくなり、逆にインフラ層がドメイン層に定義されたインターフェースに依存するようになりました。
※ 破線と白〇はインターフェースの実装を表す
これにより、ドメイン層がインフラ層の特定の技術(RDBMSやNoSQL等)に依存せず単体で独立したレイヤーになりました。
オニオンアーキテクチャにおけるレイヤー毎の責務
プレゼンテーション層
アプリケーション外部との入出力を実現することや、レスポンスデータの整形・形式選択を責務とします。
プレゼンテーション層に定義するクラス
- コントローラ
- アプリケーション外部との入出力を定義するクラス
ユースケース層:
ドメイン層が公開している操作を組み合わせてユースケースを実現します。特定のクライアントには依存しない実装にします。(クライアントがブラウザでも他のプログラムでも動作するようにする)
ユースケース層に定義するクラス
- ユースケース(アプリケーションサービス)クラス
- プレゼンテーション層との入出力を定義するクラス
ドメイン層
ドメイン知識を定義する層です。 他の層に依存しないように設計します。
また、他の層に公開するメソッドは必要最低限にして低凝集にならないようにします。
ドメイン層に定義するクラス
- エンティティ
- 値オブジェクト
- ドメインオブジェクト
- リポジトリのインターフェース
- ドメインサービス
- ファクトリ
- これらのインターフェース
(他にもファーストコレクションオブジェクトやEnum型でドメイン知識を表現するオブジェクト等を定義する場合もあります)
疑問点
-
インフラ層の技術が変わることは実際の開発でそれほど多くないのでオニオンアーキテクチャを採用するメリットは薄いのでは?(インフラ層の技術が不変であるならドメイン層をインフラ層から独立させる理由がなくなりそう)
-
依存関係逆転の原則を適用するのはドメイン層とインフラ層の間だけでよいのか?
まとめ
疑問点もありますが、オニオンアーキテクチャは3層アーキテクチャより保守性が高く、クリーンアーキテクチャよりもシンプルで扱いやすいアーキテクチャのように思いました。