战略设计为什么重要
到现在,你已经学习了DDD的一些术语及其含义,但是我还没有怎么讲到为什么它们如此重要。我确实认为它们是重要的,同时,我也希望你在这一点上相信我。接下来,我会对此做出解释。让我们来看看SaaSOvation公司的项目进展情况,他们遇到麻烦啦。
在开始采用DDD的时候,协作项目组便开始偏离正确的轨道了,原因在于他们并不了解战略设计,甚至连战略设计的基础都不了解。就像多数开发者一样,他们将关注点放在了实体(5)和值对象(6)上,从而缺少一种更广阔的视野。他们将不同的核心概念杂揉在一起,导致他们将两个模型创建成了一个。不久之后,他们便感到痛苦了,如图2.3所示。他们还没有完全达到实施DDD的目标。
图2.3 团队不了解基本的战略设计,这导致在协作模型中产生了不相匹配的概念。虚线之内表示问题所在。
团队中有些人指出:“是不是因为用户(User)和权限(Permission)与协作概念存在着紧密的耦合?我们应该跟踪谁做了什么事情。”这时,一个高级开发者指出:“问题不单单在于耦合,到最后,论坛、讨论、日历和日历条目都会在一定程度上与协作人员发生耦合,这是事实。问题出在我们使用的语言。”他进一步指出,问题在于论坛和讨论等概念与错误的语言概念耦合起来了。用户和权限与协作活动没有任何关系,并且与协作的通用语言也风牛马不相及。用户和权限是与身份(Identity)和访问(Access)相关的概念,即是与安全(Security)相关的。在协作上下文(Collaboration Context)中出现的每一种概念都必须与协作存在语言层面上的关联,而现在它们没有。“我们应该关注的是协作概念,比如作者(Author)和主持者(Moderator),这些才是协作活动中的正确概念和语言。”
如何命名限界上下文
你注意到了这里使用的“协作上下文”一词吗?在本书中我们将以这种方式来命名限界上下文,即“模型名+上下文”。这里的“协作上下文”表明它是包含有协作领域对象的限界上下文。同时,我们还有身份与访问上下文(Identity and Access Context)和敏捷项目管理上下文(Agile Project Management Context)。
重申一遍,SaaSOvation的开发者在一开始并没有意识到用户和权限是与协作工具无关的概念。诚然,他们的软件中是有用户的,但是我们应该将不同的用户种类区别对待,因为在不同的上下文中他们所完成的任务是不一样的。在协作工具中,我们更关注的是用户的角色,而不是他们是谁或者他们的权限如何。然而,在当前这个例子中,SaaSOvation的开发人员将协作模型与用户和权限完全揉合在一起了,如果系统对用户或权限的处理方式有所修改,这也将导致对协作模型的修改。事实上,这正是他们所遇到的问题——开发团队决定从权限管理方式切换成基于角色的访问管理方式。在决定切换时,他们才意识到这个由错误的战略建模所带来的问题。
开发人员现在明白了,论坛和“谁可以发表帖子,还有在什么条件下可以发表”其实没有多大关系。论坛只需要知道“有作者正在发表帖子,或者有作者曾经发表过帖子”就可以了。于是团队成员学到了:决定谁可以做什么事情其实是由另外一个完全不同的模型来负责的,在协作模型中,我们只需要知道这样的问题已经被回答过就行了。在论坛中,我们关注的是:一个作者发起一次讨论。这里的“论坛”和“作者”便是通用语言中的两个清楚概念,使用该通用语言的协作模型便是协作上下文。用户和权限,或者其他相似的概念,比如角色,应该属于完全不同的上下文,并且需要和协作上下文分离。
开发团队可以轻易地得出结论:此时需要做的只是将用户和权限带来的紧耦合去除就可以了。毕竟,将用户和权限分离到另外的模块并没有什么错误的地方。这可以帮助团队将这些概念放到同一个限界上下文的另外一个子域中——一个逻辑上的安全子域。然而,最好的方式是将用户和权限放在支撑子域或者通用子域中,因为另外的核心域也可能会用到相似的功能。
因此,这种周全的考虑可以帮助他们避免更多潜在的问题。他们先前的做法还很有可能导致大泥球(Big Ball of Mud,3)架构。这里的问题并不仅仅是由于用户和权限没有得到适当的模块化。虽然模块化是一种重要的DDD建模工具,但是它并没有解决语言上的问题。
让这个高级开发者担心的是,项目当前的情形很有可能导致一种散漫的思维模式。当团队面对协作模型之外的一套概念时,核心域将变得越来越模糊。结果,他们得到的只是一个并不能反映协作通用语言的隐式模型。团队真正需要理解的是他们正在开发的领域、子域和限界上下文,他们需要战略建模的思维模式。
别啊,怎么又是“设计”这个词!
你可以认为“设计”在敏捷中是个丑陋的词汇,但是这种看法却不适用于DDD。在敏捷中使用DDD是很自然的一种做法。我们需要将设计敏捷化,而设计不见得就一定是笨重的。
好了,到这里,SaaSOvation的团队成员学到了不少。他们展开了很多研讨,最后知道了处理领域和子域的重要性,我们将在后续章节中学到他们是如何做到这一点的。
向DDD社区看齐
本书的SaaOvation例子提供了3个限界上下文。这些限界上下文可能和你自己领域存在不同。这些例子展示的是非常典型的建模场景。然而,并不是所有人都同意将用户和权限从核心域中分离。可能在有些时候将它们放在核心域里面是有道理的。和通常一样,这只是一个团队自己的选择。但是,据我自己的经验,这是那些DDD新手们经常会遇到的问题之一,结果他们把实现搞得一团糟。另一种常见的错误是,将协作模型和敏捷项目管理模型混为一谈。这些只是常见问题中的一小部分,另外的建模错误我们将在其他章节中讲到。
这些常见的建模错误是由于团队缺乏对通用语言和限界上下文的理解造成的。因此,即便你并不同意SaaSOvation例子中那些特殊问题,这些问题及其解决方法对于所有的DDD项目都是适用的,因为他们都关注于某个特定限界上下文中的通用语言。
我的目标是通过最简单的例子讲解实现DDD的基本原则。如果这些例子妨碍到了我的教授和读者的学习,那我是负担不起的。如果我讲到:身份及访问管理、协作和敏捷项目管理都有自己的语言,那么读者便可以从这些例子的着重点中获益。每个团队都可以自己选择如何去发现适合自己领域的语言,并传达领域专家的思维模型。这里我们假定,SaaSOvation开发团队最终得出的结论是正确的。
关于子域和限界上下文,我所有的指导建议都和DDD社区紧密地保持一致。其他的DDD领导者可能有不同的关注点。然而,我所讲的是适用于任何团队的DDD基础。澄清对于DDD的一些模糊认识是我的主要目标,而你的目标则是将本书中的DDD指导建议切实地应用在自己的项目中。