首页  ·  知识 ·  架构设计
数字化企业敏捷建模之领域建模
钟敬、肖然  知乎专栏  综合  编辑:vaur_han   图片来源:网络
针对企业架构的落地实施,分别从价值建模、领域建模和组织建模三个层面完成业务架构、应用架构和技术架构的持续演进。三个建模过程域强调能够通过跨专业的协作,建立更多上下游的互动,从而在此

针对企业架构的落地实施,分别从价值建模、领域建模和组织建模三个层面完成业务架构、应用架构和技术架构的持续演进。三个建模过程域强调能够通过跨专业的协作,建立更多上下游的互动,从而在此过程中让共同的语言体系得到持续的构建。

image.png

在应用架构层面,一个关键的任务就是需要完成经典意义上业务和科技的对接,保证正确的需求得到传递。在软件工程的历史上,这个过程往往是架构师的工作领域,而由于业务和技术人员术业专攻的不同,思考问题的模式自然也不一样。所以架构师的一项重要工作就是成为两个人群之间的桥梁。

搭建这座桥梁有不同的方式,可以让架构师们凭借个人能力直接通过自然语言来作翻译,但显然这种模式太过于依赖人,规模化采用是不可能的。另一种更有效的方式是形成一个模型,然后建立上下游的统一认知,即上游的业务需求需要转换为针对模型的改变,而下游的开发人员由于理解了模型,所以能够准确理解要求的变化是什么。这里所说的“模型”是用来描述业务领域的,因此称为“领域模型”,建立领域模型的过程称为“领域建模”。类似Zachman这样的架构框架以及UP(Unified Process)这样的开发过程,都重视领域建模,由此也在强依赖于复杂业务系统的领域,如金融、保险行业,得到了认可。

建立领域模型,让业务和技术都能够在同一个抽象层面交流的想法本身是不错的,但面临的一个巨大挑战就是模型本身的演进。面对变化,模型如果不能及时刷新,在实际运作中就无法帮助业务和技术有效沟通,甚至于可能因为模型没有跟上变化而制造出更多的沟通问题。

面对快速变化的商业环境,追求领域模型的稳定性或前瞻性会面临很大的挑战,甚至于是不理智的。想要了解00后们的思想,站在上帝视角去分析或批判是没有意义的,最好的方式就是加入到他们的对话中去,由此你也能用上YYDS、xswl、5151等时髦缩写。所以,在构建领域模型上,更为有效的做法是帮助参与建模的人群建立起共同的语言体系和沟通机制,并且让语言体系在频繁的沟通中得到理解和演进,推动不同背景和思考方式的人群逐步深化共识。

通过协作的方式建立领域模型,形成统一语言,并不断对模型进行演进,以反应更深刻的领域知识,正是领域驱动设计(DDD,Domain Driven Design)的核心要点。DDD为此提供了一整套原则、实践和模式。下面我们通过几个经典的模式和实践来理解DDD中领域建模的方法,也是我们认为DEAM方法在领域建模活动中可以借鉴和采纳的。


事件风暴:协作式需求分析

不论是传统的还是现代的软件开发过程,第一步总是从需求分析开始。而需求分析往往要首先研究软件系统应该具有哪些功能,这些功能由什么人操作,会产生什么后果。这个过程称为“捕获行为需求”。

捕获行为需求的方法有多种。Eric Evans 的《领域驱动设计》一书(下文简称《DDD》)重点在于统一语言和领域建模,并没有规定捕获领域模型的具体方法。2013年Alberto 提出了“事件风暴”(Event Storming),是目前DDD实践中比较流行的方法。下面通过对它的简要介绍来体会DDD的特色。

事件风暴要求不同的干系人,尤其是业务领域专家、产品经理、架构师、核心开发人员等,一起以协同工作坊的形式进行。

第一步:识别共识领域事件

“领域事件”是业务人员关注的业务过程中发生的事情,例如“订单已提交”、“保单已生效”等。领域事件代表了业务流程中各步骤引发的“结果”,所以事件风暴是一种结果导向的方法。

首先,可以由不同的参与人各自按照自己的理解,将某个业务流程中所发生的领域事件分别写在便利贴上,按大致的时间顺序贴在白板上。由于不同的人理解不同,因此写出的领域事件也有所差异。下图是一个简单的例子。

image.png

接着,大家一起对比每人写的结果,保留相同的部分,对不同的部分进行讨论,最终形成统一的理解。这一步,除了对领域事件进行增补和删除,还会发现同一个事件,不同人的表述方式不同,或者同一个词汇,表达了不同的涵义,都需要讨论和统一。下图是统一后的样子。

