【问题标题】:How to handle getOrCreateIfAbsent inside a DDD aggregate?如何处理 DDD 聚合中的 getOrCreateIfAbsent?
【发布时间】:2015-10-22 14:09:52
【问题描述】:

我的要求中有一个情况,我想看看是否有人对此有强烈的意见。

我正在处理的项目要求,给定客户,一旦客户将产品添加到购物车,如果购物车不存在,则必须创建购物车。目前,聚合是客户,它包含包含产品的购物车。因为这个系统是一种支持真正电子商务项目的辅助系统,所以一旦收到“AddProductCommand”,就会认为创建客户和购物车是理所当然的。在这种情况下,如果 Customer 和 Cart 不存在,则必须创建它们。

如果客户不存在,我当前的实现会在应用服务中创建客户。创建后,我使用customer.addProduct(addProductRequest) 传递AddProductRequestAddProductRequest 包含购物车 ID 和产品 ID。问题来了。一旦进入客户聚合,如果购物车不存在,我必须创建它。所以基本上,我的客户中没有 addCart 的购物车实体,我先调用它然后调用 addProduct。如果客户聚合内部不存在购物车,我需要创建它,然后将产品添加到其中。为此,我不创建任何工厂,因为我不希望有一个使测试复杂化的静态方法,我也不能只在聚合中创建一个新工厂。我只是在一个受保护的方法中使用 new 运算符创建 Cart 实体,我在测试时覆盖该方法以验证我在那里做什么。

所以我的问题是,这是一种有效的方法吗?在我看来,要将产品添加到购物车,应该首先将购物车添加到客户,如果不存在则失败。为此,我需要向应用程序服务添加逻辑,首先我会询问客户是否有具有该 ID 的购物车,否则创建它,然后在添加产品之前将其添加到客户。我可以添加一个给出这个请求的域服务,但是当我在几个地方读到域服务不应该注入任何工厂时,我需要注入工厂来创建购物车,这应该是应用服务的工作。

我可以这样做,但该项目在未来会变得更加复杂,因为会有另一个层,其中可以添加产品的凭证列表,如果它们不存在,还必须创建客户、购物车和产品当AddVoucherCommand 被消耗时。在这种情况下,如果我不想在模型中创建实体,我需要在应用程序/域服务中检查每个聚合或实体中是否有必要的实体,我认为这不是很 DDD友好,或者继续做我现在正在做的事情。也就是说,每个聚合/实体负责在调用 addXXXX 方法之前创建必要的实体。

一些简化的代码来解释我目前正在做什么以及将来我将要做的事情:

public class CustomerService {
    public void addVoucher(AddVoucherRequest addVoucherRequest) {
        Customer customer = customerRepo.load(customerId);
        customer.addVoucher(addVoucherRequest);
        customerRepo.save(customer);
    }
}

public class Customer() {
    public void addVoucher(AddVoucherRequest addVoucherRequest) {
        Cart cart = getOrCreateIfAbsent(addVoucherRequest.getCartId());
        cart.addVoucher(addProductRequest);
    }
    private Cart getOrCreateIfAbsent(long cartId){
        Optional<Cart> cart = carts.stream().filter(cart -> cart.getId() == cartId).findFirst();
        return cart.orElseGet(() -> {
             Cart newCart = createCart(cartId);
             carts.add(newCart);
             return newCart;
        }
     }
     protected Cart createCart(long cartId) {
         return new Cart(cartId);
     }
}
public class Cart() {
     public void addVoucher(AddVoucherRequest addVoucherRequest) {
         Product product = getOrCreateIfAbsent(addVoucherRequest.getProductId());
         product.addVoucher(addVoucherRequest);
     }
     private void getOrCreateIfAbsent(long productId) {
         Optional<Product> product = products.stream().filter(product -> product.getId() == productId).findFirst();
        return product.orElseGet(() -> {
             Product newProduct = createProduct(productId);
             products.add(newProduct );
             return newProduct ;
        }
     }
     protected Product createProduct(long productId) {
         return new Product(productId);
     }
 }

有什么建议吗?

谢谢!

【问题讨论】:

  • 但是我会在同一个事务中创建两个聚合,这是不推荐的。此外,这个域相当简单,因为它不是完整的电子商务应用程序,而是它的一个子集。我认为这种情况下的购物车属于客户,因为没有它就无法存在。当我必须添加优惠券时,它也无法解决问题。
  • 系统将要求为每个客户在特定时间检查哪些客户有特定的优惠券。一旦找到,这些客户将在一周内更新,因此他们无法获得他们应用的某些治疗。购物车被保存,而不是被删除,产品和优惠券也是如此。该系统必须快速运行,以便将所有内容保存在内存中,一旦发送事件,必须通知所有符合条件并拥有特定优惠券的客户。所以基本上,我现在需要为每个客户及其购物车、产品和优惠券做一些说明。
  • creating two aggregates in the same transaction which is not recommended:您指的是哪两个聚合?购物车和...客户?为什么要在那时创建 Customer ?它不应该已经存在了吗?
  • 另外,在同一个事务中创建两个聚合并不像更新两个聚合那样糟糕。根据定义,对正在创建的事物没有并发访问。
  • 在当前的方法中,只有一个聚合,Customer.2 聚合是 guillaume31 的建议,但我之前解释过,我考虑了 Customer 内部的所有内容,因为我在 Customer 中有一个不变量,这取决于包含在购物车、产品和稍后在凭证中的数据。客户可能尚未创建,因为要求表明,对于请求将产品添加到购物车的命令,购物车可能尚未创建,在这种情况下它有要创建。这是我的主要问题,因为目前是使用请求中的数据创建它的购物车

标签: java domain-driven-design


【解决方案1】:

选择等待客户订购东西以在新系统中创建他/她,而不是预先从旧系统导入所有客户是纯粹的技术决定,它与您的域 IMO 无关。

因此,它不应该以任何方式影响您设计域对象的方式 - 据我了解,getOrCreateIfAbsent() 应该是某种应用程序服务中的一种行为,而不是您的某个实体中的一种行为。

【讨论】:

  • 所以,据此,如果给定客户尚未创建我得到一个 addVoucherRequest 的情况,那么我应该在应用程序服务中创建所有相关实体吗?我的问题是,如果客户还没有,我可以在应用层和工厂创建它。但是如果缺少的部分是购物车中的产品怎么办?正如你提到的,没有预先准备好所有数据是我可以做的技术决定'没有克服,我需要满足这些要求,系统使用异步消息,在这种情况下可能导致丢失消息。抛出异常是没有选择的
  • 你不能凭空想出数据,一切都必须在AddVoucherRequest。如果您拥有所有数据,您可以在服务中连续创建您需要的每个实体。话虽如此,我发现与旧购物车中的旧产品相关的新优惠券的概念非常奇怪。
  • 我还建议您问另一个问题(最好在 programmers.stackexchange.com 上)关于在该“支持系统”中水合数据的策略,而不管 DDD。我有一种直觉,你选择了一种低于标准的方法,尽管我在这类问题上没有太多经验来支持它。无论如何,如果不更好地了解该系统应该做什么,就很难说清楚。
  • 谢谢。是的,我确实拥有请求中的所有信息。否则我将无法创建实体。为每个请求创建购物车、产品和凭证对我来说感觉很奇怪但是经过考虑,没有其他办法。每个实体的 id 将用于首先在客户内部查找实体,如果找不到则首先添加新实体。我同意要求很奇怪,但这就是我得到的
猜你喜欢
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-22
  • 2020-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多