はじめに
本記事は、『ドメイン駆動設計(DDD)』について少し勉強し始めた筆者が学ぶ中で「わからん!」→「なるほど!」と思ったことを書いたものです。
間違っていたらそっと教えていただけると幸いです。
サービスって二種類あるんですか
ある日のレビュー中に「これはDDDのアプリケーションサービスだから~……」という会話がありました。
その時は「なるほどアプリケーションのサービスね!」と思ったものの、
「そういえば、サービスはドメインサービスとアプリケーションサービスの二種類あったよね?」
「何が違うんだっけ……?」
というわけで、ちょっと調べ直してみました。
ドメインサービスとは
ドメインモデルの振る舞いはドメインモデルが持つべきです。
たとえばToDoリストアプリケーションが、ToDoタスクひとつひとつを表す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にアクセスしたりするごちゃごちゃした処理が書いてある}
}
例えば、上記のうちValidateToDo()
メソッドは、他のToDoItemのインスタンスを見て重複のチェックを行うことになりそうですが、それはToDoItemとしてはいささか責務を持ちすぎ感があります。
また、SaveToDo()
メソッドはこのクラスに書くと随分複雑になりそうです。
しかし、二つともこのドメインにおいては絶対に必要な振る舞いです。そういったときにドメインサービスという、状態を持たないサービスとしてこれらを切り出すことができます。
ドメインの振る舞いの中で、ドメインオブジェクト自身に責務を持たせにくい(ほかのインスタンスの情報を利用するなど)振る舞いを実現することがドメインサービスの仕事になります。
なお、すべての振る舞いをドメインサービスとして切り出すのはNGです。
以前の記事で、すべての振る舞いをドメインサービスとして切り出してしまった結果である『ドメインモデル貧血症』について書いています。
アプリケーションサービスとは
アプリケーションサービスは、システムのユースケースを実現するためのサービスです。
例えば、先のToDoリストの例でいえば、
- ToDoを追加できる
- ToDoを削除できる
- ToDoを並べ替えられる
- ToDoを保存できる
- 完了したToDoにチェックを付けられる
...といったものがToDoリストのアプリケーションで実現したいユースケースになります。
これらをドメインのオブジェクトを利用しながら実現するのがアプリケーションサービスの仕事です。
このとき、アプリケーションサービスにドメインのルールが漏れ出さないようにする必要があります。
アプリケーションサービスはあくまでドメインオブジェクトを使ってユースケースを実現することのみを行い、ドメインのルールをアプリケーションサービス内で実現してはならないのです。
アプリケーションサービス内にドメインのルールを書いてしまうことで、コピーコード(変更時にバグの温床になる)が発生しやすくなります。
そもそもドメインオブジェクトがどう使われるかはわからないので、「こういう使い方をしてほしくないなあ」というルールはドメインオブジェクト側に存在するべきです。
同じ"サービス"だけど全然違う
ドメインサービスは、ビジネスロジックの中でドメインモデルに責務を負わせにくい振る舞いをサービスとして切り出したもので、言ってしまえばドメインの一部です。
一方アプリケーションサービスはアプリケーションのユースケースを実現するために存在するサービスであり、アプリケーションの目的を達成するために対象のドメインオブジェクトやドメインサービスを「利用する」サービスになります。