DDD面白そうなネタがあったので、自分なりの考えをまとめてみる。カルチャーについてこの記事はRuby用のDIコンテナの話題なんですが、DCIについても言及されているようです。比較軸はDIそのものというより、サービスとDCIだと思うので、それについてダラダラといくつか考えをまとめてみます。返事になるようでならないかも。それと宗教上の都合でDDDの視点から書きます...。サービスという言葉はあいまいまず、簡単に前提の整理から。"サービス"って言葉が何を指すのか結構曖昧です。サービスは簡単にいうと手続きとか振る舞いのことですが、細かくいうと、PofEAAでいうサービスと、DDDいうサービスは、目的が異なります。前者はアプリケーションのためにドメインモデルを再利用可能にするためのものです。後者はドメインの知識を表している振る舞いです。これはのちほど詳しく説明します。このあたりは具体例がないと理解しがたいですが、レイヤーの違いによって責務が異なるという感じです。DDDのサービスの章では、サービスには、アプリケーション層、ドメイン層、インフラストラクチャ層と、複数のレイヤーに存在すると言及されています。アプリケーションサービス)に相当すると思います。あわせて読んで欲しいのはこのあたり。ドメインサービス)に焦点を絞って考えてみます。あと、ここでいうモデルというのはドメインモデルとします。DCIのDataもドメインモデルの前提。事実、オブジェクト指向プログラミングにおける先駆者たちの目的は、エンドユーザのメンタルモデルをコードにおいてとらえることだった。そもそも、オブジェクト指向は、人間のメンタルモデルをシミュレーションするための問題解決手法として登場したので、これは素直に理解できます。KayのDynabook構想とか出てきますね。この記事を読んでもらうとわかりますが、DCIもメンタルモデルに近づくための手法の一つです。BeckはDDDに対する謝辞でこんなことを綴っています。Evansは素晴しい本を警いた。」あと、DHHは、おすすめ書籍のリストの中でこのように述べています。P45)"という手法を利用しますが、メンタルモデルを実装に反映するためにあります。どちらも、オブジェクトはメンタルモデルを写し取るものだというスタンスは一致しているといえます。メンタルモデルを反映するのはドメインモデルなので、やはりメンタルモデルの主戦場はドメイン層だと思います。アプリケーションサービスじゃなくて、ドメインサービスの話を重視するのはこのためです。ドメインサービスは乱用禁物さて、ドメインサービスについて少し掘り下げていきます。ドメインサービスの事例としては、次のような口座間送金サービスの例がよくでてきます。説明はいらないと思います。}}このケースでは、"口座間送金"という概念を二つの口座の相互作用として捉え、口座エンティティ単独の振る舞いとして相応しくないと判断しています(そういう設定)。送金自体を行う振る舞いは、どこにいけばよいのでしょうか。完全に宙に浮いています。いう時にドメインサービスを利用するわけですね(DDDでは)。振る舞いとは関係なさそうなモデルに無理矢理振る舞いを押し付けるのも問題なので、これはこれで現実解です。安易にドメインサービスを使ってしまうと、オブジェクトから振る舞いを奪ってしまい、オブジェクトはただのデータの入れ物になってしまう。いわゆる、ドメインモデル貧血症です。いうわけで、なんでもかんでもドメインサービスはよくない。乱用禁物だと思います。早急にドメインサービスと決めずに、宙に浮いた振る舞いが所属すべきモデルを探すのが原則です。この例ではサービスにしてもさほどデメリットを感じませんね。メソッドもユビキタス言語と対応しているし、その内部の処理もドメインオブジェクトで表現される振る舞いであれば、そんなに違和感ないです。サービスの振る舞いが増えてきたり、振る舞い自体がFATになった場合は要注意だと思います。蛇足ですが、ドメインモデルをデータクラスとサービス(手続き)に完全に分離する"トランザクションスクリプト"という実装パターンもあります。これはオブジェクト指向らしい表現は損なわれますが、よく利用される手法です。オブジェクト指向を重視するDDDとは相性がよくないというか、相容れないので注意が必要です。メンタルモデルを重視する派、重視しない派はこのあたりで分かれます。blogエンティティと値オブジェクトが主役というわけで、ドメイン駆動設計では、主役はエンティティと値オブジェクト。口座間送金の振る舞いが、ドメインサービスではなく、口座エンティティにあった場合を考えてみます。次のようにしてみました。送金元と送金先で振る舞いを分けました。コードみればわかるので説明は割愛。}}モデル(エンティティ)がFATになりますね。サービスとは裏返しの関係になるので仕方ないです。つまるところ、これの善し悪しを決めるのは、メンタルモデルをとらえている、ユビキタス言語の定義がどうなっているかに寄りますね。メンタルモデルに近づくためのDCI前置き長くなりましたが、DCIの話。DCIでは、コンテキスト(モデルを使う場面)に紐づく振る舞いと、そうでない振る舞いがある、という考え方がある(ボクの理解では)。前述した口座エンティティの例では、sendメソッドは送金元口座の役割であり、onReceivedメソッドは送金先口座の役割です。これらの役割は口座間送金という場面でしか使いません。役割にその振る舞いを定義し、口座間送金というコンテキストに入った際に振る舞いを持つ役割が口座エンティティに合成され、コンテキストから離れたら分離されるようにするイメージです。ここで問題なのは、実装方法ですね。Scalaではいくつか実装方法があるのですが、ScalaでのDCIの実装を考えるで示した、暗黙的型変換と型クラスの方式がおすすめです。この例は、先ほどのドメインサービスをDCIで置き換えたものです。}}コンテキストのインターフェイスをみるとわかりますが、ドメインサービスに近いですよね。決定的に違うところがあります。振る舞いがエンティティや値オブジェクトから流出しないという点です。それでいて、FATモデルを解消できる。これは大きな違いですね(このコード例は、DCIでよくみるサンプルとは少し違います。口座間送金という概念は、ドメイン上のコンテキストとしました。トランザクション管理とかどうすんの?って話はアプリケーション層の責務なので、アプリケーションサービスでやるか、それに相当するアプリケーションのコンテキストで行えばよいからです)。コードはドメインサービスの例よりは長くなりましたが、メンタルモデルのよりよい表現に近づけるのではないでしょうか。これをコードが長い、手間とみるか、メンタルモデルに近づいたとみるか、マインドセットの違いで変わってきます。多態を最初に学んだ時もこんな感じだったのではないかな。何やってるか直接的に理解できないけど柔軟性を手に入れるトレードオフをした。メンタルモデルに近づくためです。このトレードオフはDDDに魅力を感じている人なら分かってくれると思いますけどね。あと、ロールをほかの用途でも使えるなら費用対効果がよいですね。エンティティや値オブジェクトがFATになってきたら検討の余地はあると思います。今回の例では、ロールを型クラスで実現したのでそんなに違和感ないですね。言語の機能なんで当然ですが。そもそもロールはアドホックな多相性(継承関係がなくても同じインターフェイスで操作できる)を要求するので、型クラスでそれを実現しただけです。言語によって実装難易度の差はありますが、コンテキスト内で実行時にロールをモデルにmix-inできればそれっぽくなると思います。動的型付けが強い言語は特にやりやすいですね。Javaの例があります。PHPも5.4.0以降で使えるtraitを使えばできそう。Scalaは静的型の整合性を崩さずに動的に振る舞いを合成することができましたが、Javaはフレームワークやライブラリのサポートがないときつそうです(Qi4jが使えそうです)...。いわゆる硬直しない今どきの言語?wならできるのかなと思っています。振る舞いは原則的にエンティティや値オブジェクトに。ドメインサービスは乱用禁物。エンティティや値オブジェクトがFATになってきたらDCIを検討する(その言語において実装が現実的であれば)という方針がよいかなと思っています。Tumblr関連記事2016-03-07混乱しがちなサービスという概念について社内でサービスがよくわからないという話になったので、考察を...2013-12-24ScalaでのDCIの実装を考えるみなさん、こんばんわ。Sourcingを学ぶための教材ServiceとDCIについて月別アーカイブはてなブログをはじめよう!j5ik2oさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。