0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

実践DDD-ドメインモデル

Posted at

はじめに

前回イベントストーミングで業務シナリオを洗い出したので、今回はドメインモデルを作っていきたいと思います。

イベントストーミングがよくわからない方は前回の記事をぜひ
https://qiita.com/ma-ru55/items/81a7ef0cb3320bcba2c7

イベントストーミングで洗い出したモデル

スクリーンショット 2024-11-11 22.13.27.png

ドメインモデル描いていたら、モデル図修正した方がよさそうな箇所あったので、前回の記事と少し図が変わってます。

ドメインモデルを描いてみよう

今回は商品購入コンテキストを例に挙げていきます。
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

さいごに

今回はドメインモデルのやり方を実践してみました。
こうやって図にしてみると、どういうクラスを用意して、どういうチェックをするかというのが、だいぶイメージつくのではないのでしょうか。

次回は実際にソースにマッピングさせていきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?