はじめに
都内でエンジニアをしている者です。最近、初めてドメイン駆動設計(DDD)を用いたプロジェクトを担当しましたが、初めての挑戦だったため、結果的にDB駆動設計になってしまいました。この経験から学んだことを共有したいと思います。
どんな失敗をしたのか
主な失敗は、ドメインオブジェクト(エンティティ)を作成する際に、データベースのテーブルモデルの構造体をそのまま流用してしまったことです。本来、テーブル構造とドメインオブジェクトは独立しており、ユースケースごとにドメインオブジェクトを設計するべきです。しかし、私はDBの構造に引っ張られて、必要以上にDBの設計に依存したオブジェクトを作ってしまいました。
ドメインオブジェクト(エンティティ)の作成方法
どのようにモデリングすればよいのか当初は悩んでいましたが、以下の記事に出会い、非常に納得できました。
この記事の「役割」を基にしたモデリングの考え方が非常にしっくりきました。例えば、「りんご」を売るお店のケースを考えるとします。
境界づけられたコンテキスト
「りんご」という商品は、売り手と買い手で異なる意味を持ちます。売り手側では「売り物」であり、買い手側から見れば「買い物」です。このように、DDDでは「境界づけられたコンテキスト」と呼ばれる概念があり、役割に応じて異なるモデルを作成することが求められます。
例えば、売り手側は出荷日や卸値、利益率などの情報が必要ですが、買い手側にとってはそのような情報は不要です。それぞれの視点で「りんご」という商品のエンティティを別々にモデリングする必要があります。
実際のコード例
以下に、DB駆動設計になってしまっていたコード例と、それをDDDに基づいて修正したコード例を示します。
DB駆動設計の例
例として商品をモデリングします
// DBモデルをそのまま流用した場合
type Item struct {
ID int
Name string
Price int
ShipmentDate time.Time
WholesalePrice int // 卸売価格
ProfitMargin float64 // 利益
}
このコードでは、「Item」というエンティティが、すべての情報を含んでしまっています。これは、売り手側と買い手側の両方の情報が混在しているため、ユースケースに応じた役割分担ができていません。
修正後のDDDに基づく設計例
// 売り手側のコンテキストにおけるItemのモデリング
type SellerItem struct {
ID int
Name string
ShipmentDate time.Time
WholesalePrice int // 卸売価格
ProfitMargin float64 // 利益
}
// 買い手側のコンテキストにおけるItemのモデリング
type BuyerItem struct {
ID int
Name string
Price int
}
このように、売り手と買い手の視点で、それぞれのコンテキストに応じたエンティティをモデリングしています。
得られるメリット
このようなモデリングをすることによりユースケースによった責務が明確化されます
また、一番のメリットとしてモデルの肥大化を防ぐことができます
例えば今回の場合だと、「買い手」と「売り手」を分けたため、それぞれに必要なドメインロジックを分けることができます
そのようにして整理され肥大化も防ぐことができ保守性も高まっていきます
まとめ
今回は「役割」を用いたモデリングの仕方について記載しました
エンジニアとしての経験はまだ浅いですが、これまでに感じたことをアウトプットすることで、より深く理解し、改善点を見つけたいと思っています。もしお気づきの点があれば、ぜひご指摘いただければ幸いです。
参考にした記事