上章回顾:
上一章,通过投资管理项目,分析领域的划分规则,如何确定界限上下文实现系统边界的划分,多个界限上下文之间的关联关系。
领域建模
通过上章的学习,我们已经知道领域驱动设计有了一定的了解,在战略层面,首先需要通过和业务专家一起对整个业务系统进行建模,建模的目的是为了建立真实世界到软件世界的映射。通过不断的讨论,探索和实践,来对现实世界中的事物,行为进行建模。
面向对象的软件开发中,有一个很重要的概念就是抽象,同样对于任何系统来说,不管是从业务视角,数据视角来看,都需要对系统进行分层设计。对于DDD来说,就是进行领域的划分,有什么原则来支持领域的划分,领域之间如何工作?
原则一 高内聚原则
提炼问题领域以明确什么是重要的?开发团队和领域专家使用分析模式和知识消化的过程,将大型问题域提炼为更易于管理的子域。核心子域能在这种提炼中被揭示出来,并能解释软件编写的原因。核心领域是正在开发的产品背后的驱动力;DDD强调需要将精力和人才集中在核心子域上,因为这才是最有价值的地方,也是软件成功的关键。
原则二 关注点分离
思考层面的关注点分离。人脑的思考负载是承受不了整个非常复杂的领域的,必须得把它拆分,同一时间只思考其中一个方面,别的方面先忽略掉,不然就会陷入思考瘫痪的情况,无从下手。基于事件风暴来开展领域驱动设计,肯定也是基于每一个业务场景来做的,不可能同时做,我也不知道怎么同时做。思考层面的关注点分离必然会导致最后架构设计上的职责分离。架构上的职责分离,就是指系统的分层,分治,其实就是老生常谈的高内聚。DDD里面,战术层面识别聚合,战略层面划分子域与限界上下文,都是为了追求架构设计上的分层,分治 。至于职责分离能够做到什么程度,需要不断的练习,积累经验了。
原则三 单一职责
首先需要创建解决问题的模型,将大模型拆分为较小的模型,并逐一定义在单独的限界上下文中,来减少存在二义性的术语,避免多个团队在一个模型内并行工作,以此降低复杂度。限界上下文定义了模型的适用性,能确保其完整性得以保留,并能在模型周围形成保护边界,有助于防止软件演变成“大泥球”。整个限界上下文是逻辑自洽的。
原则四 重点关注成效而非产出
如何一个新系统,都需要考虑交付和扩展性之间的平衡,产品早期追求的是快速迭代,快速试错,找到MVP,而不是ALL In ONE。基于此,我们不需要精心设计第一个版本。即使持续不断地投入精力,前景也可能是不确定的。这也是为什么此时交付的速度的优先级,会高于灵活的设计。如果产品是成功的,那么软件就具有长期投资价值,此时可以考虑进行架构重构来支持演化,解决早期实现版本积累的技术债务,这样才能支撑长期的快速敏捷的持续交付和迭代。另外,要确保解决方案解决核心问题,MVP需要足够聚焦,用最小的成本和投入来进行产品和市场的验证。
原则五 替换而非重用
在子域中构建模型时,务必尝试单独构建,并确保它们是可以被替换的。通过使用干净的边界,使它们与其他模型、遗留代码和第三方服务分开。通过替换而非重用的方式,可以创建足够好的支持子域,而不会浪费精力来完善它们。
原则六 持续迭代
领域模型建模本身也是存在风险的,特别是复杂系统,随着对业务的理解的加深,可能早期的模型并不一定被认为是合理的,所以在建模过程中,需要持续的思考和迭代,甚至是颠覆式的。迭代的目的是找到更加接近真实场景的模型。Eric Evans创建了一个名为漩涡式的模型探索,就是解决这个问题;
原则七 全局优化
协作完成一个解决方案的多个团队,应该使用以下方式来组织,他们之间是松耦合的,且尽可能自治。减少团队对外的依赖,能够使他们加速。理想情况下,团队的划分,应该与业务能力对齐。这样团队能创造业务能力,并能完成整体解决方案。所有团队共担整个系统和流程的责任,要高于各个孤立团队的局部优化;
为需要集成的子系统定义团队所有权,并进行管理,对于确保以预期的方式按时完成功能,至关重要。上下文地图旨在调查和澄清。虽然可能无法一上来就绘制一份清晰的上下文地图,但是在绘制的过程中,可以澄清责任、明确定义模糊的概念并理解沟通流程。
业务流程的灰色地带,有时候根据限界上下文地图,往往存在一些灰色地带的业务流程,这些灰色地带并没有明确的边界和职责,没有被管理起来,但是在整个流程中又非常重要,不可或缺,所以需要识别管理业务流程的上下文之间经常出现的灰色区域,并尽早解决问责制所带来的简单假设下的所有权机制问题
面向业务变化的架构设计
架构设计的本质到底是什么???
- 设计首先是要解决问题的复杂度。
- 设计其次是要建立团队协作沟通的共识。
假设我们产生了一个团队都达成共识的架构设计,大家都兢兢业业把设计变成了现实。一个长期困扰软件行业的问题:需求总是在变化,无论预先设计如何“精确”,总是发现下一个坑就在不远处。相信很多人都有这样的经历,结果往往是情况越来越糟糕,也就是我们常说的架构腐化了,最后大家不得不接受重写。这些经历让我们逐步明确了软件架构设计的实质是让系统能够更快地响应外界业务的变化,并且使得系统能够持续演进。在遇到变化时不需要从头开始,保证实现成本得到有效控制。
软件架构设计的实质是让系统能够更快地响应外界业务的变化,并且使得系统能够持续演进 目标很清晰,但是想要做到确并不容易,当今互联网架构中,组件化或者微服务化渐渐变为一种标杆。
- 组件划分尽量靠近变化的原点,对于大多数互联网就是用户和业务,这样的划分能够让我们将变化“隔离”在一定的范围内,从而有效减少改变点。
- 组件之间能够互相调用,但彼此之间不应该有强依赖,即各自完成的业务是相对独立的,不会因为一方掉线而牵连另外一方。
- 组件在业务上是鼓励复用的。这也是目前大多数中间件大行其道的原因,确实给业务开发带来了方便。
面向业务变化而架构就要求首先理解业务的核心问题,即有针对性地进行关注点分离来找到相对内聚的业务活动形成子问题域。子问题域内部是相对稳定的,即未来的变化频率不会很高,而子问题边界是很容易变化的;
在资产管理系统的微服务架构升级中,我们首先将最稳定的用户中心和资产中心进行分离,用户中心负责用户的登录注册,用户身份相关,资产中心专注于资产的CURD,以及资产的每日总余额和昨日收益的计算。
用户中心经过多个版本的迭代开发,逐渐形成了三大子领域上下文,分别是登录模块上下文,设备中心上下文,用户信息中心上下文,其中登录模块上下文和用户信息中心上下文是核心领域,设备中心上下文是支撑领域,主要为用户提供设备唯一性校验,这个功能在后期活动中很有效,特别是手机上的邀请类活动,奖励丰厚,如果只是按照手机,用户唯一,很容易作弊,加上唯一设备能大大增加作弊成本,对活动参与者更加的公平友好。
早期的用户信息,都是传统意义上的手机,昵称,邮箱等,大多数都是UR操作。后来逐渐增加了用户等级和积分上下文系统。以及和积分有关的积分活动上下文,通过完成每日的积分活动来获取积分,从而提升用户等级和荣誉系统,并且为候选的红包活动埋下伏笔。
随着拆分出这么多的上下文,但是在早期主要还是采用共享数据库的方式,虽然服务之间进行了为服务化,但是在数据库层面还是共享的,只是采用了分用户分权限设计来达到数据库层面的隔离。而没有采取一开始就是分裤分表的方式。比较这样带来的改造成本相对更小,带来的风险也更低。