はじめに
DDD+CQRSで実装すると業務知識がドメインサービスにばかりいってしまう(気がする)で悩んでいた時に、
境界づけられたコンテキストをDCIで考えてみるでDCIについて勉強したところ、
クエリで取得したDTOにロールを与えれば良いんじゃないの?と思ったので投稿してみます。
前提
DDD+CQRSで実装すると業務知識がドメインサービスにばかりいってしまう(気がする)で話していた利益率計算についての続きです。
ざっくり書くと
のように利益率計算に関するメソッドをエンティティに入れちゃうとクエリ側で使用するの大変だよねって話です。なのでドメインサービスに置くしかないかなと思ってました。
けど利益率に関するメソッドが増えてきたら何を使えば良いかわからなくなりそうな気がしていました。
ドメインサービスに散らばっていた利益率に関する知識をロールにまとめる
そこで出会ったのがDCIという考え方。自分の中ではデータクラスにロール(役割)を与えるイメージです。文脈によってロールを切り替えてユースケースを実現します。

[](
@startuml
abstract class 値オブジェクト
class 商品ID << (V,#FF7700) >> {
- Create(GUID): 商品ID {static}
}
class 原価 << (V,#FF7700) >> { - Create(double): 原価 {static}
}
class 利益率 << (V,#FF7700) >> { - Create(double): 利益率 {static}
}
class 売値 << (V,#FF7700) >> { - Create(double): 売値 {static}
}
商品ID --|> 値オブジェクト
原価 --|> 値オブジェクト
利益率 --|> 値オブジェクト
売値 --|> 値オブジェクト
interface I利益率を計算するロール { - 原価
- 利益率
- 売値
- 売値を計算する(): 売値
- 売値を計算する(原価, 利益率): 売値
- 原価を計算する(): 原価
- 原価を計算する(売値, 利益率): 原価
- 利益率を計算する(原価, 売値): 利益率
- 利益率を取得する(商品ID): 利益率
}
note bottom of I利益率を計算するロール : メソッドはデフォルト実装する
class 商品 <> {
- 商品ID
- 原価
- 利益率
- 売値
- Get原価
- Get利益率
- Get売値
- Create(商品ID, 原価): 商品 {static}
}
商品ID -down-o 商品
原価 -down-o 商品
利益率 -down-o 商品
売値 -down-o 商品
商品 --|> I利益率を計算するロール
@enduml
)
こんな感じでI利益率を取得するとかI利益率計算とか利益率に関する業務知識がドメインサービスに散らばっていたのをI利益率を計算するロールという役割としてまとめちゃいます。
そうすればDDD+CQRSで実装すると業務知識がドメインサービスにばかりいってしまう(気がする)で問題にしていた
しかしエンティティ内に記述してしまうと、別の場所で利益率計算をしたくなったときに困ります。
たとえば利益率がn%だったときの原価と売値の一覧を画面に表示したいといった場合、
CQRS的なクエリでエンティティを介さずDTOに包んで値を取得したときに利益率計算をしようと思っても出来ません。
もDTOを定義する際に

[](
@startuml
interface I利益率を計算するロール {
- 原価
- 利益率
- 売値
- 売値を計算する(): 売値
- 売値を計算する(原価, 利益率): 売値
- 原価を計算する(): 原価
- 原価を計算する(売値, 利益率): 原価
- 利益率を計算する(原価, 売値): 利益率
- 利益率を取得する(商品ID): 利益率
}
note bottom of I利益率を計算するロール : メソッドはデフォルト実装する
class DTO { - 原価
- 利益率
- 売値
}
DTO --|> I利益率を計算するロール
@enduml
)
のようにI利益率を計算するロールを継承してしまえばクエリ側でも業務知識を無理なく使える気がします。
まとめ
- エンティティでしか使わない業務知識はエンティティに実装する
- クエリ(CQRS的な)でも使用する業務知識はドメインサービスとして実装する
- 業務知識としてロール(役割)としてまとめることができるのならばロールを用意する
と思ったんですけどどうでしょうか。