领域模型应用

系统设计

任何系统设计都可以用schema表示,不管画什么设计图,其核心思路就是找层级和关系。所以任何系统都可以用树型结构表达!

用树型结构表达局限在于,树是个二维结构。系统可以有不同维度的树,有了不同维度的树后,单靠一颗树不足以描述复杂的业务形态。所以,就要找出哪些是维度是变的,哪些是不变的。

变的是什么

场景

在领域模型中叫context,对应spring里的也是context,context的核心思路是把各种概念划分出边界。

比如购物车与结算页

在购物车的计算优惠的逻辑与结算页几乎一样,但它们有差异,如果在代码中就描述这种差异,是否逻辑将会非常清楚

购物车context

购物车只计算商品与优惠券,优惠价,换购

结算页context

结算页还需要计算个人权益、用户选择优惠方案、口令等操作

两者计算核心逻辑用OrderContext来计算

为什么要用context来记录,用文档不行吗,最好的文档就是代码!

用context可以描述各种层级关系,也就是表达树模型!过度的context也会增加理解。所以,context也需要有层级归类来保证不过度熵增!当然给context一定的命名规则也可以减少理解难度。

流程

流程容易变,系统变化最大的就是流程,流程可以变顺序、变条件、变参数、变结构、变关系、变路径等。也正是因为流程的变动大,代码的实现以面向流程是最容易实现的。

面向流程最容易完成简单的工作,当面向非复杂的工程时,流程非常长且关系较为复杂时的流程变化时,就非常吃力。

就好比人吃饭这个需求时,面向流程的编程里,你还需要把饭怎么来的,怎么煮熟,饭有没有熟等许多条件都加进来判断,才能完成这个需求。

而面向对象编程,饭这个领域对象只要创建好就行了,至于饭是怎么来的,是蒸熟、煮熟、有没有熟都由饭这个领域对象自己判断,它有一套完整的流程来保证自己是熟的饭。在完成人吃饭的需求时,只需要关心吃饭这个流程即可!

定义

定义某个业务时,在某个时刻它是大家描述的样子,在业务发展到一定阶段后,它可能变了,这是对系统稳定性是非常大的挑战!

比如定义一品多供概念时

早期的定义是,一个商品由多个供应商供应,那么,定义这个商品时的价格,规格是不变的,变的只是供应商。

而现在的定义是,一品多供只是一个组ID,代表售卖时就按这个ID去售卖,其背后的价格与供应商本来就不一样。

语言的模糊性影响代码的实现

活动概念的模糊性

有效概念的模糊性

类型概念的模糊性

不容易变的是什么

领域模型

领域模型的定义是不容易变的。这里不变指概念不变,沟通时不变的概念。但是真的是不变的吗,不一定,比如活动的时间在某个阶段,它表示单纯的时间,在业务发展中,它需要表示动成态截单时间,会变成一个带id的对象

所以,不变的是时间概念不变,变的是随着业务的发展,它由属性变成了值对象或是领域,由领域拆成了系统等等。而活动的概念是不变的。

系统的成长自然就成了,领域对象的成长+新的领域对象的加入,系统这颗树也就长大了。

领域模型的层级关系不容易变

领域模型定义好了之后,各种层级关系也就定义好了,这是不容易变的,除非有重大的变动。否则,系统的稳定性就无从说起了。

营销工具定义好了之后,工具子类,如券、价、余额、换购等就定义好了。子类的层也就定好了,不允许再变了。

层级

层级不容易变。

代码层级不容易变,画的架构图不容易变,模板与实例的关系不容易变。

领域模型的层级不容易变,一般都是不停的增加层级关系,原来的关系一般不容易变。

场景的层级也不容易变,场景只会变多与变少,复用的场景不容易变。

为什么需要领域模型

领域模型的产生在某个阶段来说,是确定不变的,需求只是在各种场景用领域对象做组合完成它。

举例:我有一个工人施工队,可以提供建房的能力。那么工人就是这个领域的对象,建房所需要的所有资源就是场景上下文,建房这个动作就是建房场景中的功能。

当建完房子后,又接到一个修路的工程,那么工人还是领域对象,就有个修路的场景上下文来保证修路工程所需要的资源,修路这个动作就是修路场景中的功能。

所以,当你建好了领域模型后,能完成的场景业务就非常多了。各种场景对于领域模型来说,你只要保证场景所需要的资源到位,就保证能完成场景的功能。

找到这个规律后,所有的系统逻辑都可以按这个不变的模型去套!本质上就是找共性。

一切皆对象

当一切皆对象时,业务全是对象,各种层级的对象建立好后,本质上就是领域建模了,只需要完成模型与模型之间的关系。

过去总结的经验,任何业务流程拆解下来,只分为几步,适用于所有业务模型!

  1. 创建领域对象
  2. 创建业务场景
  3. 在场景中给领域对象建关系
  4. 在关系中做计算
  5. 在场景中组装计算结果

虽然许多简单流程可能不用这么多步骤,但无论多么简单的流程都可以用这几个步骤来走通!

关系

