跳到主要内容
版本:Next

DDD设计原则

本文档引用的文件

目录

  1. 引言
  2. 聚合根设计与职责边界
  3. 实体标识与生命周期管理
  4. 构造函数与属性保护机制
  5. 业务方法封装与一致性保障
  6. 值对象识别与提取建议
  7. 领域层与应用层解耦
  8. 领域服务与领域事件的潜在应用
  9. 违反DDD原则的风险示例
  10. 正确实现方式与最佳实践

引言

本项目采用领域驱动设计(DDD)作为核心架构原则,通过清晰的分层结构和领域模型设计,确保业务逻辑的高内聚与低耦合。MyEntityName作为核心聚合根,体现了DDD在实际项目中的典型应用。本文将深入分析其设计原理与实现细节。

聚合根设计与职责边界

MyEntityName实体继承自FullAuditedAggregateRoot<Guid>,表明其为一个具备完整审计功能的聚合根。作为聚合根,它承担了以下职责:

  • 一致性边界:所有对MyEntityName内部状态的修改必须通过其自身提供的方法进行,确保业务规则的一致性。
  • 唯一标识管理:使用Guid作为全局唯一标识符,避免主键冲突。
  • 生命周期控制:通过仓储(Repository)模式统一管理实体的创建、更新和删除操作。
  • 事务边界:聚合根是数据库事务的最小单位,所有变更应在同一事务中完成。

该设计确保了领域模型的完整性,防止外部对象直接修改内部状态导致数据不一致。

Section sources

实体标识与生命周期管理

MyEntityName使用Guid作为主键类型,继承自FullAuditedAggregateRoot<Guid>,自动包含创建时间、创建人、最后修改时间、最后修改人等审计字段。这种设计具有以下优势:

  • 全局唯一性Guid保证了分布式环境下的标识唯一性。
  • 不可变性:一旦生成,标识符不可更改,符合聚合根的基本原则。
  • 生命周期追踪:通过审计字段,可完整追踪实体的整个生命周期。

实体的生命周期由IMyEntityNameRepository统一管理,遵循标准的CRUD流程,确保所有变更都经过领域层的业务规则校验。

Section sources

构造函数与属性保护机制

MyEntityName定义了两个构造函数:一个受保护的无参构造函数用于ORM框架反序列化,另一个为带参数的构造函数用于业务创建。

所有属性均采用protected set访问控制,确保:

  • 外部无法直接修改属性值
  • 所有状态变更必须通过领域方法进行
  • 在赋值时可执行业务规则校验

例如,在构造函数中调用Check.NotNullOrWhiteSpace()进行参数验证,确保CodeName不为空且长度符合限制(由MyEntityNameConsts定义),从源头保障数据完整性。

Diagram sources

业务方法封装与一致性保障

MyEntityName通过封装业务方法确保规则一致性:

  • Update方法:更新实体信息时,重新执行与构造函数相同的校验逻辑,确保数据始终有效。
  • AdjustSort方法:专门用于调整排序值,避免直接暴露Sort属性的修改权限。
  • Clone方法:提供克隆能力,复用已有数据创建新实例,符合开闭原则。

这些方法将业务规则内聚于领域模型内部,避免应用层绕过规则直接操作数据。

Section sources

值对象识别与提取建议

当前代码中存在潜在的值对象可提取场景:

  • CodeNameRemark等字符串字段虽有长度限制,但目前作为普通属性存在。可考虑提取为IdentifierCodeDisplayName等值对象,封装格式校验与业务含义。
  • 排序逻辑涉及多个实体的相对顺序调整,SortOrder可作为值对象封装排序规则。

值对象的特点是:

  • 无唯一标识
  • 通过属性值判断相等性
  • 不可变性
  • 可跨聚合复用

提取值对象有助于进一步提升领域模型的表达力与复用性。

Section sources

领域层与应用层解耦

项目严格遵循分层架构,实现领域层与应用层解耦:

  • 领域层(Domain):包含MyEntityName实体、仓储接口、规约(Specification)等,专注业务逻辑。
  • 应用层(Application):MyEntityNameAppService仅 orchestrator 角色,调用领域对象和仓储,不包含核心业务规则。

例如,MyEntityNameAppService.CreateAsync方法中:

  1. 调用仓储检查名称唯一性
  2. 创建MyEntityName实例(触发构造函数校验)
  3. 持久化到数据库

核心校验逻辑仍在领域层,应用层仅协调流程,符合“将业务逻辑放在领域层”的DDD原则。

Diagram sources

领域服务与领域事件的潜在应用

尽管当前未实现,但存在领域服务与领域事件的应用场景:

  • 领域服务:当业务逻辑涉及多个聚合或外部系统时,应引入领域服务。例如批量调整排序涉及多个MyEntityName实例,可封装为MyEntityNameSortingService
  • 领域事件:当MyEntityName被创建或更新时,可发布MyEntityNameCreatedEtoMyEntityNameUpdatedEto事件,通知其他模块(如缓存更新、日志记录)。

Diagram sources

违反DDD原则的风险示例

若违反DDD原则,可能出现以下风险:

  • 贫血模型:若将校验逻辑移至应用层,领域对象退化为数据容器,失去业务含义。
  • 事务边界模糊:跨多个聚合直接操作数据,导致事务过大或数据不一致。
  • 重复代码:相同校验逻辑在多处出现,增加维护成本。
  • 测试困难:业务规则分散,难以进行单元测试。

例如,若允许直接设置Sort属性而不通过AdjustSort方法,则可能破坏排序业务规则。

正确实现方式与最佳实践

正确的DDD实现方式包括:

  1. 富领域模型:将业务规则封装在实体内部
  2. 聚合边界清晰:每个聚合有明确的边界和一致性规则
  3. 仓储抽象:通过接口隔离数据访问细节
  4. 分层职责明确:领域层专注业务,应用层专注流程
  5. 使用规约模式MyEntityNameSpecification封装查询条件,提高可读性与复用性

遵循这些实践可构建高内聚、低耦合、易于维护的领域模型,为系统长期演进奠定坚实基础。

Section sources