Appearance
DDD 概述
领域驱动设计由 Eric Evans 系统提出,核心思想是让软件模型围绕业务领域建立,而不是围绕数据库表、接口清单或技术框架建立。它强调开发团队和领域专家共同塑造模型,让业务语言进入代码,让边界和规则能被长期维护。
DDD 的重点不在“使用了多少战术概念”,而在模型是否能承载业务变化。一个订单系统如果把创建、支付、取消、发货、退款规则都写在同一个过程式服务里,短期能跑,长期会变成难以拆开的业务泥团。DDD 会把订单生命周期、支付事实、库存占用、履约动作拆到清晰边界中,再通过接口或事件协作。
战略设计
战略设计决定系统的大边界。它先把业务拆成子域,再为每个模型划定限界上下文。核心域要投入更多建模精力,因为它承载业务差异化;支撑域要服务核心流程,保持清晰稳定;通用域要避免重复建设,优先使用成熟方案或公共能力。
限界上下文是 DDD 中最重要的边界概念。一个模型只在自己的上下文中保持准确,跨出边界就要重新确认语义。订单上下文里的“客户”是购买人,会员上下文里的“客户”是账号和权益主体,风控上下文里的“客户”是风险画像对象。把三种含义塞进一个全局对象,会让模型变得沉重且脆弱。
上下文映射用于说明边界之间怎样协作。订单可以调用支付接口完成支付,也可以发布 OrderPaid 事件让库存和履约继续处理。遇到外部系统模型混乱或语义不一致时,可以建立防腐层,把外部数据翻译成内部模型能理解的语言。
战术设计
战术设计决定上下文内部怎样写模型。实体用于表达有身份和生命周期的对象,值对象用于表达不可变的描述,聚合用于保护一致性边界,聚合根用于控制外部访问。领域服务承载跨对象但仍属于业务的规则,仓储负责以聚合为单位保存和取回,领域事件表达已经发生的业务事实。
这些概念要服务业务规则,而不是反过来支配设计。订单聚合需要保护“已支付订单不能随意修改订单项”“订单总金额等于订单项小计之和”这类规则;金额值对象需要保护“金额不能为负、币种不能为空”这类规则;领域事件需要表达“订单已支付”这种事实,而不是把技术动作包装成事件。
如果一个系统只有实体类、仓储接口和领域服务类,但核心规则仍然写在应用服务的大方法里,它只是换了目录结构。真正的 DDD 模型应让业务行为靠近业务对象,让规则有明确位置,让测试可以直接围绕领域对象验证。
与架构的关系
DDD 可以用于单体,也可以用于微服务。微服务不是 DDD 的前置条件,DDD 也不是微服务的装饰层。很多系统更适合先做模块化单体,把限界上下文和聚合边界梳理清楚,再根据部署、团队协作、伸缩和可靠性需求拆成独立服务。
在代码结构上,DDD 常和分层架构、六边形架构、整洁架构一起出现。应用层编排用例,领域层承载业务规则,基础设施层处理数据库、消息队列、缓存、第三方接口等技术细节。关键依赖方向是领域层不被基础设施层牵着走,领域对象不直接依赖 ORM、HTTP 客户端或消息 SDK。
落地方式
落地 DDD 可以从一个核心流程开始,而不是一次改造全系统。先用事件风暴梳理业务时间线,找出“订单已创建”“支付已完成”“库存已锁定”这类关键事实;再确认这些事实分别属于哪个上下文;随后在上下文内部识别聚合和不变量;最后把领域行为落到代码,并用测试保护规则。
改造遗留系统时,不要急着移动所有目录。更稳的方式是先把一个用例里的业务判断从应用服务和 SQL 中提出来,放进实体、值对象或领域服务;再把跨模块调用改成明确接口或事件;最后逐步收拢命名和边界。每一步都应能减少规则散落,而不是只增加抽象层。
判断标准
DDD 是否发挥作用,可以看代码能否回答业务问题。订单为什么不能取消,应该能在订单模型或相关领域规则中找到;支付完成后谁负责锁库存,应该能通过事件和上下文边界说明;新增退款规则时,应该知道它属于订单、支付、结算还是售后。
当模型能解释边界、状态、规则和协作方式时,DDD 才真正进入系统。否则,它只是把原有过程式代码搬进一组听起来更专业的类名里。
