SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/microwind/ai-skills --skill aggregate명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | 聚合 (Aggregate) |
| description | 以聚合根为边界,包含多个相关Entity和ValueObject的集合。保证数据一致性和事务边界。 |
Eric Evans 将聚合定义为:一组相关对象的集合,作为数据变更的整体单元("a cluster of associated objects that we treat as a unit for the purpose of data changes")。每个聚合都有一个聚合根——作为外部访问聚合内部对象的唯一入口,以及一条由根的标识划定的一致性边界。
核心概念:
// 聚合根:Order
public class Order {
private final String orderId;
private List<OrderLine> items; // 从属于聚合
private OrderStatus status;
private Money totalAmount;
public void addItem(Product product, int quantity) {
OrderLine line = new OrderLine(product, quantity);
items.add(line);
recalculateTotal();
}
public void cancel() {
if (!canBeCanceled()) {
throw new IllegalStateException("Cannot cancel order");
}
this.status = OrderStatus.CANCELED;
}
// 只通过聚合根修改数据,不直接访问items
// ❌ 不要:order.getItems().add(...);
// ✅ 要:order.addItem(...);
private void recalculateTotal() {
totalAmount = items.stream()
.map(OrderLine::getTotal)
.reduce(Money.ZERO, Money::add);
}
private boolean canBeCanceled() {
return status == OrderStatus.PENDING || status == OrderStatus.PAID;
}
}
// OrderLine:聚合内的对象,不应该独立存在
public class OrderLine {
private final Product product;
private final int quantity;
public Money getTotal() {
return product.getPrice().multiply(quantity);
}
}
class Order:
def __init__(self, order_id: str, customer_id: str):
self.order_id = order_id
self.customer_id = customer_id
self.items = []
self.status = "PENDING"
self.total_amount = Money(0, "USD")
def add_item(self, product: 'Product', quantity: int):
"""通过聚合根添加项"""
line = OrderLine(product, quantity)
self.items.append(line)
self._recalculate_total()
def can_be_canceled(self) -> bool:
return self.status in ["PENDING", "PAID"]
def cancel(self):
if not self.can_be_canceled():
raise ValueError("Cannot cancel order")
self.status = "CANCELED"
def _recalculate_total(self):
self.total_amount = sum(
(line.get_total() line .items),
Money(, )
)
:
():
.product = product
.quantity = quantity
() -> :
.product.price.multiply(.quantity)
// ❌ 错误:从外部直接访问聚合内部
Order order = orderRepository.find("O001");
OrderLine line = order.getItems().get(0); // 直接访问!
line.setQuantity(100); // 修改!
// ✅ 正确:通过聚合根
Order order = orderRepository.find("O001");
order.updateItem(0, 100); // 通过聚合根修改
Vaughn Vernon 在 Implementing Domain-Driven Design(IDDD,红皮书)中提炼了四条被广泛认可的聚合设计经验:
// ✅ 遵循规则:小聚合 + ID 引用
public class Order {
private OrderId id;
private CustomerId customerId; // 通过 ID 引用其他聚合,而非 Customer 对象
private List<OrderLine> items; // 聚合内部实体
private OrderStatus status;
}
// ❌ 违反规则:穿透边界、对象引用
public class Order {
private Customer customer; // 直接持有另一聚合的对象引用
private List<OrderLine> items;
public void changeCustomerEmail(String email) {
customer.setEmail(email); // 跨聚合直接写——破坏单写规则
}
}
"小聚合"是原则,但"多小才算小"没有标准答案。下列实操判据可帮助判断:
判断聚合是否过大:
判断聚合是否过小(被切碎了):
经验判据:
❌ 上帝聚合——把 Order + Customer + Product + Inventory 全塞进一个根 → 用 ID 引用其他聚合,每个聚合只承载自己的真实不变量
❌ 从外部直接操作聚合内部对象——order.getItems().add(...) 跳过聚合根校验
→ 暴露行为方法 order.addItem(...),集合修改入口只在聚合根内部
❌ 跨聚合写在一个事务里——一次事务同时更新 Order、Inventory、Payment → 同一事务只改一个聚合,跨聚合协同用领域事件 + 最终一致性
聚合是建立清晰边界、保证数据一致的关键设计。