なぜドメインモデルを採用するのか?


更新: ドメインモデルに切り出す判断基準

有名なアーキテクチャであるDDDクリーンアーキテクチャでもレイヤーとして存在しているドメインモデル。なぜドメインモデルを用いることが長期的な開発には有効とされているのかいまいちわからなかったので、ドメインモデルの中でもドメインEntityについてまとめてみました。

そもそもドメインモデルとは?

ドメインモデルとは、システムの主要な実体とそれらの関係を明確にし、一般にそれらの重要なメソッドと属性も洗い出す。すなわち、このモデルは、システムの構造的な観点を提供します。その先はUse caseだったりのいわゆるServiceに渡してビジネスロジックを組み立てていきます。

なんのため?

ドメインモデルを導入する理由を一言で表現するのは難しいと思ったので、ここでは大きく分けて2つに分けて考えたいと思います。

1.コードの表現力を上げるため

例えば、Qiitaのようなサービスを作るとします。記事は題名と本文が記載されていないと投稿はできない仕様の場合、PHP記事は題名と本文が記載されていない表現をドメインモデルで表現してみるとこのように書くことができます。

class Post
{
    ...
    public function isPublished(): bool
    {
        return $this->getAttribute('status') === Enums\Post\Status::PUBLISHED;
    }

    /**
     * 公開済みの場合はタイトルと本文が空ではいけない仕様を定義
     */
    protected function mustNotEmptyAttributesIfPublished(): void
    {
        if (!$this->isPublished()) {
            return;
        }

        $keys = ['title', 'body'];

        foreach ($keys as $key) {
            $value = $this->getAttribute($key);
            if (empty($value)) {
                throw new ValidationException('Published post must not have empty ' . $key . '.');
            }
        }
    }
    ...
}

このようにあらかじめ仕様を定義することが可能です。

これを例えばService層で実装するとしても、Service層にはすでにビジネスロジックを担当するという責務があります。上記のようなビジネスロジックには該当しないような処理は書かない方が設計の破綻を防げますし、後々リファクタリングする際も処理が混在していないので比較的楽にリファクタリングできるかと思います。

2.ロジックの混在を防ぐ

こちらは1の最後に書いたことにつながります。各処理の実装はそれぞれのレイヤーが持つ責務に合わせて実装するべきで、通常1つのレイヤーに2つ以上の責務を持たせるのはアンチパターンとされています。(SOLID原則違反)

ドメインモデルに切り出す判断基準

ドメインモデルを用いるには、どこからどこをドメインとして切り出すかが重要になってくるかなと思います。
データベースのテーブルごとに考えるのか、サービスの機能ごとに考えるのか、また機能ごとに考えるにしてもデータのライフサイクルがその機能だけで実装されているのか等判断基準は様々です。

ドメインモデルはこれで終わりではない

今回はかなり軽くまとめましたが、ドメインEntityValueオブジェクトを組み合わせると、意図しない値を存在させない,意図しない代入を防ぐなどの恩恵も受け取ることが可能になります。

なぜドメインモデルを採用するのか?

長期的な開発になって規模も大きくなっていくとコード量やデータベースからの正しい(必要としている)値受け取り、機能などの処理の流れ... など複雑になっていきやることが必然的に大きくなっていきます。そういった作業を少しでも負担を減らすという意味でもドメインモデルを採用することは受け取れる恩恵が大きいかなと思いました。

この記事を読んでなぜドメインモデルを採用することが良いとされているのか(開発規模によりますが)、少しでも興味を持ってくだされば幸いです。