オニオンアーキテクチャを知る
オニオンアーキテクチャについて調べていく…
よさげな記事を収集
提唱者Bobさんのブログ
クリーンアーキテクチャの目的
関心の分離
ソフトウェアを責務ごとに分割する(UIとか、ビジネスロジックとか)。
※クラス単位の関心の分離よりもスコープ広め
それによって、モジュール間の依存関係を制御したい。
UI、技術的な変化やアプリケーションの変化が、システムで実現したいこと基本的に変わることがないビジネスルールに影響が及ばない構成であれば、安心安全。
関心の分離がもたらすもの
- フレームワークへの非依存
- テスト容易性
UIやDBなどの層ができていなくても、ビジネスロジックをテストできる(それらに密に依存していないから)。インターフェースの活用かな。 - UIへの非依存
UIをごっそり変更してもビジネスロジックには影響が出ない。 - DBへの非依存
DBをRDBからNoSQLに変えるーとかに対して影響を受けない。 - 外部機能からの独立
依存関係について
同心円の図でソフトウェアをレイヤーを表す。
内側に進むほどソフトウェアは高レベルになる。
高レベルは要件とか実現したい目標みたいなイメージ(ポリシー)。
逆に低レベルは、ポリシーを達成するための具体的な手段のこと(メカニズム)。
特徴は依存関係の向き
各レイヤーは内側に向かってのみ依存することが許されている。
そのため、内側のレイヤー(ポリシー)は外側のレイヤーに対して何も知る必要がない。
具体的には、外側のレイヤーで宣言されたものの名前を内側のレイヤーから言及してはいけない。
同様に、外側のレイヤーで使われているデータフォーマットを内側のレイヤーで使うべきではない。
外側のレイヤーの実装詳細や機能修正・拡張が内側のレイヤーに影響を与えないようにする。
各レイヤーの概要
Enterprise Business Rule(企業のビジネスルール)
Application Business Rule(アプリケーションのビジネスルール)
Interface Adapters(インターフェースアダプター)
Frameworks & Drivers(フレームワークとドライバ)
オニオンアーキテクチャとクリーンアーキテクチャの違い
この手の話で出てくるビジネスロジックとかビジネスルールとかビジネスなんちゃらってやつの理解が曖昧な気がする。
ビジネスロジック
アプリケーションをプレゼンテーション・ビジネスロジック・データアクセスの 3 つに分けたとき、「プレゼンテーションでもデータアクセスでもない部分がビジネスロジック」
消去法か…
データアクセスはファイルやDBへの読み書きをする層。
プレゼンテーションはアプリとユーザとのやり取りを担当する層。
MVCならViewやController、APIならController・リクエスト、レスポンスの型…。
それら以外の、システムが持つルール、目的を処理するところがビジネスロジック。
ビジネスルール
ビジネスがシステム化されようがされまいが存在するルールのこと。
ECショップだと出品者、店舗、商品などの実体(名詞)がビジネス上では存在していて、それらの名詞同士の関係性によってそのビジネスルールが説明できる。
「出品者」が「店舗」を開いて、「商品」を陳列した
なるほど。この関係性のことがビジネスルール。
クリーンアーキテクチャではEntitiesとしてEnterprise Business Rule層に実装するわけね。
実際に設計に落とし込む際には、まずシステム化対象の業務知識を把握して、その業務における登場人物、名詞的な概念(ドメインモデル)を抽出する。
そのあと、それらの互いの関係性をまとめたビジネスルールを洗い出して、そのルールに対応する振る舞いをドメインモデルに持たせるようにしてEntitiesたちを仕上げていくって感じか?
Clearn Architectureでは...
ビジネスマネーを生み出したり、節約したりするルールや手続きのことと定義している。
手動で実行されたとしてもお金を生み出すことができる。
小売店を例に挙げると、商品を仕入れて在庫情報を管理することは手でノートにつけてもいいし、システムを介してDBに保存しても達成したいことは一緒。商品の販売でも対面で販売するのもオンラインで販売するのも利益を生むという目的は一緒。
このようなルールを最重要ビジネスルールという。
また、ビジネスルールにはいくつかのデータが必要になる。
小売店では、店舗や商品、商品価格、売上などが必要。このようなデータを最重要ビジネスデータと呼ぶ。
最重要ビジネスルールと、最重要ビジネスデータは互いに関連しているため、システムで扱うオブジェクトの候補になる。
クリーンアーキテクチャではこのオブジェクトをエンティティとしている。
要はビジネスルールを実現するための一連の処理がビジネスロジック。
たとえば、商品の購入処理だと、商品の在庫を確認して、クレジットカードで決済を行い、注文情報をデータベースに保存する、などの処理。
オニオンアーキテクチャ
ソフトウェアの構造を責務ごとにレイヤー化し、各モジュールの依存関係・結合度をコントロールすることを目的とした設計思想のこと。
依存関係・結合度をコントロールすることにより、ソフトウェアの保守性の向上、テスト容易性の向上、再利用性の向上が実現できる。
分割するレイヤーの種類は図で示す通り、以下の4層に分けられる。
- UI・インフラストラクチャ層
- アプリケーションサービス層
- ドメインサービス層
- ドメインモデル層
また基本的な原則としては、図の外側のレイヤーから内側のレイヤーへのみ依存することが許されており、基本的に変更されることが少ないビジネスルールが、頻繁に変更される要素によって影響を受けづらくすることができる。
ドメインモデル
円の最も中心に位置する層であり、組織の信条・ビジネスルールをモデル化したデータと振る舞いの組み合わせによって構成される。ドメインモデルの周りのレイヤーはドメインモデルに依存するが、ドメインモデルは他のレイヤーに依存しないような構造になる。
具体的なモジュールとしては、エンティティ・バリューオブジェクトなどで構成される。
ドメインサービス
ドメインモデルに直接関連する操作や複数のドメインオブジェクトにまたがる操作を提供する。
ビジネスルールとビジネスロジックの実装を含むこともある。
オブジェクトを保存したり取得したりする振る舞いを持つインターフェース(リポジトリインターフェース)がこの層に該当する。
オブジェクトの保存はデータベースという外部要素を含んでいるため、アプリケーションコアにはインターフェースのみを含むようにする。
アプリケーションサービス層
アプリケーション固有の振る舞いを実装する。UIや外部APIとのやり取りを処理し、ビジネスロジックの組み立てやドメインモデルへの操作を行う。
ドメインモデルの操作の組み合わせによってユースケースを実現する。
インフラストラクチャ層
DB(データの永続化や取得)、外部サービス、ログ出力などの外部要素とのやり取りを担当。DBの永続化や外部要素とのやり取りを担当する。
しばしば変更される可能性が高いUIやテストなどもこの層に該当する。
インフラ
ユーザインタフェース
Domain Serviceと、Application Serviceの違いが曖昧
商品が注文されるユースケースを例に、各レイヤーの依存関係を見る。
注文のリクエストを受け取った時、注文のバリデーションを行い、DBに注文情報を保存し、注文商品の在庫を更新、注文金額を引き落とし、最終的に顧客に注文が完了したことを通知したい。
インフラ層にあるGRPC Server(APIエンドポイントかな?)からアプリケーションサービス層にあるOrder Serviceを呼び出す。
アプリケーションサービスはユースケースとも呼ばれ、要求のために複数のステップを動員し機能を果たす責務を持っており、ビジネスロジックが書かれるべきではない。
アプリケーションサービスは、ユーザーの要求を満たすための他のサービスと連携する。
つまりこのアプリケーションサービスのOrder Service自体にビジネスロジックが書かれておらず、ドメインのルールを持つドメインサービスを呼び出すことによって最終的に返却するデータや結果を得る。
注文のバリデーション(注文内容に問題がないかの検証)はOrderというドメインモデル/エンティティが持つべき振る舞いであるため、Order ServiceからドメインモデルであるOrderが持つバリデーション処理が呼び出される。
ドメインモデルは、コード内でユビキタス言語の概念をモデル化するために使用されるもの。ドメインは業務固有の知識と捉えていいかも。
エンティティには、ドメインが持つ属性や振る舞いが定義される。
Orderエンティティだと、OrderIdやAddress、UserInfo、OrderItemsなどの属性と、AddOrderItemsやGetPrice、ValidateOrderなどの振る舞いがカプセル化されている。
その後、ドメインサービス層にあるPrice Compulationに対して、価格情報の取得処理を呼び出し、同じくドメインサービス層にあるOrder Repository(インターフェース)に対して、注文を登録する処理を呼び出している。
ドメインサービス層は、ドメインロジックと、ビジネスルールを持っている。基本的にビジネスロジックはドメインサービスが持つ。
アプリケーションサービスによって、ビジネスのユースケースに応じてどのドメインサービスを使用するかが調整されるイメージ。
注文処理に関しては、価格設定や、税金情報の計算、DBへ保存するための処理のリポジトリインターフェースなどが該当する。
DBに何かしらアクセスする場合はこの層のインターフェースをインフラストラクチャ層で実装する。
イメージだと、インフラストラクチャ層であるAPIからアプリケーションサービス層にある〇〇サービスが呼び出される。
〇〇サービスはユースケースを満たすために、ドメインモデルが持つ属性や振る舞い、ドメインサービスが持つビジネスルール(複数のエンティティを跨ぐようなロジックなど)を操作する。
その過程で、データの永続化や取得が必要になるようであれば、ドメインサービス層にあるレポジトリインターフェースの持つメソッドを呼び出す。
実際の処理は、インフラストラクチャ層にあるリポジトリクラスがORMを呼び出したり何かしらのデータアクセスを行って目的を達成する(アプリケーション側はこの詳細を知る必要がない)。
これもいい記事
オニオンアーキテクチャで使用される「ドメイン」とDDDで使われる「ドメイン」が若干違う。
DDD
- ドメインサービス
- ビジネス上発生する処理ではあるが、ドメインオブジェクト自身が持つメソッドとしては違和感がある場合に、〇〇〇Serviceのようなクラスを作成し、特定のドメインオブジェクトに属さないビジネスロジックを記述する。
- アプリケーションサービス
- ドメイン知識に基づかずに、アプリケーション特有の処理を記述する。
- 例えば、商品の注文が完了したら注文者にメールを送信するみたいなケース。
- メール送信はアプリケーション特有の処理。
- ドメイン知識に基づかずに、アプリケーション特有の処理を記述する。
「概念としては存在しているが、実態のないもの」に対してDDDでは「サービス」という名前を使います。
イメージしやすい。
オニオンでのドメインモデル
アプリケーションのコアがドメインであるという点は共通しているが、ドメインモデルとドメインサービスの2層に分けられている。
オニオンでは、Entity
やValue Object
、それらのAggregate
など、登場人物の実態を指すイメージ。
そして、それらの登場人物一人に対してカプセル化することができる専門知識(属性)、ロジックを表現したものがドメインモデル。
ただし、複数の登場人物の関係性によって実現できるビジネスロジックも存在しているため、それらをカプセル化するのがドメインサービスと言える。