昨年末にだらだらDCIに関する自分の考えを整理したくて身内で話していて、
結論としては「DDDとDCIどちらもメンタルモデルに近づけるために機能してる点は変わらない。その先DDDあるいはDCIをフレームワークにするか、あるいは一部に取り入れるのか、そこは取組むドメインによって取捨選択だよね」というところに落ち着いたのだけれど、勿体無い内容な気がするので改めてブログに書くことにする。
DDD脳から見たDCIの考察
DCIはFATなドメインモデルに対するアプローチというよりは、シナリオを明確にするためのアプローチなのかなと思う。
DDDを実践するような複雑な問題に直面した時、ドメインモデルは山のように増える。より知識を噛み砕いてドメインモデルにしたほうがより上層のロジックが簡潔になるので積極的にドメインモデルに落とし込む方がよく、結果として、シナリオを満たす為のAggregateも増えに増える事が分かった。知識がそのままモデルになるので、量に対する抵抗はなかった。しかし、‘蒸留’の工程で改めて俯瞰して見ると、Aggregateのインターフェースがどういうシナリオに対して提供されているか不明瞭だ、という事に気づいた。
実運用フローとして、DDDではシナリオが追加された時、まずドメインモデルがシナリオをクリアする事ができるかを評価する。 本来、シナリオに対して必要なロールは一意に紐付けられるはずが、様々なシナリオをクリアしてきた結果、 シナリオを満たす事ができないAggregateのインターフェースも眺める事になる。こういう状況では、ドメインモデルの評価が完了するにはいくらエキスパートといえどそれなりに時間がかかってしまうのではないだろうか?
この状況を打破すべくシナリオをドメインモデル(Context)に、AggregateのインターフェースをRoleに昇格させて、よりメンタルモデルに近付け、評価すべきドメインモデルをより限定的に、ドメインの理解可読性を向上させる為に、DCIを取り入れるのは良いアプローチかなーと考えている。
DDDとDCI併用しろとかそういう話ではないのだけれど、上記のような事を考えていると、DDDに加えてさらにDCIを適用しなければならない状況に陥るのは、そんなにないと思う。そもそもDDDを実践する事自体、複雑な問題領域でなければやらない。幸にも自分は今ゲーム開発に携わっているのでその点困ってはいないが、他の業種ではそうでもないかもしれない。それに加えて、蒸留によってコアドメインからサブドメインを定義し分離するアプローチをとる事もあり得る。さらに言うと、現実解としてDCIを選択・採用するというのは、現在のプログラマの知識レベルから考えるに難しいと思う。僕自身、設計に正解は無いとはいえど、DDDについてはゴールイメージがある程度想像できるようになり、今の仕事でも実践し、人に伝えられる(うまく伝わっているかはまだ定かではない)ようには理解が進んでいるけれど、DCIについてはまだゴールイメージが湧いていない。
DCIとServiceとドメイン
そんな考えを張り巡らせていると@j5ik2o氏からこんな回答を頂いた。とてもありがたい。
ボクは、DDDでいうドメインサービスやアプリケーションサービスの中には、エンティティや値オブジェクトに、本来あるべき役割が埋没している可能性があるのではないか、と思っています。 たとえば、口座間のお金の転送だって、口座間転送サービスとせずに、口座間転送という場面において、口座はそれぞれ送金元、送金先という役割を持っているのでは、と思うんですよね。ただ、場面を意識せずに、口座エンティティにそのまま振る舞いを配置してしまうと不自然になる(DDDだとこの時点でサービスにいく。これは悪いというわけではなく現実解だと思ってる)んだけど、場面を意識して適切なロールをドメインオブジェクトに合成すれば、メンタルモデルに合っているかもしれないと思うようになりました。これはEricのユビキタス言語パターンにも反しないはずです。
加えて、年明けにDCIとServiceを巡る記事が投稿された。素晴らしい。必読だと思う。
- Ruby/Rails 用 DI コンテナ Dee をつくった、あるいは Ruby のカルチャーについて
- DCI なんて面倒なだけで Service 使えばいい』への返答
- DCI/Serviceをめぐる話題
- ServiceとDCIについて
僕自身の考えは概ね@j5ik2o氏に同じなので違う視点で話す。
ドメインという言葉自体が曖昧なので大きく2つに分けると、アプリケーションの問題領域を指すアプリケーションドメインと、アプリケーションを実現する基盤となるソリューションドメインがある。ServiceだのDCIだの言われているけれど、なぜここに若干の亀裂があるのかと考えると、 アプリケーションドメインに対するアプローチとして、アプリケーションドメインそのものを中心に捉えているか、ソリューションドメインに偏って捉えているか、という思想の食い違いにあるのではないかなと思う。
DDDで語られるドメインは概ねアプリケーションドメインを指しており、そのアプローチは、アプリケーションドメインそのものに着目し、ドメインの知識を噛み砕いてユビキタス言語を構築し、会話し、それをドメインモデルとし、複雑な問題をドメインモデルが解決する、というように、アプリケーションドメインを中心に捉えていると理解している。
反対に、ソリューションドメインに偏って捉えるというのはどうことかというと、実現する言語、フレームワーク、ライブラリ等、解決手段を中心に問題の解決を図るということである。偏った、とかいう言い方をするのは決して否定しているわけではなく、僕自身、経験則とか現実解として、これはサブドメイン/ソリューションドメインとして切り出せるだろうな、というものは早期に切り出してモジュール/ライブラリ化することもある。ただし、目的はあくまでもアプリケーションのコアドメインを隔離する為である。
本来アプリケーションドメインとソリューションドメインは一致していることが望ましい。モジュール/ライブラリはアプリケーションそのものではない。目的が本来解決すべきアプリケーションドメインから遠ざかってしまう。よくプログラマが脱線してコード書きまくるのは大体新たな問題を発見してしまったからだよね、という余談はさておき、何が言いたいかというと、ソリューションドメインに偏ったアプローチはアプリケーションが本来抱えている問題を見失う可能性が高く、その回避策として、気をつけなきゃならない事(改善プロセスetc…)が増えたり等、結果的に仕事を増やしているだけかもしれないので、一旦立ち止まって、今携わっているアプリケーションを振り返ってみて欲しい。
Serviceという単語が曖昧だという話
世間一般的に語られているServiceは、純粋人工物としてのServiceだったり、PoEAAのService(Layer)だったりする。これら2つにも更に分類できて、利用者への操作セットを提供する(Domain|Application|etc…)FacadeとしてのServiceと、アプリケーションロジックを実装するOperationScriptとしてのServiceがある。
ServiceがDDDで語られるServiceであるとするならば、それは会話の中で生まれたアプリケーションドメインの知識の1つなのだけれど、それ以外だとするならば、アプリケーションドメインの知識であるとは限らない。例えば、フレームワークとしてたまたまsuffixにServiceとついているだけとか、アノテーションとして付いているだけで、そのオブジェクトの責務が一意に予測できない。
仮に目的が同じだとしても、結果としてプログラマがコードから受け取る印象はまちまちであり、DDD的にはこういうのを「コンテキスト境界」を超えている、なんて言ったりするのだけれど、それはさておき、Serviceというワードは曖昧で、UtilsとかManagerとかcheck*とか、かつて抽象的だと嫌ってきたものと同等なので、有識者の方々にはServiceという単語を使うときはもう少し気を遣ってもらいたいなと思う。
上記内容を理解するためにオススメな本
-
- DDDのServiceはこの本に記載されているGRASPパターン(責務駆動の原理原則に相当する)を継承している。
- 分厚い本なので振り回さないようにするか電子化しましょう。
-
- ServiceLayerを改めて振り返るなら是非。
-
- ソリューションドメインって何?なったらどうぞ。
- この本で語られるドメインは半分くらいソリューションドメイン。
-
- どうぞ。
年明けからソフトウェア設計の話で盛り上がるなんて
2014年は楽しい年になりそうですね。