zoukankan      html  css  js  c++  java
  • 理解限界上下文

    理解限界上下文

    上一篇:《IDDD 实现领域驱动设计-理解领域和子域

    《实现领域驱动设计》前两章内容,基本上读完了,和《领域驱动设计》不同的是,它把很多的概念都放在前面进行讲述了,比如领域精炼、界限上下文等等,在《领域驱动设计》中,是很靠后的内容,不过这样也好,可以让你从一个大局的视角去看待问题,由广到细的思路学习,我觉得也蛮好的。另外,随着一点一点的学习,你会发现,领域驱动设计越来越有意思了,有很多“新鲜”的东西等待发现。


    一张很重要的图(无意间搜到),引自:《Implementing DDD Reading - Strategic Design

    战略建模(Strategic Modeling)和战术建模(Tactical Modeling)

    战略建模和战术建模,其实是《实现领域驱动设计》最前面的内容,位于《如何使用本书》部分,当时看的时候并没有很注意,但在前两章的内容中,发现有很多这样的字眼:“团队有人花额外的时间去了解战术模式、团队采用的是战略模式的建模方式。。。”,这就不得不让你回过头看下,什么是战略建模和战术建模?其实,关于这两点,作者并没有很准确的进行定义,只是分别描述了这两点内容的关键字,我们来总结一下:

    • 战略建模:界限上下文(Bounded Context)、上下文映射图(Context Mapping)。
    • 战术建模:聚合(Aggregate)、实体(Entity)、值对象(Value Objects)、资源库(Repository)、领域服务(Domain Services)、领域事件(Domain Events)、模块(Modules)。

    像聚合、实体、值对象等,都可以称之为战术建模的工具,战略建模和战术建模的区别,你可以从字面上进行理解,战略的意思,就是从大局出发,是一种运筹帷幄的感觉,那为什么和界限上下文有关呢?在《理解领域和子域》中,有一张很重要的图,领域是业务系统的全部,其中包含核心域、子域和通用子域,相对应的就是限界上下文,你可以把某一块的领域和限界上下文进行映射,他们都是通用语言的一种表述,在项目之初,领域专家和开发人员的工作就是探讨限界上下文的划定,这个非常重要,如果限界上下文的划定有问题,那么将来战术建模的进行将“一塌糊涂”,就像作者一个例子一样,团队成员将用户和权限限界上下文划到具体的子域中实现,最后导致了一系列的问题,后来,团队发现问题后,将用户和权限限界上下文重新定义为身份和安全限界上下文,并划分到通用子域中,最后的效果显而易见,避免了很多问题的发生,也增加了业务系统的灵活性。

    如果你注意的话,会发现上面说的只是“纸面”上的探讨,也就是说都没有进行实施,所以才称之为战略建模,而战术建模可以理解为战略建模的实现,前提是界限上下文都已经划定好,并确定无误。

    问题空间(Problem Space)和解决方案空间(Solution Space)

    和战略建模、战术建模一样,又是一个概念性的问题,在问题空间中,我们思考的是业务所面临的问题和挑战,而在解决方案空间中,我们思考的是如何实现软件以解决这些业务挑战。

    具体什么意思呢?其实,问题空间和战略建模的概念有些类似,但只是思考的方式类似,他们是两个不同的概念,在上面图中,问题空间包括两部分:业务所面临的挑战、核心域+其他子域的组合,注意其中并不包含限定上下文的划分,领域专家和开发人员在探讨领域的设计中,首先,就是对问题空间的探讨,用来确定核心域和其他子域,并列出业务系统中可能会存在的一些问题。

    在上面图中,解决方案空间包含的内容很多,它是什么的解决方案?其实就是针对问题空间的解决方案,当问题空间被确定下来后,我们就会对核心域以及其他子域进行探讨和实施,然后在其中划分出很多的限界上下文,并用软件的方式进行实现。

    如果这样进行思考,你会发现,问题空间和解决方案空间对应于战略建模和战术建模,他们之间是有一些相似处,比如一个是探讨、战略,一个是实施、实现,但还是有些不同,比如界限上下文是战略建模中的概念,对应与问题空间和解决方案空间,界限上下文却是解决方案空间中的的概念,可以说问题空间和解决方案空间涵盖的东西很多,像战术建模就可以看作是解决方案空间实施的一种手段。

    问题空间和解决方案空间,你可以不把它看作是领域驱动设计中的概念,因为在原著《领域驱动设计》中并没有这些概念,并不是说没有就不重要,在实现领域驱动设计中,还是非常重要的,你可以把它看作是一种思考的方式,就像你切一个西瓜,横切、竖切、还是直接用拳头爆掉,这些方式都可以,不管怎么实施,只要最后能吃到西瓜就行。对于领域专家和开发人员所建立的通用语言,到底该如何沟通,或者相互直接如何表达?我觉得探讨问题空间和解决方案空间,是一个很好的方式,你可以把他们看作是切西瓜的“刀”,很锋利,也高效。

    理解限界上下文(Bounded Context)

    上面的四点概念,在领域驱动设计的时候,可以不必了解,因为它只是实现领域驱动设计的一种概念方式,理解它也只不过可以让你少走些弯路,你完全可以按照自己的方式去实现,当然,偏离了大道,也怨不得别人。

    限界上下文的概念很重要,我之前在做消息项目的时候,不是很了解这个概念,只是隐约记得什么限定上下文、界限上下文,然后就是实体、值对象和领域服务了,其实最准确的名字是限界上下文,限的意思就是划分、规定,界就是界限、或者一个边界,上下文就是业务的整个流程,总的来说,可以称限界上下文为业务流程在一个划定的界限中,我们知道,业务的描述是通过通用语言来表述的,限界上下文和通用语言的关系就是:在一个特定的限界上下文只使用一套通用语言,并且保证它的清晰性和简洁性。

    上面的图来自《实现领域驱动设计》,这个图我们可以和上一篇进行对比下,在之前的团队开发中,是把身份与访问上下文划分到协作上下文中了,并导致了一系列的问题,协作上下文包含的内容有论坛、博客、及时消息、留言板等,但这些都不是核心域,核心域是敏捷项目管理,也就是一开始说的那个简单业务用例:待定项提交到冲刺中,协作上下文只不过是支撑子域,它的作用就是用来支撑敏捷项目管理上下文的,可以这样说,如果协作上下文出现了问题,并不影响这个项目的运行,顶多是影响某一模块的运行,比如待定项提交到冲刺中,这个业务操作完成后,会有一个消息通知,协作上下文出现了问题,消息通知发不出去,但是待定项是可以提交到冲刺中的,因为这两个业务操作分别处于不同的限界上下文中,也可以这样说,对于敏捷项目管理上下文,协作上下文是可以替换的。

    那限界上下文和子域有什么关系呢?在上面图中,可以看到是一一对应的,比如通用子域对应于身份与访问上下文,但其实并不是这样,请注意那个虚线,虚线表示的意思是核心域和子域的界限,但界限中很多都是空白的,比如通用子域除了包含身份与访问上下文,还可以包含消息与通知上下文、日志记录上下文等等,同样,支撑子域也是如此。

    我记得我在开发消息项目的时候,在领域层只有一个 MessageManager.Domain 项目,并且项目下有很多的文件夹,比如 Entity、Domain Service 等等,然后我就认为这个 Domain 项目,是整个消息项目的核心,并且,如果我再开发一个新的项目的时候,我也会这样做,这样有什么问题呢?好像没什么问题,因为对于消息项目,业务场景很简单,Domain 项目所代表的是整个领域层,也就是上面图中整个的概念,其实这种命名是有问题的,实体、值对象和领域服务等概念,是存在于一定的限界上下文中,而不是整个领域概念,也就是说,我当时在设计 Domain 项目的时候,就完全没有把限界上下文设计好,暴露出来最明显的一个问题,就是 Domain 项目中包含有 User 实体的概念,你明白了吧,我和作者描述的那个团队开发都犯了同一个问题。

    我们再来看一张图:

    上面是协作上下文所包含的内容,你可以看到有好多的聚合根、领域对象等等,对于协作上下文的开发,IDDD 作者的做法是,新建一个程序集项目,也就是我们所说的类库项目,这个每个限定上下文都互不影响,而不是像我那样包含在一个 Domain 项目中,分开开发更新也方便,如果限界上下文足够复杂,比如上面的协定上下文,包含的聚合根太多,我们也可以进行细分。还有个问题是,比如用户的概念,在博客、论坛、日历等场景中,所表达的概念是不同的,那我们的身份与访问上下文该如何进行设计,还有就是协定上下文中的用户概念改如何进行设计,这是一个很重要的问题,如果是我的话,我以前肯定会把用户的概念放在协定上下文中进行开发,因为消息项目我就是这么干的,但这样造成的问题也是很严重的。

    对于上面所描述的问题,我们来分析一下,不管在博客、论坛、日历等场景中,用户的概念是唯一的,也就是说它必须是唯一标识的,不能有两个同样的用户同时存在,这是首要基本条件,还有就是,用户的一些基本属性,比如用户名、邮箱、密码等等,这些在不同的场景中都是可以确定的,也都是同样存在的,对待这些共有属性,我们可以抽离出来,除了属性之外,还有一些业务操作也是公用的,比如身份验证操作,我们也同样抽离出来,对于这些抽离出来的属性和操作,我们应该在哪边进行实现?该如何实现?是在协定上下文中吗?不是,我们应该把这些用户属性和操作放在身份与访问上下文中,并进行隔离实现,为什么要进行隔离?因为身份与访问上下文是在通用子域中,也就是说并不是在支撑子域中,通用子域和核心域、其他支撑子域都有联系,也就是说,不要把协定上下文中所包含的独有用户概念,放到身份与访问上下文中进行开发,如果这样做,那么身份与访问上下文就不是通用子域了,而变成了协定上下文的一个附属上下文。

    一个模型应该要与一个上下文相适应,上下文可能是指一段代码,也可能是指特定团队的工作,如果一个模型是在一次头脑风暴会议上诞生的,那么它的上下文就可能会限制在这些讨论的范围中,在有特定意义的模型中,不管模型的上下文是什么,必须要说明模型中的术语是什么意思。以上是《领域驱动设计》中,关于模型和上下文的内容,注意,上面所说的上下文并不是限界上下文,上面所说的上下文可以是一段通用语言的表述,也可以是一段代码,如果概括的话,可以认为是限界上下文的一部分。

    关于限界上下文,我只是了解冰山一角,有太多的内容需要进行探讨学习,但不可否认,限界上下文是领域驱动设计中,最重要的概念之一,可以称之为最重要的首要概念,因为它是领域驱动设计的开始,自己肚里知识有限,我希望后面可以再次对这部分内容进行补充,最后,引用《领域驱动设计》中的一段描述:

    • 细胞膜不仅能把细胞内部和外部区分开来,而且还能决定通过的物质。

    有人会说,你搞这么多的概念有什么用?还不如直接实践来的有用,但有时候,你会发现,实践是建立在一定基础之上的。

  • 相关阅读:
    打造基于CentOS7的xfce最简工作环境
    Linux下C程序的编辑,编译和运行以及调试
    修正 XE6 TListView 上方 SearchBok 右边的清除钮显示
    TabControl 显示彩色的图示 (XE6 Firemonkey)
    TSwitch 中文简繁显示支持(XE6 Android)
    改变 TMemo 的背景颜色 (Firemonkey)
    修正 XE5 Android 键盘三个问题
    Delphi 收藏
    展示 Popup 的使用方法
    ListView 下拉更新 (支持 Android)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4343177.html
Copyright © 2011-2022 走看看