“领域驱动设计”答疑(一)

问题:领域驱动设计的核心是“领域建模”,而软件如何进行”建模“这件事在二十年前,面向对象设计就已给出完整的方法论了。领域驱动设计所说的“领域建模”和面向对象建模有哪些不同呢?

先回答问题:
领域驱动设计强调要建立“领域模型”,描述了这个模型能解决什么问题,有哪些特点,但是并没有给出系统化的建模方法,这给了大家很多的发挥空间。

但是不管社区为领域驱动设计引入的建模方式再五花八门,面向对象分析设计建模依然是当前最系统和成熟的方法。只是区别在于:领域驱动设计不再将模型割裂为分析模型、设计模型和实现模型,而是用一个领域模型贯穿设计和实现,并强调代码与模型要保持一致。

另外领域驱动设计建议采用敏捷的“演进式设计”方法逐步设计、演进和精炼领域模型,这需要相应的团队协作方式(业务专家和软件团队长期协作)和对应的演进式工程能力(流水线、自动化测试、重构、简单设计...)做基础。


以下是一些补充,感兴趣的话可以看。

在面向对象分析设计中,软件开发被明确分为面向对象分析(OOA),面向对象设计(OOD)和面向对象编码(OOP)三个阶段,它们逐级对设计进行演进和细化。

OOA由业务分析师负责,以需求出发寻找和抽象对解决问题有价值的概念、关系和约束,并对其进行合理组织,形成分析模型。由于分析模型中的概念是偏向业务的,因此易于被业务专家所理解,但是还不能直接指导代码实现。

随后软件架构师将分析模型按照面向对象设计范式的要求进行转化和细化,并考虑更多非功能约束,例如部署、并发、组织分工等(例如典型的4+1视图),进一步得到设计模型,这是OOD过程。

之后开发人员参考设计模型,根据所选编程语言约束,将设计模型转化为具体的代码实现,就是最后的OOP过程。

OOD和OOP两个阶段会递进补充被OOA阶段忽略的和领域无关但是和软件开发息息相关的因素(软硬件平台、部署约束、数据存储约束、并发、编程语言、编程框架...)。

上述面向对象建模过程和方法的价值在于规范了面向对象软件设计的流程(OOA->OOD->OOP)和每个阶段建模的最佳实践和以及模型的标准表示法(UML)。

2001年OMG(Object Management Group)定义的MDA(Model-Driven Architecture)进一步标准化了模型驱动的设计过程和方法。MDA通过标准化建模过程和模型表示法,让模型和具体的实现技术分离,最后代码可以从模型自动生成。

MDA在变化相对较慢,模型关系比较稳定的软件领域取得了有效应用。例如通讯设备软件中广泛使用的MO(Managed Object)建模,抽象了设备中的可管理对象、属性、关系和校验约束,并将其按照一定的标准进行模型化描述,因此对设备进行操作维护管理的部分代码就可以通过模型自动生成。而事实也证明了基于MO模型的代码的可理解性、可复用性和可继承性明显好于其它代码。

遗憾的是上述面向对象统一建模过程在变化频繁的软件领域并没有得到很好的应用。一个主要原因是上述建模过程和方法都是在瀑布式开发占主流的时期提出的,明显看到面向对象三个阶段的设计过程,以及将模型从上往下逐级抛接和转化的方式,恰好为了迎合瀑布式开发方法。

瀑布式开发方法的问题在于其过长的反馈周期和繁重的流程并不适合快速变化的软件领域。恰好2000年前后主流软件行业进入企业应用和互联网领域,这两个领域本身需求变化快、不确定性强,需要软件开发能够通过快速迭代不断获得反馈并进行调整。同时各种具体的实现技术也发展得越来越快,各种软件技术(框架、存储、并发,部署、运维...)在同时快速的演进和变化。

另外一个问题是,面向对象建模方法中存在多个阶段的模型:分析模型、设计模型和实际代码中的实现模型。这三个模型从偏向业务逐步演进到偏向实现,最终代码里的实现模型和最初的分析模型差异较大。而且需求变更需要从上往下在这三个模型间进行设计转化,沟通成本大,上游模型对下游代码的指导有效性也衰减严重。

MDA尝试将模型之间的转化标准化,做到从模型到代码的自动生成。但是由于每个阶段模型的关注点不同,每个关注点的实现技术同时也在快速的技术演进,所以标准化工作不仅滞后而且难以实用。因此在需求或实现技术快速变化的领域,软件开发团队即使有初始的建模设计,最后也都会走向抛弃模型,直接修改代码的道路。所以上述情况中,大多数团队的代码和模型不用多久就会分道扬镳,流程繁重的面向对象建模过程随即被团队扔进了角落。

随后如我们所知,在要求软件快速响应变化的背景下,敏捷软件开发方法出现了。

敏捷方法如XP(极限编程)给出的建议是软件开发中只有一个权威的模型,那就是代码。通过简单设计、持续重构、CleanCode等技术手段,保持代码自身是自注释、易理解的,团队成员以清晰自注释的代码作为沟通的核心。另外,敏捷方法建议业务分析师和架构师要在开发全程紧密的配合,保持频繁的面对面沟通。

