DDDを意識した際のpackage構成
概要
DDDを勉強した際に増田さんやlittle_handsさん、nrsさんのサイトをよく拝見させて頂いているのですが、実際に自分でDDDで取り入れた際にpackage構成をどうした方がいいかというのを考えることが多いので備忘録と考えの整理の為に残します
DDDに関しての自分のメモを元に書いています
アーキテクチャ
isolating-the-domainのアーキテクチャが個人的にはしっくりきています
外部との接地面はPresentation層とInfrastructure層のみでApplication層やDomain層は自分たちの関心毎に集中しやすくなっています
package構成
root
├── application
│ ├── service(ApplicationService)
│ └── sharedservice(サービス間で使いたいサービス)
├── domain
│ ├── model(値オブジェクトなどを格納したオブジェクト)
│ ├── repository(インターフェース)
│ ├── service(DomainService)
│ └── value(値オブジェクトや区分オブジェクト)
├── infrastructure(repositoryの実態)
│ ├── composite(複数のdatasourceやexternalapiの結果を結合したりする場合に使用)
│ ├── datasource(DBなど)
│ ├── externalapi(外部APIなど)
│ └── transfer(外部ストレージなど)
└── presentation
├── controller
├── secutiry(認証関連やCSRFトークンなどの処理)
└── interceptor(コントローラー共通処理)
Application層のServiceとDomain層のServiceの違い
ApplicationService:ユースケース(プロジェクトの作成、参照、更新、削除)
DomainService:ドメインオブジェクトに関する操作だがドメインオブジェクト内にはおけない物(重複確認など)
DomainServiceを使う機会は滅多にないです
ボトムアップドメイン駆動設計での説明がわかりやすかったです
ドメインオブジェクトの定義
model
値オブジェクトなどを格納したオブジェクト(Entity)
value
値オブジェクトや区分オブジェクト(ValueObject)
collection
modelやvalueなどを束ねたオブジェクト(Listなどのwrapper
置き場所は元のオブジェクトと同階層に置くのでmodelだったりvalueだったりする
名称は元のオブジェクトの複数形にする(またはsuffixにCollectionとつけるなど
参考:DDD基礎解説:Entity、ValueObjectってなんなんだ
外部オブジェクトとの変換はDTOに任せる
DBから取得した値を直接modelやvalueに変換するとやりづらいことが多かった
変換用のDTOを用意してドメインオブジェクトへの変換を任せることで特に外部APIのレスポンスの形式などを意識しなくていい
各DTOの名称は下記のように用途毎に分ける
リクエスト関連:Form(Web)、Request(API)
レスポンス関連(JSON):Response、View
DBや外部API:Entity
APIの場合はOASによるgeneratorを活用すればリクエスト関連、レスポンス関連のオブジェクトは自動生成することができる
generatorでオブジェクトを作成した場合にはControllerと同階層にMapperを用意し、ドメインオブジェクトへの変換を行うようにする
packageをどこまで切るべきか
Application層のserviceやsharedservice、Domain層のrepositoryやserviceに関してはpackage直下にファイルを作成してしまうで良さそう
上記以外の物に関しては用途毎のpackageを切った方がいい
例えばdatasourceなどの場合だと実際にアクセスを行うMapperやDTO、Mapperを呼び出すクラス(repositoryのインターフェースを継承)などを置くことになる為、packageを切らないとごちゃごちゃしてしまう
Serviceなどに関してはServiceからしか呼ばないものなどはないため、packageを切る必要性が低い
Spring BootとMybatisを利用する場合には下記のような設定を入れることでXMLファイルに関しても同package内で管理することができ便利である(サンプルはGradleを使用
sourceSets {
main {
// mybatis SQL map XMLファイルをjava以下にも設置できるようにする
resources.srcDir 'src/main/java'
}
}
雑感
package構成やドメインオブジェクトなどは運用をしていく上で手を加えていくべきものである為、まだ変更のしやすさを実感できているわけではないので今後どうなるかは気になるところです
運用等をしていった結果変わった点などはまた残していけたらと思います
Discussion
パッケージ構成に非常に悩んでいたので大変参考になりました!
ひとつ質問なのですがDTOをdatasourceに置くとアプリケーション層がインフラ層に依存することになりませんか?
依存の方向を守るためにはアプリケーション層に配置するのがいいような気がするのですが、違和感があるので今はroot直下にDTOを置いています。何かアドバイスいただけると大変助かります。
コメントありがとうございます!
外部オブジェクトとの変換はdtoに任せるに書かせて頂いていますがInfrastructure層から返す際にはDomain層のドメインオブジェクトに変換を行ってから返すのでApplication層が依存するのはDomain層になります
イメージとしては下記のように一度Entityにマッピングしてからドメインオブジェクトに変換するとSQL側でドメインオブジェクトに合わせたりすることをしなくていいので使いやすく感じています
MyBatisを使う場合ですとresultMapとかTypeHandlerを駆使すれば直接ドメインオブジェクトにマッピングすることもできますが個人的にはXMLファイル側で頑張るよりはEntityに格納しちゃう方が楽に感じています
見落としていました。
詳しくありがとうございます!