【发布时间】:2013-07-20 04:16:16
【问题描述】:
我正在阅读 Vernon 的文章 Effective Aggregate Design。我有一个问题,为什么每个事务只修改一个聚合实例?
让我们举个例子,考虑一个仓库库存管理的故事。
Inventory 表示仓库中有数量的物品。以上海仓库5Implementing Domain Driven Design图书为例。
Entry 表示关于Inventory 的输入/输出操作的日志。以上海仓库入库2Implementing Domain Driven Design图书为例。
如果提交Entry,则需要更改Inventory的数量。
我很容易想到,这是一个可以通过事务一致性实现的不变量。
解决方案 A:使用一个聚合并将 Entry 集群到 Inventory。
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);
}
}
解决方案 B:对 Inventory 和 Entry 使用单独的聚合。
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);
}
}
A 和 B 都是可行的,但解决方案 B 有点不雅,因为在不涉及 Entry 的情况下会无意中调用 Inventory.add(quantity)。 这是规则(每个事务只修改一个聚合实例)试图为我指出的吗?我很困惑为什么我们应该只修改事务中的一个聚合,如果我们不这样做会出现什么问题。
Update1 开始
它是否打算缓解并发问题(使用另一条“制作更小的聚合”规则)?例如,Entry 是竞争相对较低的聚合,Inventory 是竞争相对较高的聚合(假设多个用户可以操纵一个 Inventory) ,如果我在事务中同时修改它们,则会导致不必要的并发失败。
更新 1 结束
如果我采用解决方案A,还需要解决一些进一步的问题:
1.如果一个Inventory有很多Entry,我需要一个分页查询UI怎么办?如何使用集合实现分页查询?一种方法是加载所有 Entry 并选择页面需要的内容,另一种方法是 InventoryRepository.findEntriesBy(invoiceId, paging),但这似乎打破了仅通过 get 获取本地实体的规则它是聚合的,然后导航对象图。
2.如果 Inventory 的 Entry 太多,我必须在添加新 Entry 时加载所有这些怎么办?
我知道这些问题源于缺乏充分的理解。所以欢迎任何想法,在此先感谢。
【问题讨论】:
-
我认为这种方法还可以降低死锁发生的可能性stackoverflow.com/questions/735894/database-deadlocks