بنقرة واحدة
ddd
封装复杂对象和聚合的创建过程,将创建职责从领域对象中剥离,保证聚合创建时的不变量满足。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
封装复杂对象和聚合的创建过程,将创建职责从领域对象中剥离,保证聚合创建时的不变量满足。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
以聚合根为边界,包含多个相关Entity和ValueObject的集合。保证数据一致性和事务边界。
在DDD中具有唯一身份标识和生命周期的对象,通过身份而非属性值相等判断。
没有身份标识,通过属性值判断相等的对象。不可变,通常代表领域中的度量或描述。
命令查询责任分离,将数据的写入操作和读取操作分别用不同的模型处理,优化各自的性能。
将DDD战略设计应用于微服务架构,限界上下文指导服务拆分,领域事件实现服务间通信。
对比 MVC 模式与 DDD 方法论的差异,分析 MVC 在复杂业务下的局限性,帮助团队判断何时应采用 DDD。
| name | DDD 工厂 |
| description | 封装复杂对象和聚合的创建过程,将创建职责从领域对象中剥离,保证聚合创建时的不变量满足。 |
工厂是 DDD 战术设计的一个构建块,源于 GoF 设计模式中的工厂模式。Eric Evans 在蓝皮书中保留这一概念的动机是:当对象或聚合的创建过程足够复杂,让对象自己"生自己"会污染领域模型时,把创建逻辑独立成工厂——让构造即合法。
核心动机:
将创建复杂对象和聚合的职责赋予一个独立的对象。这个对象本身不承担领域模型的业务职责,但仍是领域设计的一部分。工厂应提供一个清晰的创建接口,封装创建过程的复杂性,并确保聚合一经创建即满足其业务不变量。
⚠️ 工程务实:独立工厂类是少数场景——大多数聚合用构造函数 + 不变量校验或聚合根上的静态工厂方法就足够。只有当创建过程需要外部依赖(仓储、策略、外部服务)或涉及多步骤组装时,才升级为独立工厂类。不要为了"看起来更 DDD"而为简单聚合包一层空壳工厂。
✅ 需要工厂的场景:
▸ 创建逻辑涉及多个步骤或外部数据
▸ 聚合内部包含复杂的对象图(多层嵌套的实体和值对象)
▸ 需要保证创建时的不变量(例如订单必须至少有一个商品行)
▸ 构造函数参数过多,难以理解
▸ 对象的初始状态需要根据上下文计算
❌ 不需要工厂的场景:
▸ 简单对象:直接用构造函数即可
▸ 无任何不变量的值对象:直接 new 没问题
▸ 工厂只是构造函数的"套壳"
当创建逻辑复杂、依赖外部服务时使用。
public class OrderFactory {
private final ProductRepository productRepo;
private final DiscountPolicy discountPolicy;
public OrderFactory(ProductRepository productRepo, DiscountPolicy discountPolicy) {
this.productRepo = productRepo;
this.discountPolicy = discountPolicy;
}
public Order create(CustomerId customerId, List<OrderLineSpec> specs) {
if (specs == null || specs.isEmpty()) {
throw new DomainException("订单必须至少有一个商品");
}
OrderId orderId = OrderId.generate();
List<OrderLine> lines = specs.stream()
.map(spec -> {
Product product = productRepo.findById(spec.productId())
.orElseThrow(() -> new DomainException("商品不存在"));
Money unitPrice = discountPolicy.applyTo(product.getPrice(), customerId);
return new OrderLine(product.getId(), unitPrice, spec.quantity());
})
.toList();
Order order = new Order(orderId, customerId, lines);
return order;
}
}
适用于大多数常规聚合。
public class Subscription {
private final SubscriptionId id;
private final ReaderId reader;
private final ColumnId column;
private final Instant startTime;
private SubscriptionStatus status;
private Subscription(SubscriptionId id, ReaderId reader, ColumnId column, Instant startTime) {
this.id = id;
this.reader = reader;
this.column = column;
this.startTime = startTime;
this.status = SubscriptionStatus.ACTIVE;
}
public static Subscription fromPayment(Payment payment, Column column) {
if (!payment.isCompleted()) {
throw new DomainException("只有支付完成才能创建订阅");
}
if (payment.getAmount().compareTo(column.getPrice()) < 0) {
throw new DomainException("支付金额不足");
}
return new Subscription(
SubscriptionId.generate(),
payment.getPayerId(),
column.getId(),
payment.getCompletedAt()
);
}
}
对构造逻辑不平凡的值对象做一层封装。
public class Money {
private final BigDecimal amount;
private final Currency currency;
private Money(BigDecimal amount, Currency currency) { ... }
public static Money of(BigDecimal amount, Currency currency) {
if (amount.scale() > currency.getDefaultFractionDigits()) {
throw new DomainException("精度超过货币允许范围");
}
return new Money(amount.setScale(currency.getDefaultFractionDigits(), RoundingMode.HALF_EVEN), currency);
}
public static Money yuan(long yuan) {
return of(BigDecimal.valueOf(yuan), Currency.getInstance("CNY"));
}
public static Money zero(Currency currency) {
return of(BigDecimal.ZERO, currency);
}
}
from dataclasses import dataclass
from typing import List
@dataclass(frozen=True)
class OrderLineSpec:
product_id: str
quantity: int
class OrderFactory:
def __init__(self, product_repo, discount_policy):
self.product_repo = product_repo
self.discount_policy = discount_policy
def create(self, customer_id: str, specs: List[OrderLineSpec]) -> "Order":
if not specs:
raise DomainError("订单必须至少有一个商品")
lines = []
for spec in specs:
product = self.product_repo.find_by_id(spec.product_id)
if not product:
raise DomainError(f"商品不存在: {spec.product_id}")
unit_price = self.discount_policy.apply(product.price, customer_id)
lines.append(OrderLine(product.id, unit_price, spec.quantity))
return Order(id=OrderId.generate(), customer_id=customer_id, lines=lines)
┌─────────────┐ ┌─────────────────┐ ┌─────────────────────┐
│ Constructor │ │ Factory │ │ Repository │
├─────────────┤ ├─────────────────┤ ├─────────────────────┤
│ 简单对象创建 │ │ 复杂对象创建 │ │ 重建已存在的聚合 │
│ 参数少 │ │ 多步骤/多依赖 │ │ 从存储加载数据 │
│ 无外部依赖 │ │ 可能访问仓储/服务 │ │ 负责持久化 │
└─────────────┘ └─────────────────┘ └─────────────────────┘
新建 (create) ─▶ Factory
重建 (reconstitute) ─▶ Repository
简单值对象 ─▶ Constructor
// ❌ 工厂只是组合字段,不校验
public static Order create(CustomerId customerId, List<OrderLine> lines) {
return new Order(customerId, lines); // lines 为空也能过!
}
// ✅ 工厂保证不变量
public static Order create(CustomerId customerId, List<OrderLine> lines) {
if (lines == null || lines.isEmpty()) {
throw new DomainException("订单必须至少有一个商品");
}
if (lines.size() > MAX_LINES) {
throw new DomainException("商品行数超限");
}
return new Order(OrderId.generate(), customerId, lines);
}
工厂是领域设计的一部分,定义在领域层,与聚合同 package。不要放到应用服务或基础设施层。
domain/
└── order/
├── Order.java ← 聚合根
├── OrderLine.java ← 实体
├── OrderFactory.java ← 工厂(同层)
└── OrderRepository.java ← 仓储接口
对于大部分场景,聚合根的静态工厂方法就够了,不需要独立工厂类:
// 大多数场景
Order order = Order.create(customerId, lines);
// 仅当创建需要外部依赖(查商品、算折扣)时,才用独立工厂类
Order order = orderFactory.create(customerId, lineSpecs);
// ❌ 抽象的 create / build
Subscription.create(payment);
// ✅ 表达业务语义
Subscription.fromPayment(payment); // 因支付而产生
Subscription.giftFrom(author, reader); // 作者赠予
Subscription.trialFor(reader, days); // 试用订阅
❌ 工厂只是构造函数的"套壳"——没有任何不变量校验或业务语义 → 工厂的价值是"构造即合法"或封装复杂创建;否则直接用构造函数
❌ 工厂放到应用层 / 基础设施层——破坏依赖方向 → 工厂属于领域层,与聚合同 package
❌ 抽象的方法命名——一律 create() / build(),丢失业务意图
→ 用业务语义命名:Subscription.fromPayment()、Order.placeFromCart()、Money.yuan(100)
| 概念 | 关系 |
|---|---|
| 聚合 | 工厂的主要用途是创建聚合 |
| 聚合根设计 | 聚合根上的静态工厂方法是首选形态 |
| 实体 | 工厂负责为实体分配身份标识 |
| 仓储 | 仓储负责"重建"已有聚合,工厂负责"新建"聚合 |
| 领域服务 | 创建涉及多个聚合协作时,常以领域服务形式出现工厂 |
核心:把复杂的对象 / 聚合创建过程从领域模型中独立出来,同时保证创建时的业务不变量。
实践:
关键:工厂的名字应表达业务意图,工厂必须保证聚合创建即合法。