赵玉伟的博客

领域驱动先决条件

理想和现实之间有一道鸿沟, 要想彼此互通,需要在中间支一个保持平衡的天平。

两本书

领域驱动由Eric Evans在2004年首次提出, 当前市面上可以通过两本比较经典的书进行学习。
一本是<<领域驱动设计:软件核心复杂性应对之道>>,这本书比较通俗易懂,前半部分是通过具体的例子讲解具体的概念,后半部分主要是通过具体的例子,通过持续的重构,保持模型的新鲜度。
另一本是<<实现领域驱动设计>>,这一本很抽象,内容上大而全,偏重理论。 建议阅读的顺序是先读第一本再读第二本。

三个概念

说下我对领域驱动设计的认知。
首先一个事物的存在要有其自身的意义。在弄清楚领域驱动设计的意义之前, 先明确三个概念。

1、什么是领域?

领域描述的是一个业务范围, 在这个业务范围内, 会存在各种各样的问题, 也可以称做问题域。 比如,我们说电商领域、 邮件领域、 医疗领域、 金融领域,在这些领域中, 我们所面临的问题是不一样的, 但是每种问题不会超出自己所在的领域, 所以, 领域限制了问题的范围, 比如,电商中的浏览商品不会出现在金融领域中, 那么解决该问题的方式也就仅仅被限制在电商的范围之内。 在这些大的领域范围之内, 还需要细分各种子域, 达到将问题“分门别类”的目的。

2、什么是驱动?

A驱动B的意思是, A的作用会导致B的状态产生变化。 比如发动机驱动汽车前进, 在这句话中强调前者所起的作用,强调要以A为主体。那么领域驱动设计这句话,强调的是领域的重要性。我们把领域划分好后, 只是提供了一个感性的问题空间, 那么在某个特定的领域中,领域内具体包含什么呢? 答案是一个又一个的模型。 也就是要对领域内的现实进行建模。比如,对某个人建模、对某件物体建模、对某个动作建模、对某个事件建模,等等。 所以, 领域会产生领域内的问题模型、 然后,用在模型的基础上进行设计, 设计是围绕模型展开的。 领域中的最重要、最基本的概念是: 模型、模型、还是模型。 领域驱动设计约等于模型驱动设计。

3、什么是设计?

设计指的是解决问题的某种思路, 也就是要通过怎样的一种方式,解决怎样的一种问题。 我们对领域内的“现实”建模后,这些模型需要相互作用,一起解决我们现实的问题。 比如,要解决下单这个动作、支付这个动作时, 需要相关联模型相互作用,一起完成, 这就是设计。 模型与设计密不可分。

明白了三个概念,再来看引入领域驱动设计的价值? 领域是对现实的高度模拟;领域能够合理的分割现实,降低问题的复杂度; 合理的建模能够适应业务的扩展性, 等等。

其他概念

领域专家

领域强调了业务的理解,不强调技术实现。 所以,有领域专家这个角色。 所谓领域专家,是指对某一个特定的领域范围内, 对这个领域内所面临的 业务问题非常熟悉, 一个领域专家可以是一个在这个行业中做了很多年的业务人员、开发人员、运营人员等,最重要的是要熟悉这个领域内的问题以及问题发生的场景。在实际的环境中,产品经理应该充当领域专家的角色,产品经理的职责不是给出一个交互的设计草图, 最重要的职责是明白各种场景, 以及在场景下面临的业务问题,能够正确给出解决问题的业务手段,不是为了解决问题而制造新的问题; 另外要挖掘业务概念, 以及概念的关系, 让这些概念作用起来形成一个整体,也可以称为闭环。

统一语言

开发人员要解决问题,需要要建立模型,那么就需要和领域专家进行沟通,找出业务中的问题。 开发人员与领域专家沟通时, 要统一语言, 也就是用双方都能够理解的语法进行对话(这个太理想,不符合国人的沟通方式), 统一语言要做到简练, 尽可能达到像机器人一样说话。类似于:A对B做了C,A执行B。 有没有看出一个点端倪, 统一语言直接点说,就是用领域中明确的概念填充几个固定的语法结构, 所以,领域概念很重要。 当然, 一个人是领域专家同时又是开发人员的话,这一步会更加容易。 统一语言的形成,就是业务梳理的过程,这一步无论何时,无论在哪个场景下, 都非常重要。 有下面的关系: 领域产生概念, 概念产生模型。

