Java中架构DDD:理解聚合、实体和值对象三种核心构造块
Java中架构DDD:理解聚合、实体和值对象三种核心构造块
一、Java中架构DDD:理解**聚合、**实体和值对象三种核心构造块
在领域驱动设计(DDD)的架构实践中,聚合(Aggregate)、**实体(Entity)和值对象(Value Object)**构成了业务系统的原子单元。如同化学元素构成物质世界,这三者以特定的组合方式构建出具有业务语义的领域模型。
二、聚合:业务一致性的守护者
1. 概念本质
聚合是一组强关联对象的逻辑边界,其核心职责是维护业务规则的完整性。例如在在线教育平台中,课程报名聚合可能包含:
- 报名记录(根实体)
- 学员信息(实体)
- 优惠券使用记录(值对象)
2. 关键特征
- 原子性操作:修改课程报名状态时,必须同步更新优惠券使用状态
- 访问控制:外部系统只能通过
Enrollment
聚合根操作内部对象 - 生命周期管理:删除报名记录时级联删除关联的支付凭证
3. 实现要点
// 课程报名聚合根
public class CourseEnrollment {
private EnrollmentId id;
private Student student;
private List<CouponUsage> coupons;
public void applyCoupon(Coupon coupon) {
if (coupon.isExpired()) {
throw new InvalidCouponException();
}
this.coupons.add(new CouponUsage(coupon));
}
// 封装状态变更
public void cancel() {
this.status = EnrollmentStatus.CANCELLED;
this.coupons.forEach(CouponUsage::revoke);
}
}
三、实体:业务身份的载体
1. 概念解析
实体是领域模型的身份单元,其核心特征是具有唯一标识。以医疗系统中的患者管理为例:
public class Patient {
private PatientId id; // 唯一标识
private String name;
private MedicalHistory history;
public void updateContact(ContactInfo newInfo) {
this.validatePhoneNumber(newInfo.phone());
this.contact = newInfo;
}
}
2. 设计原则
原则 | 说明 | 反例警示 |
---|---|---|
标识稳定性 | ID在生命周期内不可变 | 使用数据库自增ID暴露实现细节 |
行为内聚性 | 业务操作封装在实体内部 | 在Service中实现病历修改逻辑 |
状态完整性 | 保证关联对象的一致性 | 允许直接修改MedicalHistory属性 |
3. 标识策略对比
策略类型 | 优点 | 缺点 |
---|---|---|
自然标识(如身份证号) | 业务语义明确 | 可能泄露隐私信息 |
委派标识(UUID) | 实现解耦 | 可读性差 |
组合标识(区域+序列) | 支持分片 | 生成逻辑复杂 |
四、值对象:不可变的业务语义单元
1. 核心特征
2. 典型应用
案例一:医疗诊断报告
public record Diagnosis(
String diseaseCode,
String description,
LocalDateTime confirmedAt
) {
// 自动实现equals/hashCode
}
案例二:地理位置坐标
public class GeoLocation {
private final double latitude;
private final double longitude;
public GeoLocation(double lat, double lng) {
validateCoordinates(lat, lng);
this.latitude = lat;
this.longitude = lng;
}
// 没有setter方法
}
3. 设计陷阱规避
- 避免过度封装:将经纬度拆分为独立类会增加复杂度
- 警惕隐式转换:货币单位转换应显式声明
- 注意序列化:确保不可变对象能被持久化层正确处理
五、组合的艺术:三要素的协同效应
1. 电商订单系统的典型结构
public class Order {
// 聚合根
private OrderId id;
private Customer owner;
private List<OrderLine> items;
private PaymentDetail payment;
public void addItem(Product product, int quantity) {
items.add(new OrderLine(product, quantity));
recalculateTotal();
}
}
public class OrderLine {
// 实体
private ProductId productId;
private int quantity;
private Money unitPrice;
}
public record Money(
BigDecimal amount,
Currency currency
) {
// 值对象
public Money convertTo(Currency target) {
return exchangeService.convert(this, target);
}
}
2. 协作规则
- 聚合根负责维护
Order
与OrderLine
的一致性 - 实体
OrderLine
记录每个商品项的独立状态 - 值对象
Money
保证金额计算的准确性
六、实施路线图:从理论到实践
1. 识别阶段
步骤 | 工具 | 输出产物 |
---|---|---|
业务概念提取 | 事件风暴 | 领域术语表 |
模型边界划分 | 上下文映射图 | 限界上下文定义 |
构造块分类 | 四色建模法 | 实体/值对象清单 |
2. 实现阶段
- 建立基类体系
public abstract class Entity<T> {
protected T id;
// 相等性比较基于ID
}
public abstract class ValueObject {
// 相等性比较基于所有属性
}
- 应用设计模式
- 工厂模式:复杂对象的创建
- 规约模式:业务规则的可组合性
- 装饰器模式:扩展对象行为
- 持久化策略
@Entity
public class OrderJpaEntity {
@EmbeddedId
private OrderId id;
@ElementCollection
private List<OrderLineJpa> items;
}
@Embeddable
public class OrderLineJpa {
private ProductId productId;
private int quantity;
}
结语:构建有生命的软件系统
在DDD的世界观中,软件系统不应是冰冷的数据容器,而是充满业务智慧的有机体。通过合理运用聚合、实体和值对象:
- 聚合如同生物细胞,维护着内部环境的稳定
- 实体承载着业务个体的独特身份
- 值对象则像DNA片段,记录着本质特征
当我们将这些构造块以业务合理的方式组合,就能创造出真正理解业务、拥抱变化的软件系统。正如Eric Evans所说:“优秀的领域模型是深入理解业务的副产品。” 这或许就是DDD带给开发者最宝贵的启示。