読者です 読者をやめる 読者になる 読者になる

実践DDDのサンプルプロジェクトが学びしかない

IDDDを読んで、それなりに書いてあることは分かり始めたかな。と思ってたけど。
いざサンプルプロジェクトを読んでみたら、全然そんなことなかった。(ノД`)シクシク

github.com

いつものように、自分メモ。

プロジェクトの構成

全部で3プロジェクトと1ライブラリがある。

iddd_agilepm

  • データストアとしてKVS(LevelDB)を使用。
  • DIコンテナは使ってない。

iddd_collaboration

Event Sourcing と CQRS。ORM使わずにやってみた。

例をシンプルにするために、Event Sourcedな書き込みモデルと、CQRSの読み込みモデルを1スレッドで実行してる。イベントジャーナルとしてLevelDBを、リードモデル用にMySQLを使ってるのでほんのちょっとだけ一貫性がない状態が発生する可能性がある。別々のデータストアを使って、でも、できるだけ一貫性が保てるようにしてみた。

iddd_identityaccess

iddd_common

共通のコードを保持。このやり方は推奨はしないけど、サンプル用には別にいいかな。

テスト

Dockerで環境構築してgradleでテスト実行できるよ。

プロジェクトまとめ

  • データ周り
    • LevelDB、MySQL
    • ORM使ってたり、JDBCMySQLにアクセスしてたり、KVSからエンティティ読み込んだりしてる。
  • コンテナ
    • DI使ってるのと、使わずにやってるのがある。
  • アーキテクチャ
    • イベントソーシング、CQRS、イベントストア
  • ドメインイベント
    • RESTとRabbitMQ

iddd_agilepmを読む

パッケージングがこんな感じ。port/adapterはヘキサゴナルアーキテクチャか。

  • application
  • domain/model
  • port/adapter

applicationパッケージ

  • サブパッケージ
    • サービス単位でサブパッケージが切られてるっぽい。
    • その中にサービスクラスが一つと、各メソッド用のコマンドクラスが複数ある。
  • ApplicationServiceLifeCycleってなんぞ?
    • サービス呼び出し時にbeginされて、終わったらsuccess/failしてる。
    • UnitOfWorkとかの管理してる。
    • DIコンテナ使ってないから、Aspectとかで織り込めないってことか。
    • そういえば大体コンストラクターリポジトリとか渡してる。
  • アプリケーションサービスの入力パラメータ
    • 更新系はCommandオブジェクトを受け取ってる。
    • って、このプロジェクトには更新系のサービスしかないや。
    • Commandオブジェクトに必要な情報を詰め込んで渡してるんだね。
  • 戻り値
    • 書き込み系なので基本的にはvoidを返す。
    • もしくは、生成されたエンティティのIDだけを返す。
    • でも、値オブジェクトじゃなくて文字列を返してる。
  • Commandクラス
    • プロパティは、値オブジェクトじゃなくて、Stringとか基本的な型だな。
    • CommandクラスはDTOみたいな感じだな。パラメータあり/なしのコンストラクターがあって、Getter/Setterがある。
  • リポジトリ
    • アプリケーションサービスはリポジトリをフィールドで持ってるんだけど、それをサービス内で使うときもgetterメソッド経由で使ってる。なんでかな?好みかな?

domain/model パッケージ

  • サブパッケージ
    • 集約毎ってわけでもなさそうだな。ドメイン毎って感じか。
    • その直下に、エンティティ、値オブジェクト、ドメインイベント、リポジトリ全部入ってる。
    • あと、変更トラッカーも。
  • モデル
    • モデルはSetterに入力値のアサーションがついてる。notNullとかnotEmptyとかlengthチェックとか。 − コンストラクターでフィールドに値を入れたい場合でも、Setter使ってる。(本に書いてたね)
    • アプリケーションサービスと違って、モデルは値オブジェクト使いまくるね。型安全。
  • 生成時のバリデーション
    • Productの生成のときにどれくらい入力値チェックしてるのかなー?と思ってみてみたけど、アプリケーションサービスでもモデルでもtenantが存在してるかどうかとかはチェックしてないな。まぁ、tenantがなかったら見えないプロダクトになるだけだから別に悪影響はないか。
    • 関連は基本的にモデルではチェックしない、ってことなのかな。

ports/adapter パッケージ

  • persistence パッケージにリポジトリの実装が入ってる
    • リポジトリからエンティティをどうやって生成してるのかなーって思ったらLevelDBから取得した文字列からDeserializeしてるだけだった。
  • messaging パッケージにRabbitMQ周りの実装が入ってる
    • キューに対して、アプリケーションサービスを呼び出してるだけだね。

iddd_collaboration

  • だいたいパッケージ構成は同じ。
  • 腐敗防止層の実装がport/adapterパッケージにある。
  • イベントソーシングだから、リポジトリの実装とエンティティのwhenメソッドが面白い。
  • 変更イベントをMySQLに適用するために、Dispatchしてて面白い。
  • アプリケーションサービスが、CQRSのクエリに対して、SQLを直接MySQLに発行してて面白い。
  • クエリの結果を返すクラスの名前はAbcDataみたいにDataサフィックスがついてる。

iddd_identityaccess

RESTインターフェイスがあるくらいかな。

面白かったーーーー!!!学びしかない。