【问题标题】:DDD - price calculation when adding item to order / receiptDDD - 将商品添加到订单/收据时的价格计算
【发布时间】:2020-03-18 04:54:02
【问题描述】:

我正在尝试在我的最新项目中应用 DDD。虽然我在确定将一些业务逻辑/计算放在哪里时遇到问题。

首先我将描述业务流程,然后我将如何考虑实施。

业务流程: 它只是将收据项目添加到收据中。但是,商品的价格取决于客户的类型和商品的数量。

即:“A”类客户想要购买两餐。在他的安排中,允许他吃一顿午餐打折,但因为他很饿,他吃了两顿。第一顿应该是 4 欧元(折扣),第二顿应该是 6 欧元(全价)。

我的想法是:

产品 - 一个实体(具有不同的有界上下文,将仅用作输入参数) - 字段:代码、名称、...、价格集合每个都针对不同的客户类型。

ReceiptItem - 实体 - 字段:产品代码、产品名称、数量、价格、... 收据作为聚合根 - 字段: 客户(实体)- 包含 ReceiptItems 的集合

在收据上,会有一个方法addItem(Product product, int count),我计划在这个方法中添加正确的价格计算。

回到我们的例子:receipt.addItem('menu1', 2)

所以我会检查客户类型以及允许他打折的午餐数量。鉴于上面的例子,我会看到,他是允许的。然后我需要获得实际的折扣价格,我会直接从公开方法 getCustomersPrice(customerid) 的产品中获得。 最后,我会在 Receipt 中添加两个收据项目。它们都将具有相同的产品代码,但价格不同。 (如果配额为 2 或更多,我只会以折扣价将一件物品添加到收藏中,但会计算 2 件)。

您会说这是一个好方法吗?我担心将产品作为输入参数传递给 addItem。因为它似乎创建了一个紧密的耦合。

谢谢。

【问题讨论】:

  • 现在我正在查看杂货店的收据。收据包含一些折扣以及产品。折扣作为单独的项目列出,产品以全价列出。这样,任何产品都不应该“知道”其对特定客户的价格。但客户可能有许多适用于不同产品的折扣,如果符合条件,可以添加到订单中。
  • 我喜欢这个,它实际上允许保持域对象相当干净,只包含必需品,同时对不同的规则集具有灵活性。谢谢一个好主意。

标签: domain-driven-design business-logic


【解决方案1】:

Receipt 聚合根应该更小:ReceiptReceiptItems,这很好,但 Customer 应该是它自己的聚合根。客户独立于收据而存在,加上小集合是首选。

Product 在这个有界上下文中也是一个单独的域对象,即使产品也存在于一个单独的有界上下文中。 Product 也需要在 Receipt 聚合之外。

鉴于存在多个聚合根这一事实,将产品添加到收据的逻辑涉及多个聚合。因此,逻辑不能驻留在Receipt 聚合或任何其他聚合内。此类逻辑最好放在Domain service 中(可能与Receipt 聚合位于同一包中),例如:

public class ReceiptService {

    void addItem(ReceiptId aReceiptid, ProductId aProductId, int quantity) {

        // business logic to determine price per item
        // ...

        // for each item, forward item creation to receipt:
        receipt.addItem(productIdOrName, priceOfItem);
    }
}

Domain 服务结合各种聚合数据以确定每件商品的价格。创建ReceiptItem 并将其插入收据项集合的内部逻辑最好转发到Receipt 聚合。

【讨论】:

  • 谢谢,我现在知道这确实是一个跨行业的操作。但是,我正在为一件事而苦苦挣扎。收据一经保存,不可更改。因此,如果我正在调用 add item 方法,这意味着收据尚未持久化,因此我当时没有收据Id。我认为,这个想法是根据 id 从数据库中提取所有必需的实体,但这不适用于接收。所以我可能不得不直接使用收据对象作为输入参数。
  • 再想一想,我现在意识到,因为这是一个 javaFx 应用程序,所以我是从全状态的角度来看待它的。因此,在无国籍状态下,这肯定会奏效。我当然必须将收据保持在无效状态(没有至少一个收据项目的收据,没有意义)。老实说,我不习惯使用默认访问级别,但在我看来,这正是这种情况下所需要的,因为所有主要方法(添加项目、删除项目、更新计数)都需要由服务方法由于业务逻辑需要不同的上下文。听起来不错?
  • 将收据保持在无效状态(没有收据项)当然并不理想。要么使用 item 而不是尚未存在的 id。或者,代替addItem 实现createReceipt(Collection productsAndQuantities) 方法,该方法创建收据和receiptItems。
猜你喜欢
  • 2013-04-20
  • 1970-01-01
  • 1970-01-01
  • 2016-08-15
  • 1970-01-01
  • 1970-01-01
  • 2015-04-24
  • 2020-05-20
  • 2020-11-13
相关资源
最近更新 更多