はじめに
本記事は、『ドメイン駆動設計(DDD)』について少し勉強し始めた筆者が学ぶ中で「わからん!」→「なるほど!」と思ったことを書いたものです。
間違っていたらそっと教えていただけると幸いです。
ドメインモデルが貧血?
ドメインモデル貧血症とは、ドメインモデルがgetter・setterしか持っておらず、まったくビジネスロジックを持っていない状態になっていることを指します。この状態になっているドメインモデルは、外から使われる場合にその使い方を制限できず、ドメインのルールを守る実装はそのドメインモデルを使う人任せになります。
例えば、以下のルールがあるTODOリストアプリを考えます。
- 期限が3日前のToDoはリストの最上段に表示する
- 同じ内容のToDoは登録できない
- ToDoの内容は100字以内
- DBにデータは保存する
そして、以下のようなToDoItemモデルを用意したとします。
// ToDoリストに登録するToDoを表すクラス
public class ToDoItem
{
// ToDoのID
public string Id {get; set;}
// ToDoの内容
public string Content {get; set;}
// ToDoのチェック状態
public string IsChecked {get; set;}
// ToDoの期限
public DateTime DeadLine {get; set;}
// ...
// ToDoの期限をチェックするメソッド
public bool CheckToDoDeadLine(){ // 期限が近いやつはtrueが返る}
// ToDoの内容の重複を検証するメソッド
public bool ValidateToDo(){// 他のToDoと内容を比較して被ってたらfalseが返る}
// ToDoの内容が100字を超えないようにするメソッド
public bool CheckContentLength(){ // 100字を超えていたらfalseを返す}
// ToDoを保存するメソッド
public void SaveToDo(){// DBにアクセスしたりするごちゃごちゃした処理が書いてある}
}
上記のドメインモデルはいまいちです。ToDoを表すクラスなのに、色々なことをやっています。
ということで、リファクタリングをしていきます。
わたし「他のToDoも確認するなら、ValidateToDoメソッドはこのモデルにあるべきじゃないね」
わたし「SaveToDoメソッドも、このモデルがDBにアクセスするのは責務的に変だね。DBに関する処理は別クラスにして……」
わたし「ToDoの内容の文字数チェックも、別にモデルになくてもいいんじゃない? これもサービスに……」
わたし「期限のチェックも別にここじゃなくていいかも……これもサービスに……」
...
こうしてできあがったドメインモデルは以下。
// ToDoリストに登録するToDoを表すクラス
public class ToDoItem
{
// ToDoのID
public string Id {get; set;}
// ToDoの内容
public string Content {get; set;}
// ToDoのチェック状態
public string IsChecked {get; set;}
// ToDoの期限
public DateTime DeadLine {get; set;}
// ...
}
振る舞いがGetter・Setterしかないモデルになりました。
以下のルールも、サービスに移動したのできちんと実装されています。
- 期限が3日前のToDoはリストの最上段に表示する
- 同じ内容のToDoは登録できない
- ToDoの内容は100字以内
- DBにデータは保存する
ですが、このToDoItemというドメインモデルは何のルールも持っていません。
ルールがドメインモデルの外側にすべて実装されている状態になっているのです。
ToDoItemはただIDやToDoの内容や期日を持っているだけの、データの受け渡しをするためだけのオブジェクトになっています。
設計した本人はともかく、他の人がこのルールの散らばったドメインモデルを使う場合に、モデルからルールを把握して正しく使うことはできるでしょうか。
例えば今のドメインモデルの状態では、『ToDoの内容は100字以内』に制限する処理はサービスに実装されており、ドメインモデルだけでこのルールを守ることはできません。
ちゃんとサービスを使って実装してくれる実装者なら問題ありませんが、ドメインモデルに実装されていない以上、サービスを使わずにToDoの内容を設定することもできてしまいます。
モデルを利用する人次第でルールを守ることはできますが、逆に言えば、ルールを守らせることを強制できなくなるのです。
今回の例でいえば、少なくとも太字で示したルールはToDoItemの中で振る舞いが実装されるべきだと筆者は考えます。
- 期限が3日前のToDoはリストの最上段に表示する
- 同じ内容のToDoは登録できない
- ToDoの内容は100字以内
- DBにデータは保存する
まとめ
設計学び始めの頃は、モデルに振る舞いを持たせることがなんだか悪いことのように思っていました。
「いや、全部サービスに持ってもらったらいいじゃん。Getter・Setterしかないモデルの方がシンプルでいいモデルじゃない?」と。
DDDの思想に触れて、ようやく「ドメインモデル貧血症」がなんでいけないのかがわかった気がします。