article.read --id=159

领域驱动设计:让代码说业务的语言

// published: 2025-07-21

领域驱动设计(DDD)不是一种技术,而是一种思维方式。它告诉我们:软件的核心是业务逻辑,而不是数据库、框架或技术栈。好的软件应该反映业务的本质,用业务的语言表达业务的逻辑。DDD的核心理念是:将复杂的业务领域分解为多个子域,每个子域有自己的模型和边界,通过统一语言连接业务和技术。这种思维方式让软件更贴近业务,更容易理解,更容易维护。DDD不是银弹,但它提供了一套系统的方法论,帮助我们应对复杂的业务系统。

DDD的核心概念是限界上下文(Bounded Context)。一个复杂的业务系统包含多个子域,每个子域有自己的模型和语言。在电商系统中,"订单"在订单上下文中是一个包含商品、价格、地址的实体;在物流上下文中,"订单"是一个包含收货地址、物流状态的实体;在财务上下文中,"订单"是一个包含金额、支付状态的实体。同一个概念在不同的上下文中有不同的含义,这是正常的。DDD告诉我们:不要试图建立一个统一的模型,而是在每个上下文中建立最适合该上下文的模型。上下文之间通过明确的边界和接口通信,避免模型的混乱。

Uber在其业务系统中深度应用了DDD。Uber的业务非常复杂:乘客叫车、司机接单、行程计价、支付结算、评价反馈,每个环节都有复杂的业务逻辑。Uber将系统划分为多个限界上下文:行程上下文负责管理行程的生命周期,定价上下文负责计算行程价格,支付上下文负责处理支付,评价上下文负责管理用户评价。每个上下文有自己的团队、自己的代码库、自己的数据库。上下文之间通过事件通信:行程结束时发布"行程结束"事件,定价上下文订阅该事件计算价格,支付上下文订阅该事件发起扣款。这种架构让Uber能够快速迭代,不同的团队可以独立开发和部署,不会相互干扰。

DDD强调统一语言(Ubiquitous Language)。开发人员和业务人员应该使用相同的语言讨论业务,代码中的类名、方法名应该直接反映业务概念。不要用技术术语掩盖业务逻辑,不要让业务人员看不懂代码。当业务人员说"用户下单",代码中就应该有一个"placeOrder"方法;当业务人员说"订单取消",代码中就应该有一个"cancelOrder"方法。统一语言让业务和技术无缝连接,减少沟通成本,降低理解难度。代码应该是可读的,不仅对开发人员可读,对业务人员也应该可读。

DDD区分实体(Entity)和值对象(Value Object)。实体有唯一标识,关注身份的连续性,如用户、订单;值对象没有唯一标识,关注属性的组合,如地址、金额。这种区分让模型更清晰:实体的相等性基于标识,值对象的相等性基于属性。DDD还引入了聚合(Aggregate)的概念:一组相关的实体和值对象形成一个聚合,聚合有一个根实体(Aggregate Root),外部只能通过根实体访问聚合内部。聚合保证了业务规则的一致性,是事务的边界。

DDD强调领域事件(Domain Event)。业务状态的变化应该表达为事件:订单创建、支付完成、商品发货。事件是业务的语言,是系统各部分通信的媒介。事件驱动架构和DDD是天然的搭档:DDD定义了业务事件,事件驱动架构实现了事件的传递和处理。通过事件,系统的各个部分松散耦合,可以独立演进。

DDD的实施需要团队的深度参与。它不是一个可以快速应用的框架,而是一种需要长期实践的方法论。团队需要深入理解业务,与业务人员密切合作,不断提炼领域模型。DDD的价值在于:它让软件真正反映业务,让代码成为业务知识的载体。当业务变化时,代码的修改是自然的、直观的,而不是痛苦的、混乱的。DDD是一种投资,短期内可能增加复杂性,但长期来看会大大降低维护成本。对于复杂的业务系统,DDD是值得的。

DDD的另一个重要概念是防腐层(Anti-Corruption Layer)。当需要与外部系统集成时,不要让外部系统的模型污染自己的领域模型。防腐层在两个系统之间建立一个翻译层,将外部系统的模型转换为自己的领域模型。这样,即使外部系统变化,也不会影响自己的核心业务逻辑。防腐层保护了领域模型的纯粹性,让系统更容易维护和演进。

DDD还强调仓储(Repository)模式。仓储是领域对象的集合,提供查询和持久化的接口。仓储隐藏了数据访问的细节,让领域层不需要关心数据是存储在数据库、缓存还是其他地方。这种抽象让领域模型更纯粹,也让数据访问更灵活。仓储不是简单的DAO,它是领域层的一部分,使用领域的语言,返回领域对象。

DDD的实践需要持续的重构。领域模型不是一次性设计出来的,而是在实践中不断提炼的。随着对业务理解的深入,模型会不断演进。重构是必要的,不要害怕重构。好的代码是重构出来的,好的模型也是重构出来的。DDD鼓励持续的改进,让代码始终反映最新的业务理解。

DDD的价值不仅在于技术层面,更在于思维层面。它让我们从业务的角度思考软件,让软件成为业务知识的载体。这种思维方式让软件更有生命力,更容易演进。DDD不是一蹴而就的,它需要团队的长期投入和持续实践。