为什么每个事务只修改一个聚合实例?

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)

  • :)我在企业集成模式中学到了这个词.如果我没有弄错的话,"有些工具可以帮助缓解这个问题".我也在学习英语. (3认同)