はじめに
ドメイン駆動設計 モデリング/実装ガイドを読んで、ドメイン駆動設計(以下DDD)について改めて思ったことを書いていきたいと思います。
自分のDDDの経験としては、実務経験はありますが、DDDで実装されたリポジトリの機能追加くらいで、メインで普段DDDで実装しているわけではない、という感じです。
なお、DDDの解説記事というより、自分の理解を整理する記事となっております。
DDDの詳細な解説は紹介した本の著者のブログが大変参考になります。
DDDの目的についての理解
ドメイン駆動設計の目的であるドメイン(実務領域)をソースコードに落とし込むことで、実務に沿った振る舞いのみを達成するコードを書くことが、DDDの目的であると自分は理解しています。
例えば、配送方法と配送金額をプロパティに持つクラスを例にして考えてみます。
ドメイン(実務領域)として、配送方法はstandardとexpressのみ、金額は1円以上1000円以下であると仮定します。
まずはDDDを特に意識しない形で書いてみます。
class Delivery { private string $method; private int $price; public function setMethod(string $method): void { $this->method = $method; } public function setPrice(int $price): void { $this->price = $price; } // その他の処理は割愛 }
このsetterは昔からよく見る書き方ですが、型さえ合っていれば、業務であり得ない配送方法や金額でも渡すことができます。
またコンストラクタで一度のみ渡すわけではなく、オブジェクトが生成された後でもsetter経由で変えることが出来ます。
このクラスに対して、ドメイン駆動設計の考えを適用して変更を加えてみます。
class Delivery { private string $method; private int $price; public function __construct(string $method, int $price) { if (!in_array($method, ['standard', 'express'])) { throw new InvalidArgumentException("Invalid delivery method"); } if ($price < 0 || $price > 1000) { throw new InvalidArgumentException("Invalid delivery price"); } $this->method = $method; $this->price = $price; } // その他の処理は割愛 }
まずはpublicなsetterではなくコンストラクタでオブジェクト生成時に値が入るようにして、不変性を担保して後から意図しない変更が入らないようにしました。
また、ドメイン(実務領域)として、配送方法はstandardとexpressのみ、金額は1円以上1000円以下であるというルールに則った値以外は入らないようにドメイン知識をソースコードに取り入れてバリデーションしています。
ドメイン駆動設計で何を達成したいのか考える時は、いきなりレイヤーの話からするのではなく、上記のような例で考えると理解しやすい、と自分は感じました。
各レイヤーについて
ドメイン駆動設計はいくつかのレイヤーに分かれて、各レイヤーがそれぞれ責務を持っているため、主要なレイヤーについて記載します。
プレゼンテーション層
ユーザーの入力・要求を受け付けて、必要なビジネスロジックを呼び出します。 配送の例で言えば、配送方法の一覧を取得するリクエストを受け付けて、必要なアプリケーション層の処理を呼び出します。
アプリケーション層
プレゼンテーション層のリクエストを処理して、ドメイン層を利用して適切な業務のユースケースを達成させます。
命名も業務のユースケースに沿ったものとなります。
配送の例で言えば、配送方法について取り扱っているドメイン層の処理を呼び出します。
アプリケーション層自体は配送方法についての詳細な業務知識(ドメイン知識)を持っていません。
ドメイン層
ドメイン(実務領域)の知識を持っている層で、ドメインに沿ったオブジェクトの情報やビジネスロジックを持っています。
先述の配送方法や配送価格の落とし込みはドメイン層で行われます。
配送の例で言えば、この層が配送方法の一覧や紐づく価格を知っているので、インフラ層を利用して必要な情報を返したり、登録処理などを行います。
インフラ層
DB操作など、ドメイン層で必要とされるインフラ領域の操作を担当します。
いわゆるRepositoryクラスの実装がメインとなります。