Yug*_*hou 10 domain-driven-design
我正在阅读Vernon的文章Effective Aggregate Design.我有一个问题,为什么每个事务只修改一个聚合实例?
让我们举个例子,考虑一下Warehouse逆变管理故事.
库存表示仓库中具有数量的物料.5 例如,在上海仓库实施领域驱动设计书籍.
Entry表示有关Inventory的进/出操作的日志.例如,在上海仓库中输入2个实施领域驱动设计书籍.
一个库存的数量需要的,如果要改变输入提交.
我很容易想到,这是一个不变的,可以通过事务一致性来实现.
解决方案A:使用一个聚集和集群进入到库存.
public class Inventory implements Aggregate<Inventory> {
private InventoryIdentity id;
private Sku sku;
private int quantity;
private List<Entry> entries;
public void add(Entry entry) {
this.quantity += entry.getQuantity();
this.entries.add(entry);
}
}
public class Entry implements LocalEntity<Entry> {
private int quantity;
// some other attributes such as whenSubmitted
}
public class TransactionalInventoryAdminService impelments InventoryAdminService, ApplicationService {
@Override
@Transactional
public void handle(InventoryIdentity inventoryId, int entryQuantity, ...other entry attributes)
Inventory inventory = inventoryRepository.findBy(inventoryId);
Entry entry = inventory.newEntry(entryQuantity, ..);
inventory.add(entry);
inventoryRepository.store(inventory);
}
}
Run Code Online (Sandbox Code Playgroud)
解决方案B:使用单独的聚合库存和输入.
public class Inventory implements Aggregate<Inventory> {
private InventoryIdentity id;
private Sku sku;
private int quantity;
public void add(int quantity) {
this.quantity += quantity;
}
}
public class Entry implements LocalEntity<Entry> {
private Inventory inventory;
private int quantity;
private boolean handled = false;
// some other attributes such as whenSubmitted
public void handle() {
if (handled) {
throw .....
} else {
this.inverntory.add(quantity);
this.handled = true;
}
}
}
public class TransactionalInventoryAdminService impelments InventoryAdminService, ApplicationService {
@Override
@Transactional
public void handle(InventoryIdentity inventoryId, int entryQuantity, ...other entry attributes)
Inventory inventory = inventoryRepository.findBy(inventoryId);
Entry entry = inventory.newEntry(entryQuantity, ..);
entry.handle();
inventoryRepository.store(inventory);
entryRepository.store(entry);
}
}
Run Code Online (Sandbox Code Playgroud)
A和B都是可行的,但是解决方案B对于让无意的机会无法调用Inventory .add(数量)而没有涉及Entry是一种不优雅的方式.这是规则(每个事务仅修改一个聚合实例)试图为我指出的吗?我很困惑为什么我们应该只修改一个事务中的一个聚合,如果我们不这样做会出错.
Update1开始
它是否打算减轻并发问题(另一个规则是"制作更小的聚合")?例如,Entry是具有相对较低争用的聚合,而Inventory是具有相对较高的竞争的聚合(假设多个用户可以操作一个库存),如果我在事务中修改它们,则会导致不必要的并发失败.
Update1结束
如果我采用解决方案A,还需要解决一些其他问题:
1.什么如果有很多项 S代表的清单,我需要一个分页查询UI?如何使用集合实现分页查询?一种方法是加载所有条目并选择页面需要的内容,另一种方式是InventoryRepository.findEntriesBy(invoiceId,paging),但这似乎打破了获取本地实体的规则,只需获取它的aggreate然后导航对象图形.
2.什么,如果有太多输入 S代表的清单,我必须加载所有的人都在添加新条目?
我知道这些问题源于缺乏充分理解.所以任何想法都是受欢迎的,提前谢谢.
Jef*_*aes 11
经验法则是保持聚合较小,因为您希望避免由于并发而导致的事务性失败.如果它不应该是为什么我们会使内存占用大?
因此,解决方案A不是最佳的.大集合通常会引入容易避免的问题.
确实,另一个经验法则是只在一个事务中更改一个聚合.如果您使Entry成为自己的聚合,则可以使库存的数量最终保持一致,这意味着Entry聚合可以引发库存订阅的事件.这样,您只需更改每个事务的一个聚合.
public class Entry {
public Entry(InventoryId inventoryId, int quantity) {
DomainEvents.Raise(new EntryAdded(inventoryId, quantity))
}
}
Run Code Online (Sandbox Code Playgroud)
如果您对最终的一致性感到不舒服,您仍然可以将聚合分开,但是现在在一个事务中修改它们 - 直到您感到痛苦,使用封装域服务.另一个选择是保持域事件正在进行中,以便它们也在单个事务中提交.
public class InventoryService {
public void AddEntryToInventory(Entry entry) {
// Modify Inventory quantity
// Add Entry
}
}
Run Code Online (Sandbox Code Playgroud)