🐘

DDDで集約を跨いだ情報でロジックを構築するための「getter高階関数パターン」の紹介

に公開4
株式会社ログラス テックブログ

Discussion

dog_cat_foxdog_cat_fox

自分だと orderLines を作って Order.create に渡してしまいそう。
動かしてなくてすみませんがこんな感じです。

    val newOrder = Order.create(
      orderLines = dto.lineParams.map { param ->
        val product = productIdMap[param.productId] ?: throw NotFoundException()
        OrderLine.create(param, product)
      }
    )

これだと OrderLine が外に露出しているので DDD 的にあんまり良く無いんですかね。

Yuito SatoYuito Sato

質問ありがとうございます。

DDD的というよりかは自然言語としてどのような文章が成立するかで考えると責務をどこに置くべきかわかりやすいかなと思いました。
「化粧水を2個、洗顔料を1個で注文を作る」という文章通りにロジックを書くならば入力は化粧水2個と洗顔料1個であり、出力は注文です。このとき文章を見ると注文金額の計算は外から隠蔽されているので注文作成が注文明細の小計計算の責務を持つべきなのではと思っています。

copplacoppla

ドメインサービスをむやみに利用させないアイデアとして非常に参考になりました。サービスは意味が広く慎重に管理しないとすぐ肥大化していくので。。。

ちょっと気になったのが、本来サービスに隔離すべき Entity を越境した依存関係が不用意に Entity へ集約されてしまった神 Entity ができてしまうのではないか、と感じました。(このあたりはトレードオフだとは思いますが)
「getter高階関数」と定義している部分は、リポジトリの抽象に依存するという内容を言い換えたものと理解したので、私だったら Read だけができる IReadProductRepository に依存したドメインサービスの実装を検討します。

class CreateOrderService(
    private val orderRepository: IOrderRepository,
    private val productRepository: IReadProductRepository,
) {
    fun create(
        lineParams: List<CreateOrderLineParam>,
    ): Order {
        // Order 作成処理
    }
}

普段 Kotlin を使わないので、このような実装はできないという前提があったのなら申し訳ありません。

Yuito SatoYuito Sato

コメントありがとうございます。CreateOrderSeviceというのはドメインサービスでProductRepossiotry全体の依存ではなくRead処理だけの依存に限定したものですね。たしかにコード量が増えるデメリット以外では依存を限定できるのでこちらのほうが単純なドメインサービスより良いですね。
もちろんこのような実装はKotlinでも可能です。