本文将从软件架构角度,介绍DDD的分层架构、组成要素、领域对象生命周期管理等核心概念。
DDD(Domain Driven Design,领域驱动设计)核心是针对业务层建模的,但是为了保证业务层的相对独立性,DDD从最佳实践经验出发提出了下面的系统分层:
我们都知道分层结构的好处。在Martin Fowler的著名的《Patterns of Enterprise Application Architecture》一书中有专门一章讲分层(Layering)。上层单向调用下层,下层不应该不知道上层;下层可以通过提供回调(callback,比如Observer模式、模板方法模式等)来注入上层的逻辑,但是下层还是不知道上层。如上面的DDD分层架构图。
各层的基本职责如下:
-
UI层:与常用的Presentation Tier类似,负责与客户端交互、展示界面等。上图也表示了UI层可以调用下面的任一层。
-
Application层:相当于一个Facade,是薄的一层。定义软件要做的任务,协调和组织(coordinate)完成用户请求需要的作业(tasks)并委托(delegate)给Domain层的调用。没有业务逻辑(rules, knowledge)在里面。功能有点类似于企业级模式和J2EE模式Front Controller。
-
Domain层:业务软件的核心。负责表示业务概念,业务状况和业务规则。需要注意的是对业务状态的保存(持久化)的技术细节不属于这一层(不属于业务模型),而要委托给Infrastructure层。在该层内部,应该采用Model Driven Design。
-
Infrastructure层:顾名思义,该层负责提供上面各层需要的通用性服务,如发送消息(Messaging),领域对象的持久化,提供绘制UI所需的Widget,提供各层通信所需框架等。
有了上面宏观的分层框架之后,就可以进一步采用DDD的元素对一个初步的领域模型进行细化和实现了。
我们从基础的四个元素或说概念说起:Entity,Value Object, Module, Association。Entity用来表示有Identity(俗称ID)的领域对象。Value Object用来表示没有Identity的领域值对象。这些对象都带有属于领域对象的行为。Association表示他们之间的联系。Module也可以叫Package,用来封装概念上内聚的一组DDD元素,表达一个具体的领域概念,相当于Java中的package。
需要注意的是DDD中的Entity与常见的基于SSH框架所做的系统中的Entity的区别。后者往往称为贫血的业务对象(amemic domain object),因为它们只与持久化相关而没有所属的业务行为,只有getter/setter之类的基本方法。相应地那些业务行为移到了”Service”中。于是Service(或者ServiceImpl)中出现大量过程式(Procedural)的代码。所以贫血业务对象往往与过程式编程并存。这实际上是违背面向对象理念的。用面向对象的语言(Java,C#等)编写面向过程的软件,是很多程序员的通病。DDD所以被称作“OOP done right”,应该很大程度上取决于它还原了Service和领域对象的“真实面目”。
在DDD中,Service处理的是cross-cutting的操作,即不属于某一个领域对象,而属于某一个Aspect(切面)的操作,有点类似于AOP中的切面。Service可以存在与上面四层中的Application层,或者Domain层,或者Infrastructure层,粒度应该不粗也不细。在Adam Bien 的《Real-World Java EE Patterns – Rethinking best practices》一书中,认为Domain-Driven和Service-Oriented是矛盾的: “Domain-driven design and service-oriented design are opposite approaches”。另一方面,因为Service处理的是Cross-cutting的操作,它往往是重构的结果,而不是一开始就建立一堆Service来处理业务。业务的处理应该放在所属的对象里面,往往是Entity或者VO里。这才是面向对象。在处理复杂多变的业务需求时这种方式的优势将体现出来。
上面是模型元素Model Components,下面将一起认识生命周期要素Life Cycle Components:聚合Aggregates,工厂Factory,仓储Repository。
聚合Aggregate是数据变化的单位,是一组Entities,VO等组成。聚合有根和边界。外界访问聚合只能通过聚合根,只能持有聚合根的引用,不可以持有其他成员的引用。将数据变化用聚合来封装,可以保证数据一致性约束、不变量等复杂的业务规则得到良好的管理。
工厂Factory是用来封装聚合的创建细节。注意这里的工厂是概念性的,具体实现可以是工厂方法模式,Builder模式等。一般认为复杂对象的创建才用工厂。简单对象直接new就可以。
仓储Repository封装对象的存储和获取(想起了Spring的XXRepository)。将对象的创建委托给工厂。事务控制应该交给调用Repository的client。
其他概念如Bounded Context等是与集成有关的,暂不介绍了。
以上又走马观花地review了一遍DDD的重要概念。希望能给读者一个基本的印象。对DDD来说,也许理解起来很容易(只要对设计模式、企业级架构有一些经验),但实践起来很困难,需要不断摸索。但是早点了解DDD中体现的面向对象的设计思想,对于提高自己的代码质量和架构质量总是有好处的。
原创文章,作者:3628473679,如若转载,请注明出处:https://blog.ytso.com/190854.html