Open3

【学び】モデルでドメイン知識を表現する(DDD)

koukou

不変条件

不変条件: モデルが有効である期間中、常に一貫している必要のある状態のこと

そのドメインモデルが満たしているべき 「仕様」 のこと。個人的には仕様という言葉が一番分かりやすいが、DDD用語としては「不変条件」が一般的とのこと。

koukou

ドメイン知識を表現出来ているモデルと表現出来ていないモデル

ドメイン知識を表現出来ていないモデルについては参考記事参照。

以下は、ドメイン知識を表現出来ているモデルについて引用。
※個人的に分かりやすいようにPHPコードに置き換え済

Task.php
class Task {
    private int $id;
    private TaskStatus $taskStatus;
    private string $name;
    private Carbon $dueDate;
    private int $postponeCount;
    private const POSTPONE_MAX_COUNT = 3; // クラス定数に現状型は付けられない(✕:private const int POSTPONE_MAX_COUNT = 3;)

    /*
     * コンストラクタ: エンティティ作成時の仕様を表現する(完全コンストラクタ)
     * Taskエンティティを作成する際には、必ずこのコンストラクタを使用する
     */
    public function __construct(string $name, Carbon $dueDate)
    {
        if (name == null || dueDate == null) {
            throw new IllegalArgumentException("必須項目が設定されていません");
        }

        $this->name = $name;
        $this->dueDate = $dueDate;
        $this->taskStatus = 'UNDONE'; // Enumにできるとより良い(?)
        $this->postponeCount = 0;
    }

    /*
     * 状態遷移メソッド:作成済みエンティティの状態遷移に関する仕様を表現する
     */
    public function postpone(): void
    {
        if ($this->postponeCount >= self::POSTPONE_MAX_COUNT) {
            throw new IllegalArgumentException("最大延期回数を超過しています");
        }

        $dueDate->addDay();
        $this->postponeCount++;
    }

    public function done(): void
    {
        $this->taskStatus = 'DONE'; // Enumにできるとより良い(?)
    }

    // nameのsetterは存在しないので、nameを変更することはできない

    /*
     * getter、状態取得メソッド
     */
    public function getId(): int{ return $this->id; }
    public function getName(): string { return $this->name; }
    public function getDueDate(): Carbon { return $this->dueDate; }
    public function isUndone(): bool { return $this->taskStatus == 'UNDONE'; }
    public function canPostpone(): bool { return $this->postponeCount < self::POSTPONE_MAX_COUNT; }
}

Taskの状態(仕様)は全てTaskドメインモデルの中で管理されていてとても見やすい。

参考記事でも書いてある通り、

  • Taskクラスを読むだけで、Taskモデルの不変条件が理解できる
  • アプリケーションでどのようなコードを書こうが、不変条件を破った状態遷移をさせることができない
  • レビュー時にもこのクラスだけ見れば安心
  • 1クラス単体テストで不変条件保持の担保ができる

これらの点は、モデルの中にそのドメイン知識を表現しておく大きなメリットだと感じた。

とりわけ重要なのは、

  1. 「タスク」というドメインがどういった仕様であるのかが正確に表現されていること
  2. 「タスク」の状態がどのように移り変わるのかが正確に表現されていること

という2点が確りと押さえられていることが、ドメインモデル実装における勘所なのだと思う。

今回で言えば、1.は、「タスク名は必須であること」や「タスク生成時は未完了状態から始まる」といったことに該当し、2.は、「未完了状態から完了状態に状態が遷移すること」といったこと。