CBcloud Advent Calender 14日目の記事です。
前回の記事に引き続きエリック・エヴァンスのドメイン駆動設計(日本語訳版)を理解するための用語解説をしていこうと思います。
この記事では本書で語られる多くの背景や詳細を省いているので、この記事を読んだだけでは理解できないと思います。本書を読む前に読んでおくと理解がしやすく、後から読んだ時にどんなものだったかを思い出せるような内容にすることを目指しています。
今回は第3部「より深い洞察へ向かうリファクタリング」から「しなやかな設計」について解説します。
本記事で出てくる「ドメイン」「モデル」「値オブジェクト」などの用語の意味については他の記事を参照してください。
- その1: ドメインモデル
- その2: しなやかな設計(この記事)
- その3: モデルの整合性を保つためのパターン
しなやかな設計
「しなやかな設計」と聞いただけではこれが何らかの設計手法を指すのか、あるいは何らかのプラクティスを施した結果、ソフトウェアの設計が「しなやか」な特性を持つようになるのか、そのどちらなのか分かりづらいですが、本書では後者を指しています。
本書ではソフトウェアを「しなやかな設計」にしていくのに有効なパターンが示されています。
ドメイン駆動設計ではドメインの分析とシステム設計のどちらにも使えるモデルを見出すことが目的です。モデルに関して新たな発見があったらその発見をコードにも反映する必要があります。よってモデルとコードの表現を一致させるためのリファクタリングが繰り返し行われる必要があります。この繰り返しのリファクタリングを容易にするためにしっかりとした設計がコードに施されていないと、リファクタリングを継続するのが難しいことは容易に想像できるでしょう。
この恒常的なリファクタリングによりモデルとコードを繰り返し変形させることで、最も変更される部分の設計が柔軟になっていくと本書では述べています。この柔軟性を持つことが「しなやかな設計」です。本書では
使い込んだグローブは、指の曲がる場所はしなやかになるが、他の部分は堅く、手を保護してくれる
と例えています。
恒常的なリファクタリングは設計においてモデルの概念を改良する洞察を得ることができ、さらにそれを容易にモデルとコードに取り込むことでより洗練されたモデルを構築することにも寄与します。
しなやかな設計に向かうためのパターン
本書では、ソフトウェアをしなやかな設計にする公式はないとしながらも、経験上しなやかな設計に近づける傾向のあるパターンについて下記を挙げています。
前回の記事で説明した用語が出てきますので適宜ご参照ください。
意図の明白なインタフェース
クラスと操作の名前にはユビキタス言語を使用する。さらにその効果と目的を記述する名前にして、実現手段については言及しないようにする。こうすると開発者が内部を理解せずに済み、ユビキタス言語からふるまいを推測できるようになる。
副作用のない関数
ロジックのうち、できる限り多くの部分を関数に置くようにする。関数とは副作用なしに結果を戻す操作のこと。
コマンド(状態を変更するメソッド)は分離して、状態変更だけを行うシンプルなメソッドにしてドメインに関わるデータを戻さないようにする。クエリは値オブジェクトを生成して戻すようにする。これらにより、副作用のない関数を安全に組み合わせて複雑な演算を行うことができる。オブジェクトの変更が演算に含まれてしまうと、結果の予測が難しくなり利用者は実装を把握しなければならず、インタフェースの抽象化の恩恵が薄れてしまう。クエリの結果として受け渡された値オブジェクトは不変なので、それに対する操作は関数であるし、安全に使用できる。
表明
操作の事後条件と、クラスおよび集約の不変条件を宣言するようにする。プログラミング言語で表明を直接コーディング出来ない場合には、自動化されたユニットテストを書くようにする。
そうしないと、操作の副作用を知る方法が実装を読むしかない場合、委譲が多く行われている設計では内部を徹底して理解する必要があるため、抽象化の価値が失われてしまう。表明が記述する不変条件は、すべて状態に関することなので容易に理解できる。不変条件により結果が保証できるなら、操作がどのようなロジックであるかを気にする必要はなくなるので、実装を読む必要はなくなる。
概念の輪郭
概念的に意味のある機能の単位を見つけ出し、それに従いインタフェースを分割するようにする。技術的な思考でインタフェースの粒度を画一的に揃えることは可能だが、それでは意味のある単位でモデルを操作できないため役に立たない。
そのため、ドメインにおける自分の直感を考慮に入れることで概念を捉えたインタフェースができあがる。このインタフェースは、論理的に組み合わせることができ、自然とユビキタス言語に従った意味のある文章を作り上げるように利用することができる。
独立したクラス
オブジェクトを低結合にするのは基本。できる時に徹底的にやる。オブジェクトの概念から本質的でない他の概念をすべて取り除くようにする。
依存は設計を理解することが難しくなってしまう。ただし間違えてはいけないのは、目標はすべての依存関係を取り除くことではなく、本質的ではない依存関係を取り除くことだということ。また、依存関係を取り除くといっても、あらゆるものをプリミティブにすることで、モデルの表現力を落としてしまっては元も子もないので注意すること。
閉じた操作
戻り値の型が引数の型と同じにできる場合は、そのようにする。
引数と戻り値とで型が異なると、操作に異なった概念を持ち込むことになる。引数と戻り値の型を同じにすると、その操作はインスタンス集合の下で閉じている。閉じた操作は他の概念を巻き込まず、組み合わせが容易になる。「閉じた操作」の由来は、数学における「加算は、実数の集合の下で"閉じている"」から。閉じているのは抽象的な型の下になることもある。その場合、具体的な引数は別の具象クラスでも良い。加算は実数の下で閉じていて、実数は有理数と無理数のどちらでも良いのと同じ。
おわりに
恒常的なリファクタリングで、その変更箇所が柔軟になるという考え方はなるほど、と思う一方、なかなかに実践が難しそうです。とはいえ変更箇所は集中するものなので取り組む価値はありそうです。