【发布时间】:2015-10-22 14:09:52
【问题描述】:
我的要求中有一个情况,我想看看是否有人对此有强烈的意见。
我正在处理的项目要求,给定客户,一旦客户将产品添加到购物车,如果购物车不存在,则必须创建购物车。目前,聚合是客户,它包含包含产品的购物车。因为这个系统是一种支持真正电子商务项目的辅助系统,所以一旦收到“AddProductCommand”,就会认为创建客户和购物车是理所当然的。在这种情况下,如果 Customer 和 Cart 不存在,则必须创建它们。
如果客户不存在,我当前的实现会在应用服务中创建客户。创建后,我使用customer.addProduct(addProductRequest) 传递AddProductRequest。 AddProductRequest 包含购物车 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 中有一个不变量,这取决于包含在购物车、产品和稍后在凭证中的数据。客户可能尚未创建,因为要求表明,对于请求将产品添加到购物车的命令,购物车可能尚未创建,在这种情况下它有要创建。这是我的主要问题,因为目前是使用请求中的数据创建它的购物车