• 人人学会模块设计模块设计实战精粹


      摘要:本方简单和大家探讨模块设计方法——领域建模的战略建模及介绍实战项目的应用经验,期望理念结合实战,使大家有收获。领域建模的战术部分将在下一篇文章介绍。

     

    我经历过这样一个故事,我和闺女一起在XX网上选了一个漂亮的水杯,到货后一用,水杯无法放入小书包侧兜,很不方便。我们很多的代码就像这个杯,单看一行代码、一个代码片段、一个函数,甚至一个类/文件都很优美,但结合整体后,就是一个蹩脚的代码。

    为什么大多数代码都那么难懂?如果简单归结于代码设计太差,这样的解释太容易了,也太廉价了,更加没有任何帮助。

    根本原因是我们缺少系统思考。系统是由一群相互连接的实体构成的整体。可见任何系统是由连接、实体和整体构成。系统由实体是构成,实体就如汽车的轮子、方向盘等;实体是相互连接的,如果一个实体与另一个实体不能连接,不能协调运动,就不能构成一个系统;为了达到特定的功能或目标,各个实体最终要构成一个整体。

    很多人对系统的认知存在误区,误区是过于注重实体而忽视了连接,分析系统时把重点放在独立的实体上,最后才发现影响系统的关键的因素是实体之间的连接。因此,我们要关注实体和连接同等重要。

    代码亦如是。我们在模块设计时,也是从三个层面来了解它。首先,它是由哪些部分构成;其次,它和哪些外部模块有交互;再次,它对外部模块的事件是如何响应的。这就是软件设计中常用的Static View ,Interaction View,Interface View。

    从领域模型的典型架构来看:领域模型部分对应Static View,应用部分对应Context View以及适配器部分对应Interface View。其应对关系见图1。

    图1.系统要素与代码架构对对应关系图

    可见,万变不离其中,如何从复杂的业务场景和需求中识别实体、连接、整体等关键要素是完成模块设计的关系。当前主流的方法是领域建模(DDD),领域建模分战略建模和战术建模。

    ü  战略建模:是从需求到领域模型;

    ü  战术建模:是从领域模型到代码。

    接下来,我主要给大家介绍的是一个简单实用的战略建模方法—事件风暴分析法。我将以案例为中心从如下方面来简介:

    ①  事件风暴简介

    ②  建模过程

    ③  实战项目案例总结

    ---------①事件风暴简介---------

    事件风暴分析法是一种领域建模的方法,它是事件驱动建模技术,一种快速的设计技术,让领域专家和开发人员都可以快节奏的参与到设计过程中。它聚焦于业务和业务流程,而非名词概念和数据。

    Ø  它具有如下优点:

    1)   一种强参与感的方法。业务人员和开发人员能平等的基础上,用共同的语言、共同参与。

    2)   聚焦于业务事件和业务流程,而不是类和数据库

    3)   它是一种高度视觉化的方法,设计过程中不涉及代码,让每个人平等地参与到设计过程中

    4)   实施快,投入成本低。只要花几个小时,就能得出一个不错的领域模型

    5)   团队成员都会更深入的理解业务

    6)   你能尽快、尽早地在模型和认知中识别出了问题,扫除误解并将结论作为新的见解善加利用。

    7)   宏观的建模和代码设计级别的建模都可以使用事件风暴

    Ø  事件风暴进行建模时,需要邀请的人员、具备的思维方式和准备的材料:

    ü  邀请人员:领域专家和开发人员,每个人都会提出问题,也能给出答案。相互支持,共同设计出更好的模型。

    ü  思维方式:开放包容的心态、不要苛刻追求速度。分析时,创造事件多多益善,将来会即快又经济的完善整个设计。

    ü  准备材料:主要是7种颜色的便签纸、笔、一面宽墙或者桌面

    Ø  做好各项准备,就可以按如下8歩完成设计:

    图2.风暴事件分析法工作流

    Ø  事件风暴分析法图例

    图3.事件风暴法涉及的元素对应的图例

    Ø  模型分析结果样例

    图4.多种业务场景的分析样例

     

    事件风暴方法不仅适用于全新系统的建模,也同样适用于遗留系统重构前的建模,当然我们针对系统内的模块也适用建模。好处就是:系统性的理解业务、参与的所有人快速达成共识、模型指导未来的开发工作&开发重点。

     

    ---------②建模过程I:识别业务流---------

    事件风暴方法聚焦于业务和业务流程,而非名词概念和数据。所以首要任务就是识别业务流。

    如何快速识别业务流呢?事件风暴方法是基于行为驱动的设计方法,而实例化需求也被称为行为驱动开发,因此,我们可以用实例化需求的需求分析套路来快速识别业务流。

    通过和领域专家、用户讨论业务,按时间顺序不断完善一个个业务流节点。这些业务流节点可以用类似“八股文”的方式模式化,模式如下:什么场景下,谁使用什么做了什么,通过什么算法,产生了什么结果,影响了什么。将这些业务节点组合起来就是一个业务流。

    根据业务节点的模式化,我们可以快速识别和抽象出领域模型的关系元素,见下图:

    图5.业务流节点的组成元素与领域模型关键元素的对应关系

     

    分析业务流的过程中不要急于一步到位,可以从产生什么结果入手,结果往往是最容易搞清楚的,然后再不断的分析、持续补充。你可能在分析某个业务流节点时,想到一个新的业务流节点或者一个子业务流;也可能不断完善业务流程节点的各元素。一开始可能很简单,逐步丰富。例如:

    从一个项目管理系统的需求中,我们想到一个业务流节点:产品负责人提交待办项到迭代中。

    然后我们再通过反问的方式,逐步丰富业务流的节点信息:例如:

    ①     提交待办项到迭代的前提是什么?

    ②     提交完成后,需要将结果通知谁?

    ……

    通过上面的方法我们完成了这个需求:只有待办项位于发布计划中时才能进行提交,而且需要赞成承诺的团队成员达到法定人数。如果待办项已经提交到另外一个冲刺中,那么需要先将其回收。当待办项的提交完成后,需要通知相关方(相关的迭代)。这样一个完整的业务流节点就完成。

    另外,我们也可以以此节点为基准点以时间线为主线,向前向后发散,不断反问来完善整个业务流。例如:

    ③   “该业务流节点将会触发的下一个业务流节点是什么”

    ④   “触发该业务流节点的前一业务流节点是什么”

    使用上面的方法,基于“产品负责人提交待办项到迭代中”业务流节点,我们可以发散出整个业务流,建立模型,可以给该业务流起个名字,例如:

    图6.项目管理业务流图

    当然,整个项目管理可能会存在很多业务流,我们可以通过“头脑风暴方法”,不断的抽象出新的业务流。例如:项目管理系统肯定有用户,用户有不同的角色就会有不同的权限,因此应该有一个用户管理的业务流;项目研发过程中可能涉及很多角色完成不同的任务,像产品经理创建产品、项目成员领取迭代任务,并且不同角色的权限也不相同等,因此要有一个项目人员管理的业务流。等等。这样我们可以得到多个业务流甚至子业务流。

    图7.多条业务流模型

    ---------③建模过程II:领域事件 ---------

    Ø  什么是领域事件:

    ①   理解为是发生在一个特定领域中的事件,是在同一个/不同领域中希望被其他部分知道并产生后续动作的事件。

    ②   不是所有发生过的事情都可以称为领域事件。一个领域事件必须对业务有价值,有助于形成完整的业务闭环,也即一个领域事件将导致进一步的业务操作。

    例:“当X发生时,需要做Y。”则表明X可以定义成一个领域事件。

    Ø  什么是规则(Policy):

    是对分支条件或复杂业务规则的抽象,目的是通过降低分支复杂度聚焦主要业务流程,未来在技术实 现时可能是一些分支条件,也可能应用适合的设计模式。如:事件:订单支付已超时à规则:支付超时规则。

    Ø  如何从业务流中识别领域事件:

    从业务流节点的需求描述模型成提取,如:什么场景下,谁使用什么做了什么,通过什么算法,产生了什么结果(领域事件),影响了什么。标红的就是领域事件。

    Ø  如何描述:

    “产生事件的对象名称 + 完成的动作的过去式”的形式。例如:订单的发货的领域事件:订单已发货)

    Ø  优点:

    领域事件可以是一种基于事件的架构。它是模块的边界容器。它可以把处理的业务解耦,实现系统的可扩展性,提高主业务流程的内聚性。如:Android的Handle事件框架,加强各个Activity子类的内聚性和独立性。

    Ø  特点:

    解决领域的聚合性问题、也是领域分析的工具。领域事件可以通过观察者模式和订阅模式进行实现。比较常见的实现就是事件总线。

    下面基于项目管理系统已经识别的三个业务建立的领域事件模型样例:

    图8.在业务流中,补充了领域事件

    ---------③建模过程III:决策命令---------

    决策命令(Decision Command),是领域事件的触发动作,代表业务流程上的重要业务决策,在技术实现的时候,会通过领域类上的方法来实现。

    操作要点:

    ①   针对每一个领域事件,寻找产生该事件的业务视角上直接相关的动作,将其识别为决策命令,用业务语言进行描述,一般来说是动词或动宾短语

    ②   在命名决策命令的时候,应当避免简单粗暴的将事件改为动宾结构描述的方式,例如“订单已创建”的决策命令被命名为 “创建订单”。而应更进一步思考贴近业务描述的用于,例如命名为“下单”,然后将事件根据该命名进行基于统一语言的适当调整,比如将事件重命名为“订单已下单”。这样的目的是能够使用更贴近于业务的描述方式来进行统一语言和命名,在未来技术实现的过程中,代码中的相关元素的命名也应与此保持一致,做到“代码即文档”。

    下面基于“项目管理系统”补充了“决策命令”的模型样例:

    图9.  在业务流中,补充了决策命令

    ---------③建模过程IV:实体---------

    实体(Entity),是决策命令的触发原因,它可以是:

    •       角色:由某个具体的用户角色通过用户界面触发;

    •       外部系统:由某个具体的外部系统通过调用API等方式触发(也可以是某个组织内开发的,但当前不在分析范围 内的内部系统);

    •       定时任务:由系统内部,基于定时任务触发;

    •       前一个事件:由前一个事件发生后自动触发,如果触发过程包含业务逻辑,可在该决策命令对应的领域事件前放 置规则卡片。

    操作要点:思考和确定该决策命令的实施者类型,并按照图例要求将其予以贴出,其中:

    a.       角色:需要写明具体的角色名称;

    b.       外部系统:需要写明具体的外部系统名称;

    c.       定时任务:需要写明具体的最高触发频率(例如:1天、10秒、每1次)

    下面基于“项目管理系统”补充了“实体”的模型样例:

    图10.     在业务流中,补充了实体

    ---------③建模过程V:聚合根---------

    聚合根:是可以承载决策命令、其对应的领域事件和属性(输入和输出)的重要业务概念。

    图11.            聚合根和其领域模型元素的关系

    关键操作:

    1.       在每一对儿决策命令和领域事件之间,识别并抽象用于承载该决策命令和领域事件的最直接的业务概念,并将其以名词形式予以贴出,通常是决策命令与领域事件中均出现了的名词;

    2.       检查业务承载物、决策命令和领域事件在概念和粒度上的一致性,通过重命名的方式统一语言,消除二义性。

    要点:

    •       决策命令携带了业务承载物的输入信息,领域事件携带了业务承载物的输出信息。

    •       警惕“XXX配置”、“XXX记录”、“XXX规则”等技术向的词汇,寻找正确抽象,正确的抽象很有可能是配置的结果、记录的主体等等。

    •       如果不是信息系统中的业务概念,那它是现实中会看得见摸得着的事物(例如订单);

    •       虽然在当前业务中看不见摸不着,但是可以在未来抽象出来的业务概念。

    下面基于“项目管理系统”补充了“聚合根”的模型样例:

    图12.            在业务流中,补充了聚合根

    ---------③建模过程VI:界限上下文---------

    限界上下文(Boundary Context),是业务上下文的边界,在该边界内,当我们去交流某个业务概念时,不会产生理解和认知上的歧义,限界上下文是统一语言的重要保证。

    在未来的技术实现中,应当尽可能避免两个在概念上容易混淆的限界上下文内的业务需求,被同一个团队开发和维护。

    如何操作:

    1.       将全部已使用事件风暴进行分析的业务场景中的全部聚合根和实体提取出来放到一张新的大白纸上,并保证不重复。

    2.       补充事件风暴未覆盖的遗漏业务概念或外部系统。

    3.       根据业务概念的相关性,将聚合根和实体进行快速的归类放置,每个外部系统单独放置。

    4.       不断澄清业务概念和消除二义性,例如“商品”一词,在“销售”和“库管”两个业务上下文中,所指代的含义是有区别的(就算只有1个属性不同,也是两个概念)。

    5.       根据统一语言的结果,对聚合根进行增加、删减或修改。

    6.       当语言二义性完全消除之后,用实线圈出每一组聚合根,每一个实体的外部系统单独圈出,并给每一个圈按照“XXX界限上下文” 的方式进行抽象和命名。

    注意点:聚合根和实体如何归类,体现在对业务的理解不一样,归类方式会不同,没有唯一答案,关键是从业务理解上达成一致。所以不断澄清业务概念和消除二义性非常关键。

    下面基于“项目管理系统”三条业务流抽象的界限上下文模型如下:

    图13.     界限上下文图

    ---------③建模过程VII:界限上下文依赖---------

    限界上下文依赖关系,是为了通过分析依赖关系,提前识别依赖矛盾,减少低级设计错误的手段。每一个限界上下文都不会带有全量信息,那么补充信息的来源方向就是依赖方向,或者叫“知道(known)”的方向(我需要知道它的存在)。利用带箭头的实线,以依赖方向为箭头方向,绘制不同限界上下文间的依赖关系,只对有直接依赖关系的两个上下文画线。在此过程中,我们思考的应该是业务概念层面的依赖关系,而非系统实现。

    若出现以下依赖关系,需要思考是否存在未澄清的问题:

    ①   双向依赖:上下文之间可能存在一个新的上下文未被澄清、两个上下文可被合并;

    ②   循环依赖:任何一个上下文发生变更,依赖链条上的上下文均需要改变,某个或者多个上下文的边界过大。

    下面基于“项目管理系统”三条业务流抽象的界限上下文依赖模型如下:

    图14.     界限上下文依赖关系图

    ---------③建模过程VIII:子域---------

    子域(Subdomain),是对问题域的澄清和划分,同时也是对于资源投入优先级的重要参考,利用虚线,将解决同一个业务问题的限界上下文以切割图像的方式划在一起,并以“XXX子域”的形式对每个子域进行命名。子域的类型有:

    •       核心域(Core Domain):是当前产品的核心差异化竞争力,是整个业务的盈利来源和基石,如果核心域不存在,那么整个业务就不能运作。对于核心域,需要投入  优势的资源(包括能力高的人),和做严谨良好的设计。

    •       支撑域(Supporting Subdomain):该类问题解决的是支撑核心域运作的问题,其重要程度不如核心域,但具备强烈的个性化需求,难以在业内找到现成的解决方案,需要专门的团队定制开发。

    •       通用域(Generic Subdomain):该类问题在界内非常常见,所以很可能有现成的解决方案,通过购买或简单修 改的方式就可以使用。

    要点:

    •       可以先识别核心域,再识别通用域,这样的话最后剩下的就全都是支撑域

    •       一个子域可以包含多个限界上下文,一个限界上下文不应跨越多个子域

    •       问题子域解决的是问题澄清和优先级排序问题,限界上下文解决的是业务边界识别和统一语言的问题,两者不是隶属或者包含关系。

    •       子域类型的判断,会随着视角的切换而变化,例如从全局视角识别的支撑域,可能从负责开发的团队视角来看,是该团队的核心域。

    •       对于小模块的重构本步骤可以省略

    下面基于“项目管理系统”的业务流抽象的子域模型如下:

    图15.     子域划分图

    ---------④实战项目案例总结---------

    本节,我将以大数据安全的CIS业务的资产管理模块的领域战略建模为例,和大家一起探讨实战项目中的建模过程。

    CIS产品的资产管理模块代码规模4.2万行,核心代码有4个子项目组成。主要负责针对金融、网安、政府、运营商等大、中、小型企业网络中大约24类设备进行管理,在CIS产品将海量网络基础数据采集后,进行大数据实时及离线分析,最终通过资产维度将分析结果呈现给企业用户,呈现企业IT资产的网络安全状况全貌。因为涉及到大数据平台,安全分析平台等众多子系统,在产品的演进过程中,资产管理模块逐渐变得腐朽,不可维护,系统在团队多个人的维护下,各领域只理解小片段模块代码,看不到产品全貌以及存在的问题,为了提升资产管理的业务可扩展性,急需进行资产管理领域模型识别问题,消除腐朽盘活演进。

    本次资产管理建模的目的是为重构工作指引方面。函数级及以下的重构活动完成后,势必进入模块设计的调整阶段。模块设计的调整最有效的输入就是“模块的模型”。少则几千行、多则上万、甚至几万行的模块,没有模型,任何人想深入了解它都很难,就像盲人摸象。模块的模型就是复杂模块代码的可视化呈现,重构模型,就会有效指导我们的代码重构,少走弯路。未来,新增、修改需求时,模型就是模块设计的防护网,有效阻止腐化。

    接下来我们一起应用事件风暴方法,梳理CIS业务资产管理模块的领域模型(战略建模)。

    建模过程I:识别业务流

    首先,召集业务领域专家、MKT专家一起通过分析遗留系统和头脑风暴的方法,识别了模块对外的依赖关系。这些依赖关系将指引我们进一步展开业务流分析。

    图16.     图X 资产管理系统周边依赖图

    然后按上图中,从资产管理系统管理员、第三方系统(同步)等使用资产管理系统的角色起点,通过分析遗留系统、业务知识头脑风暴等方法,按时间发生顺序梳理业务流。

    图17.     图X 资产管理系统管理管理员的业务流图

     

    图18.     图X 第三方系统(同步)的业务流图

    图19.     图X CIS系统内部模块与资产管理系统的业务流

    注:虽然业务流以时间为主线组织,但也会存在单点的业务流。系统内部模块与资产管理系统的业务流就是典型的这种场景。

    建模过程2:识别领域事件等7个元素

    基于分析出的业务流,按“图5业务流节点的组成元素与领域模型关键元素的对应关系”,逐步梳理出各业务节点的领域模型元素。

    1.         首先,识别领域事件、决策命令、实体、聚合。

    图20.     图X 资产管理系统管理管理员的业务流图(补齐4元素)

     

    图21.     第三方系统(同步)的业务流图(补齐4元素)

    图22.     CIS系统内部模块与资产管理系统的业务流(补齐4元素)

     

    2.         然后根据聚合根和实体采用亲合图的方式,识别界限上下文及相关依赖。

    ①     建立实体和聚合的关系

    图23.     图X 实体和聚合的关系图

     

    ②     根据聚合根、实体的亲合关系识别界限上下文和依赖关系

    图24.     图X界限上下文和依赖关系图

    上图分析出来后,评审发现管理员属于自然人,要对资产管理系统操作,需要在系统中指定管理员角色,并分配权限,所以针对管理员的管理按事件风暴方法细化如下(不再详述过程):

    图25.     完整的界限上下文和依赖关系图

     

    3.         按界限上下文对资产管理的重要性和亲合关系对界限上下文分类,形成子域。子域类型的确定参考:“③建模过程VIII:子域”章节。

    图26.     子域划分关系图

    总结

    通过领域建模的战略建模过程,使业务领域专家、软件开发人员等所有参与分析的人员都更深入理解了业务。输出的各领域模型图将有效可视化呈现了业务需求和软件的抽象结构,为后续新需求的增量开发、架构演进、遗留系统重构等活动高质量、高效完成,提供了有力保障。

  • 相关阅读:
    R语言 which() 、 which.min() 、 which.max() 函数
    R rep() 函数
    R语言 一个向量的值分派给另一个向量
    R语言 sample抽样函数
    超参数 hyperparameters
    随机游走模型(Random Walk)
    随机数
    Lambda 函数与表达式
    static
    变量的申明定义
  • 原文地址:https://www.cnblogs.com/gongxianjin/p/16418697.html
Copyright © 2020-2023  润新知