建关系时,领域对象自身有效性判断,能不能建关系又涉及到有效性校验等,建了关系是不是有效的关系等。关系建好时,业务都已经计算好了,只需要从场景对象的有效关系中获得结果就行了

所有的关系都可以用1对1来表示。

1对多的关系时,这就是系统领域拆分点。

商品与导购 1对N

营销工具与优惠券

关系有效性

场景 我们常说业务复杂,本质上就是场景多。如果场景全都梳理清楚了,所谓的复杂只是关系多!多不复杂,乱才复杂。

充血模型带来思考方式的转变!

思考角度 传统贫血模型(事务脚本) 充血模型(领域模型)
模型视角 实体只是数据容器(DTO) 实体拥有行为和业务逻辑
业务行为 写在 Service 里 写在 Entity 或 Domain Object 中
代码职责 Service 肥大、Entity 无脑 Entity 主动、Service 变薄
封装性 数据和行为分离,易出 bug 数据与行为绑定,增强一致性
过程驱动 谁来调用?怎么调用? 对象该做什么?它职责是什么?
可维护性 易失控、缺乏内聚性 高内聚、变化局部化
建模方式 结构导向:建表→写增删改查 语义导向:动词行为→建对象

充血模型改变的,不只是代码位置,而是建模哲学!

不破不立

继承会增加复杂性

继承的形态比组合的形态复杂

父类是不知道子类的类型

继承的引入一定会带来泛型,泛型是复杂代码的原罪之一

没有继承就没有父类,自然就没有抽象类,抽象类的实现只有在运行时才知道具体的实现类

不用map结构作为领域对象

对象比map有更明确的结构,也更容易扩展

数据表的设计没有map类型的表

领域的定义是明确的这点矛盾,map表达是模糊的,不确定性太强!

redis缓存只是缓存,减少依赖缓存数据结构。

现在许多系统都滥用redis,其本质redis数据结构简单,容易完成某些业务功能。而从设计的角度出发,redis数据结构不具备表达领域特性。

如需要redis表达领域特性,需要把对象序列化后保存到redis中,这样,redis就具备领域特性了。

当系统成长到一定规模时,不得不依赖redis的高性能数据结构时,才引入redis数据结构。

要明白redis定位是缓存,不是数据库。

领域模型的一个好处是带来思维方式的转换。你不用关心用哪种数据库,用哪个redis版本,用什么orm框架!在设计阶段,只需要关心设计的领域模型是否能满足业务。

只有模型设计好了,后续考虑如何存,如何缓存等真正技术上的事。软件工程容易做复杂的原因之一是过早的技术介入会限制系统设计。

技术上大胆突破,做大量减法

避免继承

没有泛型

不用map数据结构表示对象

避免引入设计模式(大量context来抽象表达)

避免复杂算法。算法可以用AI帮忙生成和用大量测试用例保证

缓存就是缓存,避免把缓存当数据库

使用轻量级ORM JPA

指标系统全新设计,去掉redis的lua脚本

避免分布式事务,避免分布式锁,依赖数据库事务

领域模型都是充血模型,所有context都是充血对象,保证各自完成自己领域内的事!

service层只做流程,不做领域层业务逻辑

做减法

忘记中间件,专注于领域模型。只有缓存,存储,事件概念

不执着于技术的先进性,业务才是技术发展的唯一源动力。效率才是解决成本的关键

业务体量对于系统设计的影响

系统以领域模型划分模块,不再以技术角度或流程拆分系统!

系统成长

用领域建模的关键点是对业务足够了解,如果是新业务,在不足够了解业务的前提下建模,容易推翻。个人经验会在建模时,最好走1步想3步,多考虑以后的业务发展。这就是系统成长带来的必然之痛,好的抽象归类是系统建模关键一步。

传统的系统很依赖数据的关联查询,因为有了数据库的关联查询,几乎不再考虑领域与领域之间的关系是如何连接的。自然而然,很多的重逻辑都用sql写完,本质上用sql写逻辑是屏蔽了领域之间的关联关系。当业务做大时,数据库不堪重负。当把业务剥离出数据库后,才有机会做缓存,做分布式!

引入领域模型后,做缓存与做业务会非常清晰,你只将需求拆解成领域模型的各种任务,然后用对应的context组织起来,用service层串流程就好了。

领域模型本质是将管理学应用到工程

领域对象一般指有唯一id的对象称之为领域对象。一切设计从领域对象定义好后开始。

领域对象就如同一个大型项目,需要把参与这个项目的人分成各种工种的人。大家协调合作来完成一个大型项目。领域对象就是来完成大型项目的人的抽象!

小项目是一个人就能全做了,当遇到庞大复杂的大型工程,一个人的能力是有限的,就需要多人协助完成。而人多了就需要分工,就涉及到对象职责、工作边界的问题。工程大了就涉及到各种场景应用问题,各种工具和各种工程承包等问题。这种问题也同样存在领域模型的设计中,所以就引入了各种领域对象,边界上下文,各种工具,各种拆分领域等概念来规范!

领域模型的本质是将管理学应用到工程项目中。而将管理学应用到工程中的典型就是spring。