戏说领域驱动设计(五)——子域
细心的您可能已经发现了一个规律,DDD使用了一种由上至下的方式来指导系统的构建。第一层考虑如何把大的领域划成多个小的子域,重要性不一样,投入的人和钱肯定也不一样;第二层考虑系统的架构方面,仍然是一类宏观的工作,不过其更加聚焦于如何把大的系统分成几个物理子系统及子系统间的交互方式(如果非微服务架构,就是要考虑分几个包或名称空间);第三层考虑划小后的子系统自身的架构模式(事件驱动、三层架构、ODD架构、事件源架构等)、领域模型(设计模型)等技术细节。这种从上到下的方式约束着我们在看待问题时应当由宏观到微观,更具条理性。好比您认识一个人,先有一个整体印象然后才能了解对方的三观等问题。
我们上一章对子域做了一个大概的说明,总结起来有四点:1)子域是一种对整体领域的概念上的划分,是人为的主观行为;2)子域的目的是把大的领域划小,使用分而治之的方法来简化系统建设时的复杂度;3)子域的划分不是一成不变的;4)领域有界限,不是无限的大。您常听客户说“我们有这样几块业务”,一般来说这里所谓的”几块儿“就是指子域。谈论子域的时候一般不会涉及过多的技术。假如时间可以倒退50年,那个时候没有电脑,人们用纸记录工人的工资。工资管理通过人工的方式来完成,如何管理就是业务规则,是客观的,和是否有电脑关系不大。
1、子域分类
DDD的定义里,子域包含“核心域”、“支撑域”和“通用域”三类,您经常会迷惑为什么会这么定义?怎么理解不同域的作用?如果我分错了是不是对系统影响非常大搞不好会失败?回答您的问题之前我们先看下图对“子域”是如何定义的。
核心域是指业务中最重要的那部分,是您做这个系统的主要目的。核心域体现了企业的个性,比如“的的”中的“打车”功能,这个业务不存在那“的的”可以说是0价值的。所以核心域相对来说很好确定。如果您是甲方爸爸,不建议使用乙方的人做核心域的研发工作除非乙方是“微软”、“IBM”这种牛掰公司。
支撑域主要目的是为了支撑核心业务的运转,关注于业务的某一个方面。以一般的电商购物系统为例,“客户中心”用于支撑商品买卖过程,客户是其中的主体对象;“消息中心”中于支撑交易过程中的如邮件、短信、消息等功能,这两类中心既没有“订购中心”这种核心域业务那么重要,也不能随便找一个开源的来用,里面仍然有许多充满个性化的东西。这一块可内部人员开发也可以考虑外包来做,必须要做好质量与进度管控。
通用域的概念相对而言就比较模糊了:有说“如果子域被用于整个业务系统,这个子域就是通用域。比如保险业务,其核心是‘投保’、‘生成保单’等,电商化后为了让用户在各类渠道如APP、网站、微信小程序等进行各类保险(车险、养老、财产等)的受理而引入了‘订单中心’,这里的‘订单中心’就是通用域,因为他通用于各类渠道和各类保险销售场景”,有点支撑域的味道;有说“通用域用于起到增强的作用,比如我们看视频网站时通常会有的‘收藏’功能”;本文对于这个概念的解释是:通用域的东西一般是有现成的解决方案,可直接购买或下载开源。以此作为解释也是为了对应我们所强调的子域对于资源投入的指导性。
“核心域”是最关键的,“支撑域”和“通用域”相对来说就差了一级,此处的差一级是指资源的投入。
将一个大的领域划分成多个小的子域有两个作用:第一是避免系统混乱,使用分而治之的方式让您或您的团队的关注力更加集中。很多时候,系统的需求在一直无限的扩大而您的团队人数是固定的,先建设哪个后建设哪个得有个先来后到。把业务上分成一块块的,排个优先级,系统开始投入建设后可以让客户在最短的时间内看到成果。您做项目为了赚钱,客户可从中受益;如果客户是甲方时,局方的负责人看到了东西也好对其上级有个交待,大家相互理解好办事儿。第二也是最为核心的,不同的业务域可以让您决定投入的人与钱的数量。比如核心域应该投入最多的钱,能力最好的员工;支撑域相对少一点;通用域不行就花钱买一个现成的或直接搞开源。
您可能会说:不对,我的系统九成的工作全是核心域的,没法分。首先想说明的是,一个优秀的系统不仅仅要包含系统本身,还需要有很多的周边设施做支撑,是一个完整的生态,系统越大越是如此,比如日志汇聚、链路追踪、业务监控等都是生态的一部分。子域的划分是一个需要投入精力和思考的工作,您不仅要看眼下还需要再多往前看一步。您也不应该只考虑功能性需求,涉及安全、可用性等问题都应该在规划之出定义出来,做不做单说,得考虑。“推动技术进步的往往是非功能性需求”。回归到本文,即使核心域比例很大,也不太可能会涵盖系统的全面。极端一点,如果真的有95%的核心业务,您还可以对核心域作进一步划分呢?对划分出的“核心子域”进行优先级设定并决策资源投入度,与我们前面的不矛盾。
注意! 1、建议您在项目建设时将“核心域”与“支撑域”等同看待。虽然概念上进行了划分,但实际落地的时候两者几乎是需要同时进行的。划分的目的其实是让您在投入资源的时候比如开发人员选择,支撑域让中级或一般高级工程师出马就差不多了,监控运维就可以搞定。 2、“账户管理”、“数据字典”这类典型的属于支撑域的东西,开发工作往往优先于核心域 3、虽然某些域在业务上定义为核心,但系统建设时需要以同核心域相同的资源投入对待。这是灵活的,千万别陷入教条主义。 |
2、子域划分方法
子域的划分是指:确定业务属于“核心”、“通用”和“支撑”三个域的哪一类;针对每个子域如“核心域”中的内容如何再做细化。
把一个大的领域分成三个固定子域后,每个子域中的内容就是与您的业务相关的了。需要说明的是业务子域可能会再分为多个部分。比如核心域中包含A、B、C三个业务子域,A的业务子域又可再分成A1、A2两个业务子子域。细分的标准是每个子域应该有一个明确的边界,这个边界不是以技术作为划分依据的。比如电商系统中,“销售中心”是核心子域中的一部分,其内部可再划分为“商品中心”和“订单中心”两个子域。
业务子域的划分一般来说可以从两个维度出发:功能与流程。通过功能划分其实是可以根据感觉走的,虽然很不严肃,但也不是什么很玄的东西。有些业务听起来看起来就可区分其重要性。比如做个论坛,监控非常重要,但您不会真的投入一半人马去开发个主机监控系统吧?比如您熟悉的“豆瓣网”,“读书频道”与“电影频道”肯定得作为核心域中的两个业务子域吧?这种下意识的分割有时候很准,人是有这个灵性的。根据功能来分域一般是划分子域的一个非常非常重要的参考,下面我们以个人微信为例尝试通过功能的维度进行子域划分。
注意! 在做子域划分时请使用业务语言而非技术术语,尤其不能说什么微服务、Spring Cloud、MySQL、Kafka等。域属于业务方面概念,此阶段尚未到达讨论技术的地步。 |
下图为个人微信子域的示意图,也许与您设想的有不一致的情况,此处有很强的主观性。试想一下,这样的一个图如果可以在设计之初定义下来,基本上您和您的团队以及客户就可以在宏观上大概知道了这个系统要做什么,有哪些主要的业务。此图除用于资源分配的决策,还能在后续设计限界上下文时给予指导。
根据流程来划分一般是在功能划分后的基础上所做的补充,假如某个业务的进行顺序为:A - B - C,三个任务节点亦可以视为三个子域而存在。以电商购物为例,您在按功能分域的时候分割出了一个订购子域,订购的一般流程是:下单、支付、送货,那么我们就可以把这个子域再分解为三个子子域:下单子域、支付子域、送货子域。团队建设时可能会以此为据组建三个小的项目组。
如果您是一个DDD初学者,可能会对子域划分的正确与否产生怀疑。其实没必要有这一层的顾虑。子域的划分主观性很大,经过仔细分析后的划分基本上也八九不离十,随着后续对业务的深入了解,还会引入新的子域或对现有子域做调整。即使规划之出有些偏差也并不影响项目的成功失败,随时进行调整即可。除非您在规划之初把很明显应该作为核心的东西没有投入足够的关注,在项目建设前完全没做过分析工作才可能会出现这类情况。
下一节,讲解限界上下文及其与子域的关系。