はじめに
前回イベントストーミングで業務シナリオを洗い出したので、今回はドメインモデルを作っていきたいと思います。
イベントストーミングがよくわからない方は前回の記事をぜひ
https://qiita.com/ma-ru55/items/81a7ef0cb3320bcba2c7
イベントストーミングで洗い出したモデル
ドメインモデル描いていたら、モデル図修正した方がよさそうな箇所あったので、前回の記事と少し図が変わってます。
ドメインモデルを描いてみよう
今回は商品購入コンテキストを例に挙げていきます。
PlantUMLを使用していきます。
集約
まず、集約を定義していきます。
今回はOrder(注文)をルートエンティティに、OrderItem(注文商品)とPaymentMethod(決済手段)エンティティを持つようにします。
@startuml OrderAggregation
title 注文集約 (Order Aggregation)
package "注文集約(OrderAggregation)" {
class "Order(注文)" as Order << (R,red) RootEntity >> {
OrderId: OrderId
OrderDateTime: 注文日時
Status: ステータス
TotalAmout: 合計
}
class "OrderItem(注文商品)" as Item << (E,green) Entity >> {
ItemId: ItemId
quantity: 個数
subtotal: 小計
}
class "PaymentMethod(決済手段)" as Item << (E,green) Entity >> {
PaymentMethodId: PaymentMethodId
PaymentMethodType: 決済種別
}
}
@enduml
属性
次にエンティティの持つデータや情報を定義していきます。
コンテキスト合わせて名称を決めていきます。
@startuml OrderAggregation
title 注文集約 (Order Aggregation)
package "注文集約(OrderAggregation)" {
class "Order(注文)" as Order << (R,red) RootEntity >> {
OrderId: OrderId
OrderDateTime: 注文日時
Status: ステータス
TotalAmout: 合計
}
class "OrderId" as OrderId {
+ id: string
}
class "OrderDateTime" as OrderDateTime {
+ datetime: LocalDateTime
}
class "Status" as Status {
+ status: Enum { UNORDERED, ORDERED, DELIVERED }
}
class "TotalAmout" as TotalAmout {
+ totalAmount: Integer
+ subtotal: SubTotal
+ taxAmount: TaxAmount
+ feeAmount: FeeAmount
}
class "TaxAmount" as TaxAmount {
+ amount: Integer
}
class "FeeAmount" as FeeAmount {
+ amount: Integer
}
class "OrderItem(注文商品)" as Item << (E,green) Entity >> {
ItemId: ItemId
quantity: 個数
subtotal: 小計
}
class "ItemId" as ItemId {
+ id: String
}
class "Quantity" as Quantity {
+ value: Integer
}
class "Subtotal" as Subtotal {
+ amount: Integer
}
class "PaymentMethod(決済手段)" as Item << (E,green) Entity >> {
PaymentMethodId: PaymentMethodId
PaymentMethodType: 決済種別
}
class "PaymentMethodId" as PaymentMethodId {
+ id: String
}
class "PaymentMethodType" as PaymentMethodType {
+ type: Enum { CREDIT_CARD, E_MONEY, BANK_TRANSFER }
}
}
@enduml
今回は教科書的に全て値オブジェクトにしましたが、個人的に制約ないものなどは、値オブジェクトにしても、クラス増えるのに対して、あまりメリットを感じてないので、値オブジェクトでなくていいと思います。
制約
次に制約を追加していきます。
ビジネス上の制約を記載していきます。
@startuml OrderAggregation
title 注文集約 (Order Aggregation)
package "注文集約(OrderAggregation)" {
class "Order(注文)" as Order << (R,red) RootEntity >> {
OrderId: OrderId
OrderDateTime: 注文日時
Status: ステータス
TotalAmout: 合計
}
class "OrderId" as OrderId {
+ id: string
}
note bottom of OrderId
UUIDを適用する。
end note
class "OrderDateTime" as OrderDateTime {
+ datetime: LocalDateTime
}
note bottom of OrderDateTime
日本標準時で定義すること。
end note
class "Status" as Status {
+ status: Enum { UNORDERED, ORDERED, DELIVERED }
}
note bottom of Status
初回作成時はUNORDEREDから始まる
UNORDEREDは未注文
ORDEREDは注文済み
DELIVEREDは配達済み
end note
class "TotalAmout" as TotalAmout {
+ totalAmount: Integer
+ subtotal: SubTotal
+ taxAmount: TaxAmount
+ feeAmount: FeeAmount
}
note bottom of TotalAmout
日本円で扱う
MIN=0
end note
class "TaxAmount" as TaxAmount {
+ amount: Integer
}
note bottom of TaxAmount
日本円で扱う。
小数点以下は切り捨て
MIN=0
end note
class "FeeAmount" as FeeAmount {
+ amount: Integer
}
note bottom of FeeAmount
日本円で扱う。
小数点以下は切り捨て
MIN=0
end note
class "OrderItem(注文商品)" as OrderItem << (E,green) Entity >> {
ItemId: ItemId
quantity: 個数
subtotal: 小計
}
class "ItemId" as ItemId {
+ id: String
}
note bottom of ItemId
UUIDを適用する。
end note
class "Quantity" as Quantity {
+ value: Integer
}
note bottom of Quantity
MIN=0
end note
class "Subtotal" as Subtotal {
+ amount: Integer
}
note bottom of Subtotal
日本円で扱う
MIN=0
end note
class "PaymentMethod(決済手段)" as PaymentMethod << (E,green) Entity >> {
PaymentMethodId: PaymentMethodId
PaymentMethodType: 決済種別
}
class "PaymentMethodId" as PaymentMethodId {
+ id: String
}
note bottom of PaymentMethodId
MIN=2
MAX=2
end note
class "PaymentMethodType" as PaymentMethodType {
+ type: Enum { CREDIT_CARD, E_MONEY, BANK_TRANSFER }
}
note bottom of PaymentMethodType
CREDIT_CARDはクレジットカード
E_MONEYは電子マネー
BANK_TRANSFERは銀行振り込み
end note
}
@enduml
関連性
最後に関連性を定義して図を完成させます。
重みも一緒につけます。
@startuml OrderAggregation
title 注文集約 (Order Aggregation)
package "注文集約(OrderAggregation)" {
class "Order(注文)" as Order << (R,red) RootEntity >> {
OrderId: OrderId
OrderDateTime: 注文日時
Status: ステータス
TotalAmout: 合計
}
class "OrderId" as OrderId {
+ id: string
}
note bottom of OrderId
UUIDを適用する。
end note
class "OrderDateTime" as OrderDateTime {
+ datetime: LocalDateTime
}
note bottom of OrderDateTime
日本標準時で定義すること。
end note
class "Status" as Status {
+ status: Enum { UNORDERED, ORDERED, DELIVERED }
}
note bottom of Status
初回作成時はUNORDEREDから始まる
UNORDEREDは未注文
ORDEREDは注文済み
DELIVEREDは配達済み
end note
class "TotalAmout" as TotalAmout {
+ totalAmount: Integer
+ subtotal: Subtotal
+ taxAmount: TaxAmount
+ feeAmount: FeeAmount
}
note bottom of TotalAmout
日本円で扱う
MIN=0
end note
class "TaxAmount" as TaxAmount {
+ amount: Integer
}
note bottom of TaxAmount
日本円で扱う。
小数点以下は切り捨て
MIN=0
end note
class "FeeAmount" as FeeAmount {
+ amount: Integer
}
note bottom of FeeAmount
日本円で扱う。
小数点以下は切り捨て
MIN=0
end note
class "OrderItem(注文商品)" as OrderItem << (E,green) Entity >> {
ItemId: ItemId
quantity: 個数
subtotal: 小計
}
class "ItemId" as ItemId {
+ id: String
}
note bottom of ItemId
UUIDを適用する。
end note
class "Quantity" as Quantity {
+ value: Integer
}
note bottom of Quantity
MIN=0
end note
class "Subtotal" as Subtotal {
+ amount: Integer
}
note bottom of Subtotal
日本円で扱う
MIN=0
end note
class "PaymentMethod(決済手段)" as PaymentMethod << (E,green) Entity >> {
PaymentMethodId: PaymentMethodId
PaymentMethodType: 決済種別
}
class "PaymentMethodId" as PaymentMethodId {
+ id: String
}
note bottom of PaymentMethodId
MIN=2
MAX=2
end note
class "PaymentMethodType" as PaymentMethodType {
+ type: Enum { CREDIT_CARD, E_MONEY, BANK_TRANSFER }
}
note bottom of PaymentMethodType
CREDIT_CARDはクレジットカード
E_MONEYは電子マネー
BANK_TRANSFERは銀行振り込み
end note
Order "1" -down- "1" OrderItem : has >
Order "1" -down- "1" PaymentMethod : has >
Order *-down- OrderId
Order *-down- OrderDateTime
Order *-down- Status
Order *-down- TotalAmout
TotalAmout *-down- Subtotal
TotalAmout *-down- TaxAmount
TotalAmout *-down- FeeAmount
OrderItem *-down- ItemId
OrderItem *-down- Subtotal
OrderItem *-down- Quantity
PaymentMethod *-down- PaymentMethodId
PaymentMethod *-down- PaymentMethodType
}
@enduml
さいごに
今回はドメインモデルのやり方を実践してみました。
こうやって図にしてみると、どういうクラスを用意して、どういうチェックをするかというのが、だいぶイメージつくのではないのでしょうか。
次回は実際にソースにマッピングさせていきます。