【DDD】「メタデータがドメインモデルに含まれないのは当然」??
はじめに
こんにちは、BlueZzです!
最近私は業務でオニオンアーキテクチャ x DDDを用いたプロダクトのバックエンド・フロントエンドの開発に携わっております。
そのプロジェクトに入るまでDDDの知識が浅かったのでネットの記事を漁ったり本読んだり自分で実装したりと学習していたのですが、そんな中で先日こちらの記事を拝見しました。
こちらの記事では「OSS化された某社内フレームワーク」について、
- traitを用いて全てのエンティティにid, createdAt, updatedAtを含めている
- ドメインモデルがDBの受け皿と化している
- DDD意識したと言いつつこれってDDDやってないよね、永続化層の都合にドメインモデルが引っ張られてるから
といったことが述べられております。
こちらの記事の内容は賛同できる部分が大いにあるのですが、
おそらく「OSS化された某社内フレームワークがいかにク⚪︎か」という部分を強調したいがために断定しすぎてる部分があるというか、
特に、 「メタデータがドメインモデルに含まれないのは当然」 という記述については議論の余地があるんではないかと思ったので、今回は主に
ドメイン層におけるメタデータの扱い
について短くまとめ、
そのほかにも記事を読んで思ったことについても書いていければと思います。
ちなみに私は関数型DDDなるものには一切知見がないため、
いわゆるDDD全般として言えることについて述べていきます。
そしてこの記事で言及されていた
OSS化された某社内フレームワークについては
どういうフレームワークなのかとかどう言った経緯でOSS化されたのかなど
コンテキストを全く知らないので言及しませんので悪しからず。
というか2019年3月時点だと僕プログラミング始めてすらないですね🤔
メタデータがドメインモデルに含まれないのは当然、なのか?
軽量DDDを除いたDDDでは解決したいドメインを抽象化することでモデリングして実装に落とすということをやると思います。
その過程でメタデータがドメインモデルを表現するものではない場合実装には含めないという選択を取ることが一般的かと思います。
ですので メタデータがドメインモデルに含まれないのは当然 という主張は百理あると思います。
また拝見させていただいた記事にも記載されている様に
脳死で全てのドメインモデルにメタデータをぶち込むのはちょっとどうかと思いますね🈲
しかし逆に ドメインの知識としてメタデータが重要ならドメインモデルに含めても良い のではないかと考えることもできると思います。
ドメイン知識としてメタデータが重要なものとしての具体的な例があまり思いつかないんですが、
例えばXとかのSNSの投稿のドメインモデルを考えてみると割とcreatedAtとかupdatedAtってドメイン知識として重要な場面もあるんではないかと思います。(最近の投稿みたいな感じで出したいとかのロジックってよくある気がする)
結局は、
- インフラ都合でドメイン層が引っ張られない
- インフラ都合のメタデータをエンティティが持たない
- ユビキタス言語(業務用語)に出てこないものをドメイン層に含めない
という点が守られていれば必ずしもメタデータをドメイン層に含めるのがダメというわけではない気がしました。
ただしcreatedAtなどの日付がエンティティ内に含まれる場合は、日付の設定タイミングについて考慮する必要があります。
エンティティの実装クラス内で日付生成のライブラリへの依存を許容し、エンティティが作成されたタイミングや更新されたタイミングをそれぞれcreatedAtやupdatedAtにすることにした場合は良いのですが、
DBへ保存するタイミングでのcreatedAtやupdatedAtをドメインの知識として持たせたい場合は、
インスタンス化したエンティティがrepositoryに渡って日付を付与されるまでcreatedAtやupdatedAtが初期化されていないことになります。
これはインスタンス化された時点で完全な状態が保証される完全コンストラクタパターンに反することになります。
エンティティが作成されたタイミングかDBに保存されるタイミングかを気にしてドメイン層の実装を行うことなんてまずないとは思うんですが、
先ほど例示した後者の場合は、日付をドメインオブジェクトに付与するためのミューテーションメソッドを別で定義してあげる必要がありますね。
ドメインの知識としてメタデータが重要じゃない場合のメタデータの扱い
ドメインの知識としてメタデータが重要じゃない場合は、
ドメインオブジェクトにメタデータを含めない方が良いと思います。
メタデータを表現する値オブジェクトは作らず、それらを含むエンティティを作らないということです。
ドメインの知識として重要じゃないけど画面表示などなんらかのユースケースに応じてメタデータが必要なら、DTOなどを用いて詰め替えて返却するのが良いと思います。
またcreatedAtやupdatedAtなどに関しては、
永続化するタイミングでインスタンス化したドメインモデルには含めない形で保存するのが良いと思います。
prismaでユーザーを新規作成することを想定したコード例を示すと以下の様にかけるのではないでしょうか。
// userエンティティのインスタンスを受け取ったとする
await prisma.user.create({
data: {
id: user.id.value,
name: user.name.value,
email: user.email.value,
// DB都合のメタデータをここで付与
created_at: new Date(),
updated_at: new Date(),
},
});
IDをエンティティに含めるかどうか
※ 関数型DDD界隈について筆者は一切の知見を持ち合わせていません。
これに関しては、
「エンティティは一意性をIDで表現する」ため、
IDはエンティティに入れて良いと思います。
逆にIDをエンティティに含めないで一意性を表現する方法を私は知らないのであればむしろ教えて欲しいです。
ちなみにIDは他のIDと異なっている必要があるので必然的にUUIDとかを選ぶことになるかと思います。
オートインクリメントのIDを値オブジェクトとしてエンティティに持たせたい場合、
DBに永続化する前のエンティティ生成段階で持たせようとするとこれまた値オブジェクトをインスタンス化した時点でIDがない不完全なオブジェクトになりうるので、
DBからオートインクリメントの値を取得してそれをエンティティのインスタンス化の段階で渡すことになるかと思います。
最後に
今回はDDDを学ぶ中で目に留まった記事の内容について筆者の考えを織り交ぜながら意見を述べました。
拝見させていただいた記事の内容に関しては一部私とは意見の相違がありましたが、
おかげで関数型DDD界隈や違う流派の存在を認知することができましたし
設計ってめっちゃ奥が深いし考えることが多いしだからこそ面白いなと改めて思いました。
今後も自分の学びの中で周りに還元できるものがあればどんどん記事としてアウトプットしていきたいと思います!
Discussion