関ジャバのDDD勉強会で紹介されていた isolating the domain を読んでみました。
紹介のスライド https://speakerdeck.com/haljik/isolating-the-domainfalseshao-jie
プロジェクト https://github.com/system-sekkei/isolating-the-domain
ちょうど直近のプロジェクトでDDDに沿った設計を導入することになったので、その参考にしたい、という意図です。
実際にDDDを導入しようとすると、各層のディレクトリ名の付け方でチームメンバーの意見が一致しなかったりするので、そういうところはパクっていくのがいいかなと考えてます :)
プロジェクトの概要
ユーザ情報のCRUD操作機能を提供しているWebアプリケーション。
Java + Spring MVC + MyBatis。
DDDのプロジェクトの雛形として利用されている。
手元ですぐ実行できるスクリプトが付いている。
設計指針は Wikiにまとめられてる。 https://github.com/system-sekkei/isolating-the-domain/wiki
プロジェクトの構造
プロジェクトは 3層(プレゼンテーション層、アプリケーション層、データソース層) + ドメインモデル で構成されています。
ドメインモデル
- 業務上の判断/加工/計算ロジックを表現したドメインオブジェクトの集合。ドメインオブジェクトにはEntity, Value Objectなどが含まれる。
- 記録と通知の関心事も、ドメインモデルの一部としてインタフェース宣言する
プレゼンテーション層
- ユーザからの入力受付と出力
- 入力を元にしたドメインモデルの生成(バインディング)、バリデーションの実行もここでやる。
アプリケーション層
- プレゼンテーション層からの依頼に基づき、ドメインオブジェクトを使った操作をする。
データソース層
- 記録/参照の実装
- 外部との送受信の実装
いかにしてドメインを隔離するか?
DDDでは、ドメインモデルにはドメイン(業務の関心事)のみを記述し、実装技術は隔離することが重要です。isolating the domainでは以下のテクニックを使ってそれを実現しています。
- (業務上の判断/加工/計算ロジックに加えて) 記録と通知の関心事も、ドメインモデルの一部としてインタフェース宣言する
- ドメインモデルで定義したインタフェースをデータソース層で実装する。
- データソース層のクラスは、他のレイヤから直接呼び出さない。
その他
以下、各層の実装方法について、個人的に気になったところを挙げます。
ドメインオブジェクトがimmutableになってない
- バリューオブジェクトも含め、ドメインオブジェクトのフィールドにprivateが設定されていません。
- 同じパッケージのクラスからは値をセットできてしまう。真にimmutableではない。
- private修飾子をつけてもDirect Field Accessできてしまうため、どうせ真にimmutableではない。なのでprivateつけてもつけなくてもいい、むしろ余計な情報がコードに増えるのでつけないほうがいい、という割り切った設計判断があるのかな?
かならずアプリケーションサービスを通してドメインモデルを操作する
プレゼンテーション層から直接リポジトリを操作せず、アプリケーションサービスを経由して操作しています。
プレゼンテーション層から直接ドメインモデルの操作をすべきでない、ということを最近考えていたので、
やっぱりそのように実装されてるなー、と追認できました。
ドメインオブジェクトへの値のセットは、O-Rマッパー、Web Data Bindingにまかせている
- コンストラクタで値をセットするのでなく、ダイレクトフィールドアクセスを使って値をセットしている
- O-Rマッパー、Web Data Bindingが作るオブジェクトをDTOとして扱わず、ドメインオブジェクト自体を作っている。
- ドメインオブジェクトに値の設定方法に関するコードを含めなくて良いのはメリット。
- DTOをコードに含めなくていいのはメリット。
- O-Rマッパー、Web Data Bindingがないと、ドメインオブジェクトへの値のセットができないのはデメリット。(リフレクションを使って実現するしかない。)そういうテストのときに困るので、privateにせずに同じパッケージからなら値をセットできるようにしてるんだろうか?
Application Serviceでトランザクション制御してない
トランザクション制御はアプリケーション層の責任だと思ってたので、ここにないのが意外でした。
MyBatisでのトランザクション制御方法をしらないので、どこでトランザクション制御してるか不明。
オブジェクトをフィールドにセットするのにSpringのDIの機能を活用
アプリケーション層で利用するリポジトリクラスの実装や、プレゼンテーション層で利用するアプリケーションサービスをインスタンス化するのに、DI機能を利用しています。
個人的に、DI機能使ってるとどうやってオブジェクトが作られるかわからなくなってしまうので、あまり使いたくないんですよね。なので、へえ、という感じでした。
感想
DDDのプロジェクトでの、各レイヤのクラスの置き場所や記述内容の具体的なサンプルとして、大変参考になりました。
ValueObjectが真のImmutableとして実装されてないという点は、かなり意外でした。このあたりはImmutableにこだわるより、ドメインオブジェクトにとって重要なことのみを集中させるために、オブジェクト生成に関わるコードはできるだけ含めないポリシーなのかなと思いました。ある種の割り切った設計ですね。
また、すぐ動かせるサンプルがあるというのは、いいなーと思いました。
メンバーが参加するときに知識の共有をするのに非常に便利そう。
できれば自分たちでもこういうプロジェクトの雛形を作りたいですね。