敏捷开发方法在需要快速响应变化的软件行业取得了很好的实践结果,简单设计、持续重构、开发者测试、持续集成与持续交付等敏捷实践都成为了现代软件工程的核心能力。然而敏捷方法在很多业务门槛相对较高的领域应用时却遇到了一点点问题。

高业务门槛的行业往往业务知识复杂、软件规模大、对非功能特性要求比较高,但是变化却相对受控。如果业务专家和软件专家的知识背景差异比较大,直接就代码进行沟通其实是不容易达成共识的,而且也容易过早让沟通落入实现细节。

另外即使只考虑软件开发团队内部,也需要有一个架构共识能穿越一个一个的迭代周期,帮助开发成员在同一套所有权代码之上长期保持高效的协作。而极限编程(XP)给出的解决方案“隐喻”正如它的名字一样充满了禅学而难以被人理解和应用。

于是,领域驱动设计出现了。

领域驱动设计指出模型设计是非常有价值的!但是它摒弃了瀑布式的多阶段多模型的设计方式,而是建议软件在分析、设计和编码阶段应该围绕一个统一的模型,这个模型被称之为“领域模型”。领域模型应该沉淀业务的核心价值差异,由业务专家和软件团队共同设计建立。领域模型承载了软件在其完整生命周期中的概念一致性,降低了软件开发中的沟通和修改成本。最后领域驱动设计要求领域模型对应的代码应该是与模型持续保持高度一致。

领域驱动设计同时也强调了领域模型应该通过敏捷倡导的“演进式设计”得到,并确保持续的演进和精炼。

领域驱动设计给出的一系列方法和实践帮助取得上述目标:

  • 强调业务专家和软件团队的协作方式。他们需要共同参与软件设计,以得到表达共识的领域模型。其实就是敏捷中所倡导的全功能团队,只是没有强制业务专家全部时间参与开发。

  • 强调保持领域模型的纯粹性。人们需要区分核心域、通用域与支撑域,优先面向核心域建模。为了保持概念一致性,领域模型需要被划分到不同的限界上下文(BC:Bounded Context)中。每个BC有自己独立的领域模型,不同的模型之间通过一定的上下文映射(Context Map)进行合作。所有这些在领域驱动设计中通过“战略设计”进行描述。

  • 强调领域模型对应的代码应该是被严格隔离出来的,并保持与模型的高度一致性。领域驱动设计推荐使用分层架构或者解耦更纯粹的六边形架构,将领域模型和其它支撑代码(UI,DB,通讯等)进行解耦。同时领域驱动设计给出常用的建模元素分类(Entity、Value Object、Aggregate、Repository、Factory、Service、Event),用来标准化模型元素的分类和设计原则。上述这些在领域驱动设计中通过”战术设计“进行描述。

  • 强调了领域模型是需要演进的,通过不断的“演进式设计”和“重构”,保持模型和代码的持续演进和精炼。

遗憾的是,很多同学学习领域驱动设计,即使掌握了所谓的“战略设计”和“战术设计”,也不容易设计出好的领域模型,或者让代码和模型保持一致。这是因为讲领域驱动设计的书和教程虽然强调领域模型,其实并没有很系统的给出建模的具体方法和技术。

事实上领域驱动设计要想很好落地还需要以下几项核心技能作为辅助:

  • 面向对象分析和设计能力:领域模型不是凭空想出来的。它一定是按照工程化的可重复的方式设计出来的。到现在为止软件开发中最成熟和系统的建模技术仍然来自于面向对象技术,因此掌握面向对象分析和设计能力必不可少。另外虽然领域模型更加强调其共识性而非呈现方式,但是不可否认UML仍然是最成熟和标准的表达方式。

  • 面向模型的代码实现模式:代码要清晰的表达模型,与模型保持一致,是有一定的原则和实现模式的。领域驱动设计虽然给了模型元素的分类方式,但是这并不足以指导设计出好的领域模型以及写出能清晰表达模型的代码。每种高级编程语言都有一套表达模型的最佳方式,掌握面向模型的实现模式,可以让自己的代码更显示化的表达模型。

  • 演进式设计:领域模型要演进,每个迭代都要增量的调整模型和代码,这需要“演进式设计”能力做支撑。演进式设计要求团队具备相应的软件工程能力,包括快速反馈的自动化流水线,以及团队成员具备简单设计、编写开发者测试以及高效重构代码的能力。这样模型设计和代码的演进才能低成本、低风险,否则就难以落地。

这就是“《领域驱动设计》的书为什么看起来比较晦涩?”,因为它缺少了一些必要的铺垫和技术,或者是认为这些是大家默认应该具备的背景知识和能力。


《“领域驱动设计”答疑(二)》

《“领域驱动设计”答疑(汇总)》

你可能感兴趣的:(“领域驱动设计”答疑(一))