model
値オブジェクトなどを格納したオブジェクト(Entity)
staticファクトリメソッドを用意する場合はcreateなどを使用する
value
値オブジェクトや区分オブジェクト(ValueObject)
staticファクトリメソッドを用意する場合はof、fromなどを使用する
例外
コレクションオブジェクトはvalueと同様のオブジェクトではあるが使用方法を踏まえ元のオブジェクトと同パッケージ内に置く
staticファクトリメソッドを用意する場合はof、fromなどを使用する
名称はsuffixにCollectionとつける(複数形では他の単語とかぶる場合がある為。例:years)
DDD基礎解説:Entity、ValueObjectってなんなんだ
ドメインオブジェクトではオブジェクト内の値はイミュータブルな値となっている為、セッターは用意しない
上記を踏まえゲッター、セッターの見分けをつける為のprefixは不要としgetなどは省略するように統一する
@Accessors(fluent = true)
@Getter
model
model内の変数でnullが許容されている場合は返り値でnullをそのまま返す(Optionalは使用しない
またintやbooleanなどはnullを許容する場合のみInteger型、Boolean型を使用する
value
nullを許容しない
値オブジェクトや区分オブジェクトを直接返すのではなくそのmodelとしてどういう値を扱いたいのかを返すようにする
オブジェクト内の値を返したい場合は値オブジェクト内の値をmodelのメソッドから直接取得できるようにする
オブジェクト自体を取得したい場合はextractXXXなどの抽出用メソッドを用意する
MyBatisなどでは変数を直接参照できる為、オブジェクト自体を取得したいケースは限られていると思われる
value
├── discount
│ ├── MallCouponDiscount
│ └── XxxDiscount
├── identifier
│ ├── ApplicationId
│ ├── Id
│ └── Guid
├── name
│ ├── ItemName
│ └── UserName
└── price
└── PurchasePrice
商品(item)に関する型などのパッケージをどう切るかは検討中
itemパッケージとして切るべきかItemNameはnameパッケージ内に入れるべきかなど
パッケージを適切に切ればprivateをつける必要性が低い為、使用しない
package privateにすることでテストでのspy化なども可能になる
パッケージの依存はimportで気付けるのでclassのアクセス修飾子もpublicに統一する
methodのアクセス修飾子はpublicを基本とし、privateと同様に扱いたいメソッドはpackage privateにする
protectedもあまり必要なケースが浮かばないので極力使わない
まとめ
クラス: public
クラス変数: package private
インスタンス変数: package private
メソッド
クラス外から参照される場合: public
クラス内から参照される場合: package private
基本@RequiredArgsConstructorを使用する
理由は@Valueとの干渉を防ぐ為
DTOなどオブジェクトマッピングを考慮したオブジェクトは@AllArgsConstructorを使用する
MyBatisを利用している場合はコンストラクタが優先されるようなのでEntityは@RequiredArgsConstructorを使用するで良さそう
ApplicationService: ユースケース(プロジェクトの作成、参照、更新、削除)
DomainService: ドメインオブジェクトに関する操作だがドメインオブジェクト内にはおけない物(プロジェクトの重複確認)
ProjectQueryApplicationService: プロジェクトの参照に関するユースケース
ProjectRecordApplicationService: プロジェクトの記録に関するユースケース
Sharedserviceに関してはMacchinettaのServiceクラスとSharedServiceクラスを分ける理由についてを参考にしている
SharedServiceはServiceから呼ぶものなのでプレゼンテーション層から直接は呼び出さないようにする
APIの場合はOASによるgeneratorを活用すればRequestやResponseのオブジェクトは自動生成することができる
generatorでオブジェクトを作成した場合にはControllerと同階層にMapperを用意し、ドメインオブジェクトの生成を行うようにする
Web: Form
API: Request、Response
DB: Entity
compositeの中の実態はdatasourceやexternalapiからデータを取得しマッシュアップしてドメインオブジェクトに変換し返す
この時に呼ぶdatasourceやexternalapiのオブジェクトはcompositeから参照できるようにする(要検討
依存関係は一方通行とする
新卒にも伝わるドメイン駆動設計のアーキテクチャ説明[DDD]
プレゼンテーション層: アプリケーション層、ドメイン層
アプリケーション層: ドメイン層
インフラストラクチャ層: ドメイン層
ドメイン層: 依存しない
アプリケーション全体のExceptionHandlerを定義し
パッケージ単位でもExceptionHandlerを定義したい場合はOrderアノテーションを利用する
パッケージ単位で作成したExceptionHandlerの優先度を高くする(デフォルトの優先度は最低な為
どうしてもサーバーサイドでのチェックが必要なものは登録に関するApplicationService内でエラーチェックを行い
エラーがあればValidationErrorExceptionなどの例外を投げ、それをコントローラーでキャッチして出し分けを行う
オニオンアーキテクチャなどを参考に構築
src/main/java/jp/moriopg/dddsample
├── application
│ ├── service(ApplicationService)
│ └── sharedservice(サービス間で使いたいサービス)
├── domain
│ ├── model
│ ├── repository(interface)
│ ├── service(DomainService)
│ └── value
├── infrastructure(repositoryの実態)
│ ├── datasource(DBなど)
│ ├── externalapi(外部APIなど)
│ ├── composite(複数のdatasourceやexternalapiの結果を結合したりする場合に使用)
│ └── transfer(外部ストレージなど)
└── presentation
├── controller
├── secutiry
└── interceptor(コントローラー共通処理)