分层

实施领域驱动的分层架构如下:


最重要的一点是存在领域层,领域层是我们设计的重点。 领域层的实体是充血模型, 和三层架构的贫血模型对立。最古老的三层架构对于小型的业务, focus在CRUD其实问题不是很大; 如果实施在较复杂的业务中, 会有庞大的service, 此时,我们的业务对象(各种bean)仅仅充当数据结构的角色, 其实就是完全面向过程的开发,导致的结果是大量重复,各种奇怪的依赖。 有时候,需要操作数据库一个简单的sql语句(比如,create),在这种严格的三层限制性(上层只能调用最紧密的下层,不能跨层调用),只能是controller调用service,service调用dao,我觉得是多余的方法包装; 改成四层架构后,这样的操作相对会变的少很多,不会完全杜绝,判断的一个依据是一个create的sql语句,是不是一个完整的业务。 领域变的丰富之后,尤其是聚合根的出现,导致一个create会牵带出其他的操作,这样,单纯的create操作会变的少很多。 四层架构让领域层变的丰富, 对象除了有属性外, 还有了行为,当然,如果是一个实体, 也具备了生命周期,这是一个很大的转变,有生命周期就意味着要有操作生命周期的方法;这样可以把以前大量的针对bean的操作从service中拿到bean中,重复减少了,依赖也从service 依赖 service 变成 service 依赖 bean; 同时, 因为又多了领域服务, 所以,针对领域间的操作, 又可以从最初的service拿到领域层的service中(此时的service是泛指, 每一层都可以有自己的service),这样,最初的service仅仅起到了调度和控制事务的作用, 控制整个事务,调度各个bean、调度各个service、调度各个repository; 最初的service完成了瘦身, 那么,最初的service实际上变成了四层架构的应用层,而最初的controller层还是controller层, 和user interface层相对应。

输出

产生概念后,如何表达设计? 答案是各种设计图。 设计图没有统一的标准, 因为每个领域面临的问题不一样,所以,表达方式也不一样, 最终的目的是大家都能基本理解。 比如, 我们可以用一条线段描述从A点到B点的过程; 可以用一个饼图描述一个人资产的分配比例等等。 最终, 这些设计图,配合UML图和流程图、加上针对性的说明,一起表达我们的设计。 如果完全用UML图表示导致太过复杂,UML最好有针对性的表达某个业务场景。

落实

一个项目,完全按照领域驱动实现,在当前环境下不太现实。原因:
成本。领域驱动以模型为基础实现业务,模型要做到准确,要准确的表达, 要有高质量的领域专家和开发人员, 实际上统一语言就很难做到。
团队。团队需要对模型达成高度一致的认知, 反复沟通达成一致, 一是沟通的成本太高,而是团队的成员必须要对领域驱动的认知达到一定的层级。
持续重构。重构很危险,对于访问量非常大的服务, 重构的项目不亚于新启一个项目。 投入的人力成本太高,而且,尤其是项目把控, 不过可以小范围的持续重构。
持续性。 好的开始并不等于成功的一半, 即使通过领域驱动作为开端, 后续人员变动的话, 新的人员能否达到认知的一致是个问号,其实还是团队的问题。
业务复杂性。 领域驱动是解决复杂性的有效方案,但是业务的复杂性缺却很难完全用领域驱动落地, 因为领域驱动的工具箱中,限制了必需把实际的业务建模成固定的几种类型(聚合、值对象、实体、工厂、 服务等),也就是对领域建模实际上是严格的建模方式, 把实际转换成这种业务模型的过程是非常曲折的, 工作中不可规避的“惰性”会以偏重结果为出发点而忽略了模型的严谨性。

不过,我们可以借鉴领域驱动的思想,划分领域,拆解业务, 充实领域模型, 在现有技术框架上实现部分模型的设计。 在纯粹数据驱动的贫血模型和业务驱动的充血模型之间建立一个平衡点, 让理论在我们的实际项目和环境中找到一个合理的落脚点, 让领域驱动服务于我们的项目。