第二步:识别共识操作信息

识别出领域事件后, 就可以一边讨论,一边识别出操作、参与者、读模型、业务规则等信息,用便利贴贴在白板上。如下图。

image.png

第三步:初步识别领域对象

最后,通过分析事件和操作所牵涉到的名词,或者说背后隐含的实体,可以初步识别领域对象,为后面的领域建模奠定基础。如下图所示。

image.png

在真实的项目中,上述过程会反复迭代、不断精化。这一过程体现了先发散后收敛、再发散再收敛的过程,是一种头脑风暴的共创方式,也是事件风暴中”风暴”二字的来源。头脑风暴是一种常见的不同干系人协作解决问题的方式。在事件风暴中,大家通过沟通,不断统一认知,逐步开始形成统一语言。这一过程也结合了视觉学习,触觉学习、听觉学习,是构建知识体系的开始。同时也为后续的领域建模打下了基础。

事件风暴并不是捕获行为需求的唯一方式。 像传统的用例分析法、敏捷中的用户故事、最近提出的“Domain Story Telling”(https://domainstorytelling.org/) 等方法也是可选项。只要抓住协作、统一语言等要点,都可以在DDD的项目中运用。每个团队可以根据具体的业务和技术情况选择。


模型驱动设计:不一样的领域模型

建立准确而深刻的领域模型是DDD的核心关注点。为此,DDD提出了“模型驱动设计”(Model-Driven Design)模式。该模式可以概括为两句话:

  • 领域模型要和业务需求一致

  • 代码实现要和领域模型一致

这两个一致的最终结果就是代码与业务需求总是一致的,避免了常见的业务和实现“两张皮”。

所谓领域模型,就是反应业务领域知识中的各种概念,以及概念间关系的模型。下图是一个简单的例子。

image.png

事实上,传统软件工程同样重视领域模型, 然而DDD中的领域模型却和传统上有一个重要区别:DDD强调领域模型要兼顾业务和技术两个视角。

在传统软件工程中,首先要建立分析模型,或者说分析层面的领域模型。这一步强调完全从业务领域出发,不考虑技术视角。第二步,则是在分析模型基础上建立设计模型。这一步会在分析模型的基础上,增加技术实现视角,对模型进行增补和修正。最后,再根据设计模型进行编码。传统的分析和设计模型的关系如下图。

image.png

这种方法虽然在理论上是完备的,但实践中常产生问题。由于分析阶段没有技术视角,于是在开发设计模型时,可能对分析模型中的对象和关系进行较大的改动。在之后的开发过程中,开发人员会往往聚焦于设计模型,而将分析模型束之高阁。时间久了,分析模型与设计模型的差距越来越大,也就失去了存在的意义。另一方面,设计模型中由于掺入了实现细节,模糊了领域知识,因此通过模型表达领域知识的作用会逐渐削弱,领域建模的价值也就逐渐不存在了。最终,技术实现与业务需求越来越远,软件系统也就难以真实反应业务需求了。

针对这个问题,DDD视角下的领域模型是下图这样的一个交集。

image.png

领域模型是业务视角(原来的分析模型)和技术视角(原来的设计模型)的交集,是业务人员和技术人员共同创建的,反应了两者的共识。尽管技术人员仍然可能为领域模型增加实现细节,形成设计模型,但设计模型必须严格遵循领域模型。对领域模型的修改,必须经过业务和技术双方的同意。这样,就避免了传统软件工程中分析模型和设计模型相互割裂的风险。

DDD的领域模型之所以成为可能,在于业务和技术人员持续的协作。要注意的是,尽管领域模型中包含技术人员的视角,但采用的仍然是业务语言,不应该存在任何领域专家不能理解的技术术语。既然如此,所谓技术视角又体现在哪里呢?

事实上,即使从纯粹的业务视角出发,对于同一业务,也可能会构建出不同形式的领域模型,这些模型都是“正确”的,关键在于哪一个“更好”。这时候,技术人员从技术视角出发,往往可以发现,有些模型更容易进行技术实现。因此,就可以从开发视角帮助选出更恰当的领域模型。

为了便于领域建模技能的掌握,DDD在传统面向对象方法学的基础上,总结了一套模式,包括用于表达业务概念的“实体”(Entity)和“值对象”(Value Object);用于对业务概念进行分组的“模块”(Module);用于管理对象生命周期并保证不变规则(Invariant)的“聚合”(Aggregate)等。很多采用DDD方法的开发团队对于这些模式已经耳熟能详,这里就不再赘述了。


限界上下文:分而治之与概念一致性

对于大规模的复杂系统,软件工程界总结出了分而治之的方法 —— 关注点分离(Seperation of Concerns)。也就是把大系统分解成若干子系统,从而管理系统的复杂性。在建模层面,则意味着划分子模型。

DDD中的限界上下文(Bounded Context)也可以认为是分而治之思想的体现,一个限界上下文大体上可以对应于一个子系统或微服务中的模型。那么,限界上下文与传统方法的区别在哪里呢?

本质区别在于,DDD明确地指出,限界上下文的目的在于维护概念一致性。

早在六、七十年代,软件工程的早期,人们已经意识的到,在大型系统中保持概念一致性非常困难。但人们潜意识中一直认为,总有办法达到全局的概念一致性,也为此想了很多办法,例如及时更新文档、增加开会沟通等等,但并没有达到预期效果。

而DDD认为,面对复杂系统,全局概念一致性从根本上是不可能的。因此退而求其次,将复杂模型分解成若干子模型,每一个的规模都不超过一个开发小组的认知负载(Cognitive Load),因此可以在这些子模型中达到概念一致性。然后,DDD借用了语言学中上下文(Context)这一概念,并强调了明确的边界划分,将这种子模型称为“限界上下文”。从而通过“退化的”方式解决了概念一致性问题。

限界上下文之间常常需要沟通,两个上下文间会有一些对应的概念,他们可能表达同一个事物,但关注点或结构又有所不同。为了在不同上下文沟通时进行这种概念上的转换,DDD提出了上下文映射(Context Map)模式。

根据团队的组织和技术条件等因素,上下文之间呈现出各种不同的关系,DDD提出了多种模式进行处理,包括:共享核心(Shared Kernel),客户供/应商开发团队(Customer/Supplier Development Team)、遵奉者(Conformist)、防腐层(Anticorruption Layer)、开放主机服务(Open Host Service)等。这些具体模式应该说正在随着敏捷组织、云原生架构的发展而持续演进和改变,值得大家更多的关注。


核心域:提纲挈领,抓大放小

对于大型复杂系统,除了上述概念不一致问题以外,还有另一个问题:模型中关键的核心概念和非核心概念混杂在一起,核心概念淹没在大量非核心概念中,造成系统难以理解和维护。

尽管对于系统的正常运行来说,非核心的概念也是必要的,但人的理解能力有限,不可能理解系统中的所有部分。这时比较好的策略是从领域模型抽取出一个比较小的核心,在DDD中称为“核心域”(Core Domain)。从而,人们可以首先聚焦于核心域,提纲挈领地理解系统最本质的部分。在必要时再选择性地理解其他部分,从而建立更好的迭代认知机制。

在系统开发中,核心域部分的开发力争尽善尽美,做到九十分以上;其他部分够用就行,六七十分即可,也就是所谓“抓大放小”。这一思路决定了系统开发的投资和技术选型策略。比如说,核心部分需要投入更多更好的人力资源,最好掌握在企业自己手中。其他部分则可以投入少些,或者采用外购、外包等形式。

将核心域从模型中提炼出来的过程,在DDD中称为“精炼”(Distillation),在精炼出核心域的过程中,可以用到“领域愿景描述”(Domain Vision Statement)、“突出核心”(Highlighted Core)、“分离核心”(Segregated Core)、“抽象核心”(Abstract Core)等模式,并产生与核心域对应的的副产品“通用子域”(Generic Subdomain)和“相干机制”(Cohesive Mechanism)。


柔性设计:通过持续重构加深理解

由于业务需求总是不断变化的,必然导致软件系统相应的变化。

这些变化可分成两类:一类是“量”的变化,不会影响整体架构。另一类是“质”的变化,会牵涉到包含领域模型在内的架构性变化。为了应付这种变化,需要将模型设计得更加灵活。这就需要运用抽象思维对业务进行更深层次的理解,然后反应到模型的演化,并进行相应的重构。

为了使这一复杂任务变得可行和可控。DDD提出了“柔性设计”(Supple Design)。为了理解这一概念,《DDD》一书中打了一个比方:好比一副新的皮革手套,开始时整副手套都很僵硬;戴得久了,关节处就会自然变得柔软,而其他部分还是比较硬的。

同样,在系统设计中,也不是一开始就把所有地方都设计得很灵活,而是先进行“足够的”和“整洁的”设计。随着业务变化,将模型和系统中相应的部分重构得越来越灵活,而没有牵涉到的部分则保持不变。假以时日,模型中需求变化频繁的部分就会变得很灵活,需求不常变化的部分则没那么灵活。也就是说,模型中的哪些部分需要设计得灵活,是自然演进形成的。这个过程就是柔性设计。这种方法与基于早期的猜测而进行的“过度设计”形成了鲜明的对比。

加深对领域的理解,实现柔性设计的关键仍然是业务和技术人员密切协作,不断优化领域模型和统一语言。DDD中也提供了“揭示意图的接口”(Intention-Revealing Interfaces)、“概念轮廓”(Conceptual Contour)等模式,并可结合运用分析模式、设计模式等其他方法。


统一语言:业务与技术的“普通话”

前面已经反复提过“统一语言”(Ubiquitous Language),这是DDD的核心模式之一,包含两个层面的含义:一是业务和技术人员之间的统一;另一个是开发团队内部各角色之间的统一,例如架构师和一线编码人员的语言不能有偏差。 最终结果就是每一行表示业务逻辑的代码都能对应到统一语言,从而与业务保持一致。

此外,统一语言在限界上下文中才是有意义的。也就是每个限界上下文都有自己的一套统一语言。没有脱离上下文的统一语言。这和语言学所认为的“语言在上下文中才有意义”的理论是一致的。

统一语言的概念其实很容易理解,难度在于怎样真正在实践中贯彻。

用好统一语言,要注意“软”、“硬”两方面。“硬”的方面主要就是前面说的领域模型。领域模型图是统一语言的物理载体。此外,实践中还常常结合词汇表(glossary)使用。

“软”的方面,则是在日常口头和书面沟通的过程中,使用统一语言进行协作。在分析需求、建模、代码实现等所有过程中,对于每个牵涉到领域知识的词汇,都要看一下在领域模型中是否有所反映;大家对同一词汇的表述和理解是否一致;已经废弃的词汇是否还在使用等等。如果发现语言与模型不一致,就要及时纠正语言或模型,让两者保持一致。编写代码的时候,也要使代码中的各种命名与模型和词汇表保持一致。

最后,如果正确的运用了统一语言,那么领域模型和自然语言必然呈现出平滑的转换关系。也就是说,领域模型中的每一个领域对象、每一个关联关系,都能直观地翻译成自然语言,反之亦然。


总结:人和互动 胜过 流程和工具

上文基于DDD的框架,概述了通过领域建模,兼顾业务和技术视角,形成统一语言,实现业务和技术人员之间沟通协作的方法,从而能够完成DEAM方法在领域建模上的目标。

语言是知识的载体。建立统一语言的过程,实际上也是构建领域知识体系的过程。领域模型的有效性,在于蕴含在其中的领域知识的准确性和深刻性。因此《DDD》一书开篇第一章就是“咀嚼知识”(Crunching Knowledge)。

掌握构建领域知识的方法包含两个方面:一是对具体方法和技能的掌握,例如怎样绘制领域模型图;二是以团队协作的方式不断运用这些方法技能的过程。

这两方面都是必要的,而团队协作的过程则是更加本质的方面。如果仅仅是团队某些成员掌握了某个方法,但不能在多人的协作中进行运用,那么就不能真正实现统一语言,也无法在团队层面用好领域模型,这是很多团队失败的原因。反之,如果重视不断在协作的过程中构建知识,那么即使短期内没有很好的掌握某项技术,也会在此过程中不断得到弥补,形成适应自己团队的方法。所以沟通协作是软件为基础的企业架构需要解决的核心本质问题,也是我们在DEAM方法中一再强调的。

DDD并非全新的方法,而是对传统面向对象方法学的提炼和发展,并在此基础上总结了一整套原则、模式和实践。本文介绍了其中的核心部分:

  • 通过协作共创捕获行为需求,开始建立统一语言,为领域建模打下基础;

  • 通过建立领域模型,将领域知识可视化,弥合业务和技术两个视角,成为统一语言的物理载体;

  • 通过限界上下文,分而治之,解决了大型复杂系统概念不一致的问题,也为统一语言的实现提供了前提;

  • 通过核心域,将核心领域知识提炼出来,从另一个维度解决了大型复杂系统难以理解和维护的问题;

  • 通过柔性设计,使领域模型不断演化,得到对领域知识更深刻的理解;

  • 最后,重申统一语言的含义——业务与技术的“普通话”,并探讨如何在实战中践行统一语言。


本文作者:钟敬、肖然 来源:知乎专栏
CIO之家 www.ciozj.com 微信公众号:imciow
    >>频道首页  >>网站首页   纠错  >>投诉
版权声明:CIO之家尊重行业规范,每篇文章都注明有明确的作者和来源;CIO之家的原创文章,请转载时务必注明文章作者和来源;
延伸阅读