Skip to content

Instantly share code, notes, and snippets.

@morio-pg
Last active April 24, 2023 16:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save morio-pg/cab2fc4e9e01b3c398f389b64a1fc3be to your computer and use it in GitHub Desktop.
Save morio-pg/cab2fc4e9e01b3c398f389b64a1fc3be to your computer and use it in GitHub Desktop.

DDDに関しての自分のメモ

ドメインオブジェクトに関しては用途毎にmodelとvalueとに分ける

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

ドメインオブジェクトでのnullの扱い

model
 model内の変数でnullが許容されている場合は返り値でnullをそのまま返す(Optionalは使用しない
 またintやbooleanなどはnullを許容する場合のみInteger型、Boolean型を使用する
value
 nullを許容しない

modelでの値オブジェクトや区分オブジェクトの返し方

値オブジェクトや区分オブジェクトを直接返すのではなくそのmodelとしてどういう値を扱いたいのかを返すようにする
オブジェクト内の値を返したい場合は値オブジェクト内の値をmodelのメソッドから直接取得できるようにする
オブジェクト自体を取得したい場合はextractXXXなどの抽出用メソッドを用意する
MyBatisなどでは変数を直接参照できる為、オブジェクト自体を取得したいケースは限られていると思われる

valueのパッケージ構成は種別毎に切る

value
├── discount
│   ├── MallCouponDiscount
│   └── XxxDiscount
├── identifier
│   ├── ApplicationId
│   ├── Id
│   └── Guid
├── name
│   ├── ItemName
│   └── UserName
└── price
    └── PurchasePrice
商品(item)に関する型などのパッケージをどう切るかは検討中
itemパッケージとして切るべきかItemNameはnameパッケージ内に入れるべきかなど

アクセス修飾子はpublicかpackage privateのみを使用する

パッケージを適切に切ればprivateをつける必要性が低い為、使用しない
package privateにすることでテストでのspy化なども可能になる

パッケージの依存はimportで気付けるのでclassのアクセス修飾子もpublicに統一する
methodのアクセス修飾子はpublicを基本とし、privateと同様に扱いたいメソッドはpackage privateにする

protectedもあまり必要なケースが浮かばないので極力使わない

まとめ

クラス: public
クラス変数: package private
インスタンス変数: package private
メソッド
 クラス外から参照される場合: public
 クラス内から参照される場合: package private

DI関連のアノテーション

基本@RequiredArgsConstructorを使用する
理由は@Valueとの干渉を防ぐ為
DTOなどオブジェクトマッピングを考慮したオブジェクトは@AllArgsConstructorを使用する
MyBatisを利用している場合はコンストラクタが優先されるようなのでEntityは@RequiredArgsConstructorを使用するで良さそう

ApplicationServiceとDomainServiceの違い

ApplicationService: ユースケース(プロジェクトの作成、参照、更新、削除)
DomainService: ドメインオブジェクトに関する操作だがドメインオブジェクト内にはおけない物(プロジェクトの重複確認)

ApplicationServiceは肥大化しやすい為、肥大化した場合は参照系、記録系などに分割していく

ProjectQueryApplicationService: プロジェクトの参照に関するユースケース
ProjectRecordApplicationService: プロジェクトの記録に関するユースケース

複数のServiceで利用されるユースケースはSharedServiceとして使い回せるようにする

Sharedserviceに関してはMacchinettaのServiceクラスとSharedServiceクラスを分ける理由についてを参考にしている

SharedServiceはServiceから呼ぶものなのでプレゼンテーション層から直接は呼び出さないようにする

外部オブジェクトとの変換はMapperに任せる

APIの場合はOASによるgeneratorを活用すればRequestやResponseのオブジェクトは自動生成することができる

generatorでオブジェクトを作成した場合にはControllerと同階層にMapperを用意し、ドメインオブジェクトの生成を行うようにする

Web: Form
API: Request、Response
DB: Entity

compositeの使い方

compositeの中の実態はdatasourceやexternalapiからデータを取得しマッシュアップしてドメインオブジェクトに変換し返す
この時に呼ぶdatasourceやexternalapiのオブジェクトはcompositeから参照できるようにする(要検討

パッケージの依存に関して

依存関係は一方通行とする

パッケージ間の循環依存を自動で検出する

新卒にも伝わるドメイン駆動設計のアーキテクチャ説明[DDD]

プレゼンテーション層: アプリケーション層、ドメイン層
アプリケーション層: ドメイン層
インフラストラクチャ層: ドメイン層
ドメイン層: 依存しない

ExceptionHandlerを用意する場合

アプリケーション全体のExceptionHandlerを定義し
パッケージ単位でもExceptionHandlerを定義したい場合はOrderアノテーションを利用する
パッケージ単位で作成したExceptionHandlerの優先度を高くする(デフォルトの優先度は最低な為

Annotation Type Order

ユーザー向けのバリデーションはJavaScriptやHTML5に任せる

どうしてもサーバーサイドでのチェックが必要なものは登録に関する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(コントローラー共通処理)

参考資料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment