プログラミングの初心者から中級者、さらにその上を目指すにあったって、おそらくソフトウェアアーキテクチャの理解は避けては通れないものだと思います。
その中で、単なるオブジェクト指向やエクストリームプログラミングの先にある、現在の基礎的な考え方となっているドメイン駆動設計について、改めて書籍を読んで勉強してみました。
どの本から入ると読みやすい みたいなルートがあるようですが、私には読むべき本を選ぶ力がなかったので(笑)原著をよんで自分なりに解釈してみることにしました。
ドメイン駆動設計とは
ドメイン駆動設計(Domain Driven Design = DDD)とはドメインに注力して設計しましょうという設計の考え方の一つです。
ドメインとは解決したいビジネス上の課題を含んだ領域のことで、乱暴に言ってしまえばいわゆる、業務知識のことです。
ドメインについて詳しい人のことをドメインエキスパートと呼び、開発者はその人と会話をしながらドメインへの知識をソフトウェアで表現していきます。
至極当たり前のことを言っていますが、エリック・エヴァンスのドメイン駆動設計の中では、アナリスト(ドメイン知識を見える化して開発者に仕様を伝える人)と開発者が別であってはいけないと主張しています。
ドメインに関する知識をドメインエキスパートとのコミュニケーションによって深めていきながら、適切にモデリングを行います。
ドメイン駆動設計では上記のように、ドメインエキスパートとの会話でドメイン知識をモデル化します。
これをドメインモデルと呼び、UML図やその他ドキュメントなどで表現していきます。
ユビキタス言語
ドメイン知識をモデリングする際に、重要になる考え方が「ユビキタス言語」です。
アナリストと開発者を分けた時によく起こる問題の一つが、使っている言葉が違うので徐々にソフトウェアの仕様とドメインモデルが乖離していくということです。
ドメインエキスパートとの会話の中で出てくる言葉はそのままソフトウェアとして表現していくべきであり、例えば曖昧な定義が出てきたら、それは一緒に再定義しましょうということです。
会話する際の言語がそろうことで、ソフトウェアとドメインの実体に差が出にくくなり、適切にドメインを表したソフトウェアが実装出来ます。
ドキュメントと図
ソフトウェアで使う言葉がいくらドメインエキスパートの使うそれと同じになったからと言って、コードを見せながら会話するわけにはいきません。
ですが、コードの詳細な仕様や逆に全体の概要を示すものがないとドメインエキスパートとの会話も成り立ちませんし、開発者同士の会話も大変になります。
ですので、適切に図やドキュメントを使用して表現していく必要がありますが、エリック・エヴァンスはあくまで仕様を理解する補助的なものとして使用すべきと唱えています。
詳細すぎるドキュメントは、メンテナンスコストがかかりすぎて形骸化してしまうし、UML図などでいくら詳細にドメインを表現しようとしても、コードと完全に一致させることはほぼ無理です。
ですので、ドメインエキスパートとの会話が適切に行える粒度を探りながら、本質的な図を書いたり、また開発者向けには、コードから読み取れない実装の背景などを伝えるために、ドキュメントを記述した方が良いと書かれています。
レイヤードアーキテクチャ
ソフトウェアを実装する際に、どういったアーキテクチャを採用するかというのはたびたび議論になりますが、ドメイン駆動設計ではドメインに関する部分に注力したいということで、レイヤードアーキテクチャを採用するのが一般的なようです。
おそらくドメインとその他の部分が適切に分離出来るのであれば、以下に示すアーキテクチャである必要は特になく、開発規模やメンバーの習熟度などで選択すればよいのだと思います。
とりあえず、エリック・エヴァンスのドメイン駆動設計に書いてあったアーキテクチャを紹介します。
大きく4つの層に分ける
エリック・エヴァンスのドメイン駆動設計 では4層に分けて設計すると良いと書かれています。
プレゼンテーション層
ユーザーへの情報の表示や、ユーザーからの入力の解釈を行う。いわゆるUIの部分。
MVCモデルでいうところのViewにあたるところ。ユーザーというのは実際に画面を操作する人だけというわけではなく、別のシステムなどに置き換わる場合もある。
アプリケーション層
ビジネスにとって意味があるものか、他のアプリケーション層と相互作用をする作業を担う。
分かりやすく言うと、ドメイン層を使う側の層にあたる。ドメインに関するロジックなどはドメイン層に任せるので、アプリケーション層ではあくまで作業を移譲して調整を行うだけ。
ドメイン層
ビジネスルールを表すところ。ビジネスの状態自体もここで制御を行う。
技術的な実装も排除する。例えばDBへのデータの格納などはここでは行ってはいけない。
インフラストラクチャ層
上位のレイヤを支える一般的な技術機能。データの永続化のためにDBに関する処理を行ったり、メッセージ送信を行ったりする。
注意点
上記のような層に分けるて、基本的に上位のレイヤに対しては疎結合になるように心がける。ドメインモデルに関係するコードをドメイン層のみに集中させ、他のレイヤーから分離すること。そうすることで、ドメインをより的確にコードで表すことが出来るようになる。
ただし、下位のレイヤが上位のレイヤからの呼び出し応答など以外に通信が必要になる場合もある。例えばDBのデータが書き変わって、それを画面に反映したい場合など。
そのような場合には、コールバックやオブザーバといったアーキテクチャパターンを用いて実装すること。
DDD固有のデザインパターン
DDDでは上記のレイヤードアーキテクチャに迎合するパターンが使われています。
そのパターンを紹介します。
オブジェクトモデルの基本要素
エンティティ
エンティティは別名参照オブジェクトと言い、同一性を持ち可変です。つまり状態を持つことが出来ます。
例えば顧客とか口座とかそういう複数存在してそれぞれが別ものか違うものかを区別したいものに対して使います。
値オブジェクト
値オブジェクトはエンティティと粒度的には似ているのですが、真逆の性質をもつものです。その性質とは同一性を持たず、不変であるということです。
例えば、顧客エンティティがもつ住所などが値オブジェクトです。
またエンティティと違う点として、値オブジェクト同士の関連は持たせてはいけません。
サービス
エンティティのも値オブジェクトにも属さないが、ドメインにとって重要なものやふるまいをサービスとして定義します。引数と結果はドメインオブジェクトになりますが、必ずしもドメインレイヤに居る必要はありません。アプリケーションサービスやインフラストラクチャサービスなども考えられます。
ドメインオブジェクトのライフサイクル
上記のようなドメインオブジェクトにはライフサイクルがあります。オブジェクトが生成され、参照され、そして最終的には破棄されます。
オブジェクトの寿命も様々です。すぐに破棄されるものもあれば、長い間メモリ上に存在し続けるオブジェクトもあるでしょう。こういったオブジェクトを管理することに気を取られてしまうと、ドメインに集中できないので、以下のような3つのパターンを用います。
集約
集約は関連オブジェクトの集まりです。例えば口座振替には、残高が増える口座と減る口座がセットで存在するように、ある一連の処理をまとめて行う必要がある場合があります。
そのようなオブジェクトをまとめて、データを変更するための単位とします。
集約にはルートと境界があり、ルートは集約の中の特定の1エンティティです。ルートエンティティはドメイン内でグローバルな同一性を持ち、境界内部のエンティティは集約内でのみ一意を保つローカルな同一性を持ちます。
また、境界外のエンティティは集約ルートのみ参照を持つことが出来、境界内部への参照を持つことが出来ません。(ルートエンティティ以外には別の集約からはアクセスできないようにしておく)
ファクトリ
ファクトリはオブジェクトの生成を担うものです。オブジェクトの生成はそのオブジェクトの責務ではないので、別にクラスを作って実装することがあります。ただ、必ず分ける必要があるかというとそういうわけでもなく、コンストラクタで十分であれば、集約ルートのコンストラクタが生成を行えば良いです。
リポジトリ
データベースやストレージへの永続化をカプセル化して隠蔽化します。リポジトリは外から見るとあたかも、あらゆるデータにインメモリでアクセスできるかのようにふるまいます。
また、技術的な工夫もこの中に閉じ込めます。技術的な工夫とは例えば、データベースのアクセスの仕方によっては著しくパフォーマンスが下がってしまう場合などに、リポジトリでクエリを制限することによってそれを解消します。
通常は集約ルートに対してリポジトリを作り、オブジェクトそのものを返したり、場合によってはある特定の数学的な集計結果(金額の合計値など)を返すこともあります。
まとめ
以上で エリック・エヴァンス のドメイン駆動設計の第6章までをざっくりまとめたものになります。
第7章で具体的な例を示し、第8章以降はより実践的な話題になっていくので、ここまでが入門編といったところでしょうか。
ただ、この第6章までで、なんと160ページくらいあります笑
書籍ではいろいろな例を示しながら、なぜこのようなパターンにすべきなのかを解説してありますので、覚悟をもって一度読んでみることをお勧めします笑
参考:
コメント