05 领域建模实践(上):怎样既准确又深刻地理解业务知识?
你好,我是钟敬。
上节课咱们完成了事件风暴,梳理了系统的行为需求。但你可能也发现了,其实还有些微妙的业务概念还没有澄清,这就要靠领域建模来完成了。
建立领域模型是DDD的核心。要建好领域建模,需要理论和实践相结合。由于我们的模型有一定的复杂性,所以我把领域建模的实践分成两节课。完成实践以后,我们会再用一节课,从理论层面让你进一步深化对领域建模的理解。
今天这节课,我们先通过租户、组织和员工这几个部分学会基础的建模方法。
领域建模中的一些基本概念
我们先来理清领域建模中的一些基本概念,方便你理解下面的建模实践。领域建模主要有两个目的:
- 将知识可视化,准确、深刻地反映领域知识,并且在业务和技术人员之间达成一致;
- 指导系统的设计和编码,也就是说,领域模型应该能够比较容易地转化成数据库模式和代码实现。
而我们建立领域模型,主要是要识别领域对象(domain object),领域对象之间的关系,以及领域对象的关键属性,必要的时候还要将领域对象组织成模块。当然了,还有一些比较深入的内容,我们会在迭代二中再讲。
那么,什么是领域对象呢?我们系统中要处理的各种“事物”就是领域对象。比如说项目、员工、账户等等。这些对象都反映了名词性的概念。
其中,有些名词化了的动词也是领域对象。比如说我们进行了一笔支付操作,并且想把这笔操作记录下来。这时,“支付”也是领域对象。支付本来是动词,但这里实际上是要把一笔支付的信息记录下来,在这里就把“支付”当名词用了。
领域模型是用领域模型图来表达的,通常用UML来画。UML是“统一建模语言”的意思,英文是Unified Modeling Language,是面向对象建模的国际标准。其中,领域对象用下面这个符号来表示:
这个符号表示“员工”对象。其中第一栏是领域对象的名称,第二栏列出了对象的属性(attribute),姓名、性别都是员工的属性。
严格地说,在UML中,这个符号叫做“类”(class)。比如说,张三是员工,李四也是员工,我们可以说,员工指一类事物。这时我们可以用UML中的术语说,员工是领域对象的一个类,张三和李四是这个类的实例。在领域建模过程中,我们说领域对象时,有时指类,有时指实例,一般可以通过上下文来区分。
此外,DDD中将领域对象又分成实体(entity)和值对象(value object)。值对象我们等到第二个迭代再讲,这个迭代我们只关心实体。我们前面说的“员工”“账户”等都是实体。
由类和他们之间的关系组成的图叫做类图,这也是领域建模里用到的最主要的图。
下面我们就开始通过画类图的方式进行领域建模。同样,你扮演架构师,我扮演产品经理。
初步识别实体
我们可以从上节课中识别的领域名词入手,分成几部分来建模。我们先考虑租户、组织和人员。上节课的图是这样的:
首先,你可以先假定每个领域名词都是一个实体,把它们用类的符号画出来。如下图:
这就可以算作一张最简单的类图了。你可能注意到了,这里并没有写属性。其实,在领域建模阶段,我们主要关注的是实体和它们之间的关系。如果实体的名字已经能清晰说明实体的含义,那我们就不需要加属性了。如果名字还不足以充分表达含义,我们可以写几个关键属性,来辅助说明。
另外要注意,我们这里只是简单粗暴地假定了领域名词就是实体。通过后面的分析,我们还会发现,有些名词不是实体,有些要转换成其他形式。
识别“一对一”关联
现在,我们来识别实体之间的关系。先来看看租户和企业。
首先,你问我:“租户和企业这两个概念有没有关系呢?”我回答说:“肯定是有关系的。”于是你可以在它们两个之间画一条线,表示它们之间有关系。
然后,你又问了我两个有关业务的问题。第一个问题是:“一个租户最多可以对应几个企业?”我回答说:“只能对应一个企业。”于是,你在企业那一端写了一个 “1” 来表示。
然后你反过来问第二个问题:“一个企业可以作为几个租户?”我回答说:“一个企业也只能作为一个租户。”于是你在另一边也写上 “1” 。
这时,我们可以说,租户和企业具有一对一的关系。
这里的两个 “1” ,在UML中称为多重性(multiplicity)。那么,这种关系整体上呢,在UML的术语里叫做“关联”(association)。后面我们都用这种严格的说法,说成一对一关联。
这时你可能又想到了一个问题:“为什么不把租户和企业合并成一个概念呢?”
在有些情况下,一对一的两个实体确实是可以合并的。这取决于这两个概念的关注点是否相同。
但在我们的需求里,租户关注的是客户和提供云应用的供应商之间的协议,背后隐含的需求可能是云平台要为这个客户分配多少硬件资源、怎样收费、提供哪个级别的备份等需求。假如这不是一个基于SaaS的应用,根本不会有租户的概念。
而另一方面,企业是组织结构管理中的一个概念,即使不基于SaaS,企业这个概念也存在。所以这是两个不同关注点的概念,不应该合并。
识别“一对多”关联
下面我们再分析企业和开发中心的关联关系。这时你同样考虑了两个问题。首先,一个开发中心可以属于多少个企业呢?只能属于一个企业。这和上面的画法相同。
第二个问题是,一个企业可以有多少个开发中心?答案是可以有很多个。这可以在开发中心一端写一个 “*” 来表达。
这时,我们可以说,企业和开发中心具有一对多关联。
接着你用同样的方法画出了开发组,如下图:
现在咱们来考虑部门。你可能会发现,部门这个词其实用得不太准确,因为开发组也可以认为是部门。其实按照这里的需求我们想表达的是财务部、人事部等区别于开发中心和开发组的部门。
然后你又问我了:“在业务上,怎么称呼这种区别于开发中心的部门呢?”我说:“业务上一般叫做直属部门。”于是你在图中增加了直属部门。
然后,你把员工也加上了。
就这个图的含义而言,一个员工可以属于开发组,也可以属于直属部门,好像已经满足了需求,但是仔细想一下,你可能就会发现两个问题。
第一个问题是,如果将来组织层级发生变化,比如说在开发中心和开发组之间又增加了一层开发团队;或者有些开发组不属于任何开发中心,而是直属企业,那么这个模型就要修改了。也就是说,这个模型不容易适应组织层级的变化。
第二个问题是,一个员工其实可以不属于开发组,而只属于开发中心,比如开发中心的主管就是这样。同理,企业总经理也只属于企业本身而不属于任何下属部门。那么为了表达这种关系,我们就要再增加两条表示关联的线。
线越多,图就越杂乱。也就是说,这个模型不够简洁。
那么怎么解决这两个问题呢?我们要对这个模型进行抽象。
进行抽象
你可能已经注意到了,企业、开发中心、开发组、直属部门,其实都是组织结构中的节点而已,从这一点来说,他们是有共性的。
于是,你问我:“既然它们有共性,能否起一个统一的名字呢?也就是说,企业、开发中心、开发组、直属部门,可以统称为什么呢?”我回答说:“在业务上可以统称为组织。”所以你把模型改成下面这个样子:
由于开发中心、开发组等都是组织,所以只画出组织就可以了,模型图变得很简洁。但是你马上就发现,无法区分出一个组织到底是开发组还是开发中心了,也就是归纳了共性,但个性却丢了。
这时你问我:“一个组织是开发中心,另一个是开发组,那么在业务术语上可以说,这两个组织具有不同的‘什么’呢?”我告诉你:“可以说,这两个组织具有不同的组织类别。”
于是你画出下面的模型图。
也就是在这个模型中增加了一个组织类别的实体。企业、开发中心、直属部门等都是组织类别的实例。一个组织类别下可以有多个组织,而一个组织只能属于一个组织类别。比如说,开发组这个组织类别,下面可以有开发一组、开发二组等等很多具体的组织。
为了怕以后读这个模型图的人不理解什么是组织类别,我们还可以加一个注释,用来举例说明有哪些组织类别。在UML中,注释用折角的矩形表示,和被注释的实体之间用虚线连接,如下图:
识别“自关联”
然后,你又发现,这个图还不能表示企业、开发中心、开发组等之间的上下级关系。这可以用组织这个实体上的“自关联”来表达。画出来是下面这个样子:
在这个图中,组织实体上有一个自己到自己的一对多关联。这个关联翻译成自然语言可以这么说:一个组织可以有多个组织作为自己的下级;而一个组织只能有一个组织作为自己的上级。这样就表现出了上下级的层级关系。这种一对多的自关联,实际上表达的是一种树形结构。
另外,在这个自关联的两端,有上级和下级两个词。它们在UML里称为“角色”(role)。也就是说,在这个关联的 “1” 端的组织充当上级这个角色,在另一端充当下级角色。如果没有这两个角色名称的话,我们就不知道是一个上级有多个下级,还是一个下级有多个上级了。
增加“约束”
另外,为了说明“一个开发中心下面有多个开发组,而不是一个开发组下面有多个开发中心”这个业务规则,我又另外加了一个注释。如下图:
你可能发现了,这个注释和上一个有些区别,它的内容用大括号括起来了。在UML中,用大括号括起来的内容称为“约束”(constraint)。和一般性的注释不同,凡是约束,必须在程序中的某个地方进行实现。约束也是一种业务规则,应该加进前面讲过的业务规则表。
不过,细心的你又发现,关于租户还有两个问题没有在这个模型中表达出来。
第一个问题是,没有说明“只有企业才能作为租户,其他类别的组织不能作为租户”;另一个问题是,作为多租户系统,其实每一个实体都应该与租户有一个关联,说明这个实体是属于哪一个租户的,这样才能把不同租户的数据区分开。但是,如果把这些关联都画出来,模型图中的线条就会太多了,变得非常混乱。
于是,你又添加了一个注释和一个约束来说明上面两个问题。有了这个注释,租户和组织上原来的那个关联也可以省去了。如下图:
你看,这个模型既简洁又灵活,解决了我们前面说的员工实体上有多个关联以及组织层级难以扩展这两个问题。
识别“多对多”关联
你问我:“管理员和人事人员其实也是员工是吧?”我说:“是。”但这时你可能会纠结,这两个东西到底是不是实体呢?
于是你又问我:“从业务的角度,可不可以认为管理员和人事人员其实都是员工担任的岗位呢?”我说:“这个理解很正确。”于是,你就画出了下面的图:
这个图表示,一个员工可以担任多个岗位,而一个岗位也可以有多个员工担任。员工和岗位之间是“多对多”关联。我表示,这完全符合我对业务的理解。
更丰富的“多重性”
现在,我们再看一下多重性问题。前面说过,关联两端的 “1” 或者 “*” 称为“多重性”。比如说下图中组织和员工之间就是一对多关联:
你还可以问两个问题,把多重性进一步细化。
第一个问题是:“一个组织可以没有任何员工吗?”我回答:“业务上允许先建立一个组织,但暂时不往里面分配任何员工。”那么,你可以在员工那一端的 “*” 前面加上 “0..”,变成 “0..*” 。如下图:
这里的 “0..*” 我们也可以这样理解:一个组织最少有0个员工,最多可以有很多员工。
类似地,你可以从关联的另一个方向再问第二个问题:“一个员工可以不属于任何组织吗?”我回答:“不行。一个员工必须属于一个组织。”于是,你把另一端改成了 “1..1” 。
这里的 “1..1” 表示,一个员工最少要属于一个组织,最多也只能属于一个组织。
通过上面的方法,多重性的语义就变得更加丰富了。在实际项目中,团队可以自行决定,把多重性识别得粗一点,只写 “1”和 “*” ;还是细一点,识别出 “1..1” “0..*” 等等。一般来说,如果目的是在短时间内大致了解业务概念,就可以做粗一点;如果是为了指导具体的开发,则可以做细一点。在后面的例子中,我们都按照比较细的方法来做。
丰富了多重性以后,整个模型成为了下面的样子:
现在,我们终于完成了关于租户、组织和员工的领域模型。这节课就先到这,我们下节课再完成领域模型的其他部分。
总结
下面我们总结一下这节课的内容。
我们今天首先解释了用于领域建模的几个基本概念,包括UML、领域对象(domain object)、实体(entity)、类(class)、实例(instance)等。
然后我们从事件风暴识别出的领域名词出发,开始进行领域建模。首先假定每个领域名词都是一个实体。然后识别实体之间的关联。关联可以分为三种:一对一、一对多和多对多。而这些不同的关联,可以用多重性来表达。
假设有A和B两类实体,你可以通过问四个问题,来把多重性搞清楚:
- 一个实体A最多可以对应多少个实体B?
- 一个实体A最少可以对应多少个实体B?
- 一个实体B最多可以对应多少个实体A?
- 一个实体B最少可以对应多少个实体A?
这些问题看起来很“机械”,但对于初学者来说,关联关系是非常容易搞错的。所以如果你还不是很熟练的话,建议你老老实实地问自己这四个问题,一边问,一边把多重性写出来,这样可以保证不出错。
还有一种关联是实体自身上的“自关联”,可以表达由某种实体组成的树状或网状结构。这比一般的关联稍微难理解一些,但熟练以后就容易了。
在建模过程中,我们还可以通过“抽象”,找出领域名词中并没有直接揭示出来的实体。例如把企业、开发中心等抽象成组织和组织类别,把管理员、人事人员等抽象成岗位。这样的模型更能反映出业务的本质,从而更加灵活。通过这种抽象的过程,使模型和业务不仅仅是“形似”,更是“神似”。
我们还可以通过增加注释和约束,使模型中的业务知识更加丰富和准确。其中,约束是一种特殊的注释,它的内容必须以某种形式在代码或数据库中实现。约束也属于我们在之前说的业务规则,需要补充到业务规则表中去。
思考题
1.为什么领域建模要由业务人员和技术人员一起协作完成呢?
2.通过抽象思维,可以抓住需求的本质,达到“神似”,你可以在遇到过的实际项目中,举出类似的例子吗?
好,今天的课程结束了,有什么问题欢迎在评论区留言,下节课,我们继续进行领域建模的实践。
- 李威 👍(61) 💬(6)
为什么领域建模要由业务人员和技术人员一起协作完成呢? 1、知识传递: 技术同学说:我没太懂你到底想要个啥玩意儿 产品同学说:好,让我跟你再细说说 2、知识提炼: 技术同学说:你讲的我大概都明白了,但是就感觉有些凌乱,东扯一下,西扯一下,还是有点迷糊 产品同学说:好,那我们再来梳理一下,有些点确实可以归归类啥的,可能更方便理解 3、可视化: 技术同学:嗯,你的需求我基本是了解了,要不我把我的理解画出来,跟你确认一下? 产品同学:好主意,那咱们一起来搞吧。 4、认知对齐: 技术同学:你看我画的这个,是那个意思哈,确定哈。 还有这个这个,是那个那个意思哈。 产品同学:对的对的,这个是对的。 不过,那个可能稍微再调整一下更符合实情业务情况。 5、统一语言: 技术同学:看来,咱们画这一波图,已经把需求的要点都囊括了哈。只是有些概念我感觉还是有点模棱两可,要不咱们再对一下,把这些概念的叫法给定死咯,这样我写代码的时候,也方便取名字。 产品同学:要得要得,我们再拉一个表吧,把所有的业务概念再罗列一遍,包括中文表达和对应的英文表达都敲定一下,以后咱们就以这个术语表为准来沟通。
2022-12-21 - 超斌hello 👍(9) 💬(1)
这一课主要讲了模型及各自之间的关系 问题: 1. 业务和技术需要当前的模型结构是否适合未来, 对于电商的订单业务,刚开始时,我们是一个订单只做一个配货单,增加了下订单时要按仓库拆单,复杂度有点麻烦;为了保证前端用户下单的TPS,我们做了妥协,使用一个订单多个配货单处理,不影响前端效率,整体的方案如下图: https://www.processon.com/view/link/639a6faa1e085317e09dde02
2022-12-15 - Jxin 👍(18) 💬(3)
1.两个角色在一起协作产出,是因为两个角色共同参与是必要条件(不然,单干效率最高)。为什么会是必要条件,因为我们假定业务知识只在业务人员脑子里,领域建模知识只在技术人员脑子里,我们要得出某块领域知识,用领域建模做的模型图就必须要整合这两块知识。两者谁更核心些,业务知识更核心些,毕竟建模这个事有各种方式,只要能高效传递知识都是好方式。 2.租户的概念很特别,是否放到领域建模需要斟酌。毕竟租户来源于saas,saas来源于客户对产品的使用方式。但不是所有的软件都需要产品化(我就搞个功能集的小软件,不做限定),不是所有产品化都采用saas(也肯能是专项交付)。 3.多对多都可以拆成两个1对多,而且最好拆掉,拆时时常能发现一些隐式概念。 以上边为例,“员工”-**-“岗位” 中间可以加个 “编制”,“员工”-1*-“编制”-*1-“岗位”。 "编织"可以挂组织,可以挂项目,取决于交付模式。 4.注释和约束还是看情况加吧,抽象模型目的是聚焦核心方便沟通,铺得越详细也就越失焦。把注释和约束交给故事卡背景/AC或则PRD,也是一种方式。(为什么有这个想法, 如果我抽象的模型需要一直改,哪很可能是抽象得并不好《还是太具象了》,比如文中的“开发中心”“直属部门”。uml图也是这个思路,加注释和约束后,要改的机会会显著增加)
2022-12-27 - 老狗 👍(12) 💬(3)
1. 实际上DDD针对的复杂软件是个铁匠和花匠一起参谋做除草机的事情,业务人员懂它要干嘛,但是不懂如何做,铁匠能打造工具,但不知道工具如何被使用更好。单铁匠只能做出他学过的工具,比如他学过如何制造锄头,单花匠只能吐槽手里的锄头不好用。所以,从另一方面,如果这种软件你学过怎么做(你知道抄谁并且各种分析都到位了)就不要花匠(业务人员了)可能那个花匠还没你懂,不过能告诉我们开发人员抄啥的好日子好像越来越少了。
2022-12-17 - Michael 👍(9) 💬(2)
这个模型有点像《分析模式》第一节里面的模型。我比较好奇的是我们怎样去发现需要抽象的地方,就像例子里,为什么我们需要考虑组织层级变化这个点?其实也可以有其他的点,比如以后我希望企业和租户是多对一的关系,就关于抽象组织层级这个好像也没有从需求里能看到蛛丝马迹,我们应该怎么训练这种能力呢?
2022-12-15 - 沐瑞Lynn 👍(3) 💬(3)
正好同时在复习八叉老师的另一门课,里面提到的用知识消化(Knowledge Crunching),“双关联一循环”,来提炼领域模型。 让业务人员和技术人员一起,建立统一语言(Unified Language),建立双关联,也可以加速完成循环。
2022-12-15 - Geek_31faab 👍(2) 💬(1)
我们的开发模式是产品提供原型,然后产品再根据原型给我们讲解需求。这类的我们还需要再进行事件风暴了吗?
2023-05-18 - 吴钢 👍(2) 💬(1)
你好,企业是非常特殊的组织,企业最好作为独立的实体,即使合并也应该和租户合并。否则按企业查询用户,或按用户确定企业都很麻烦。
2022-12-22 - leesper 👍(2) 💬(2)
领域建模由业务人员和技术人员共同完成,是因为技术人员开发的软件,是技术人员从业务人员获得的知识在大脑中的反映,因此技术人员必须把业务人员脑子里的知识可视化出来,才能开发出满足需求的软件。 实际项目中,往往是一上来就开始建表,然后围绕表就各种CRUD,这样开发出来的面条式代码,往往没有表达出目标问题背后那些不变的本质的东西,因此需求越复杂就越到后面越难以维护,写成了一坨巨大的屎山,程序员每天在屎山里搬屎
2022-12-17 - SMTCode 👍(2) 💬(1)
业务最终要落地到技术实现上,业务人员可能会把所有的业务场景都罗列出来,但不能从代码实现层面对相似业务的抽象;而技术人员需要把业务需求转成实实在在的代码,但只关注编程语言等的特性,不能整体把握业务的整体。通过业务人员和技术人员的共同头脑风暴,完成业务的抽象,提高技术人员对业务需求的整体认知,才能使用合适的语言特性实现业务的真实功能。这是一个打破业务与技术之间墙的非常高效的方法。能把这两者之间融合于一身的人,才能称为系统级架构师,但能做到的人少之又少。太好的内容,很厉害的老师。谢谢
2022-12-15 - aoe 👍(1) 💬(1)
AI 简化实体之间的关联关系 思路 ====== 1. 将模型图转换成 Mermaid 语法,便于 AI 处理 2. 简单描述核心需求,例如:尝试提取出抽象实体 3. 使用 coze 的 Prompt Optimize 功能优化提示词 4. 带入 Mermaid 模型图,查看 AI 处理结果 5. 根据 AI 处理结果,优化 Prompt 6. 多轮迭代后得到启发 结果 ====== GPT4 缩减到 5 个实体 智谱清言缩减到 3 个实体 智谱清言胜出,结果更加简洁,符合实际业务需求。 详见 https://wyyl1.com/post/28/practice1/
2024-04-14 - 瀚海 👍(1) 💬(2)
为啥感觉建模后,好复杂 😂
2023-07-17 - aoe 👍(1) 💬(1)
02 课被“分层”惊到了,我还不会分层 03、04 课被“事件风暴”惊到了,一个企业管理系统居然包含这么多功能! 05 课看到“进行抽象”后惊呆了:抽象之后居然变得那么简洁!模型就直接支持开闭原则了(添加新组织、组织的级联关系可以任意层、添加新岗位等) 这个过程有点像修仙小说中的炼丹师 分层:将不同的材料分类 事件风暴:找出现有材料可以炼制多少种功能的丹药 进行抽象:将材料投入炼丹炉,第一次提取精华,让原材料的某些属性威力倍增 虽然自己不会炼丹,但第一次见怎么炼丹,还是非常激动!
2022-12-15 - Geek_fe41a4 👍(0) 💬(1)
事件风暴的产物需要体现在概要设计中吗? 领域模型在概要设计和详细设计中分别需要体现到什么粒度?
2024-02-18 - 牛奶 👍(0) 💬(1)
1、我们一般是由具有开发经验的BA来建模,建模完成后和开发拉通。 2、比如我们业务中用到条款和条款组别的关系,报价单和商品关系,就把它们统一抽象到了关系表中。
2023-10-06