第11章 有界上下文集成介绍
如何集成组成分布式系统的有界上下文
构建分布式系统所固有的基本挑战
理解面现服务架构-SOA如何才能有助于构建松散耦合的有界上下文和独立团队。
在保持显式的事件驱动领域模型的同时使用功能响应式DDD应对非功能性需求。
一大挑战是成功地设计一个强健的分布式系统。
分布式计算中的基础概念,它们允许你在妥善处理非功能性需求的同时保留显式领域概念,比如可扩展性和可靠性等。
技术挑战知识 集成有界上下文和构建分布式系统的其中一部分,社会化挑战是剩余部分。
团队合作和沟通模式,成功团队利用领域驱动设计DDD将其用于构建高可扩展性系统。
一种常用模式是面向服务的框架,SOA.
事件驱动的系统将带来挑战,最明显的就是他们需要开发人员对如何设计其系统进行另类思考,并且还要促成最终一致性。
还将涉及操作型问题,比如 监控服务等级协议SLA和错误。
- 如何集成有界上下文
审慎而明智地选择边界和通信协议会让每个团队都能在彼此不妨碍的情况下独立工作。
-
- 有界上下文是独立自主的
有界上下文的松耦合,很可能会减少瓶颈,减少冲突,并且业务价值得到更快速和更高效创建的几率会更高。
-
- 在代码层面集成有界上下文的挑战
在DDD中,你可以使用SRP(单一职责原则)来将独立的业务功能隔离成独立的有界上下文。也可以将你的有界上下文当做一个解决方案中的独立模块/项目来使用。
1 | 多个有界上下文存在一个解决方案中 |
| 保持有界上下文独立的命名空间或项目;以下呈现了,多个独立的有界上下文的单个解决方案。Common类的变更困难性。 |
| 通过数据库进行集成 减缓团队进度的另一个常见的依赖关系就是数据库。 另一个问题,通过数据库集成的每一个模型都具有相似但又不同的领域概念。 从本质上讲,在具有不同语义的模型之间共享一个结构可能会违背SRP。 |
| 在单个代码库中工作的多个团队 如果你将你的领域划分成多个有界上下文,每一个都有自己的代码库,则你将杜绝一整类的组织问题。其中,大的项目源码将会有大量的WIP Task(进行中的任务),导致无法快速得到快速持续集成和发布。 如今许多公司都是用每天多次向顾客持续交付和部署价值的方法,多个团队共享单一代码库会造成业务的高成本支出的情况就异常明显。 |
| 对模糊不清的情况建模 如果你有一个复杂的领域,它实际上具有多个有界上下文,但你又只有一个代码库,则不可避免的是,每个模型的边界将无法保持完整。 副本的问题,同步重复修改的,以及遗漏的可能。但是DRY原则不是必须遵守的。 |
|
|
|
|
-
- 使用物理边界来强制实现整洁的模型
为了保留有界上下文的完整性并确保他们是自主的,最广泛应用的方法就是使用一个零共享架构,其中每一个有界上下文都有其自己的代码库,数据存储以及开发团队。
-
- 集成遗留系统
当你面对集成由遗留代码构成的有界上下文的限制时,有大量的模式你可以用来限制遗留代码对系统其它部分的影响。这些模式会帮助你管理复杂性并避免出现你必须降低新代码的明确性以集成遗留组件的情况。
1 | 气泡上下文 记住,当你具有对领域模型的完全控制并且随着你获得了新领域见解从而能够随意对其频繁迭代的时候,才能得到DDD的最佳效果。 为了让气泡上下文变得有效,气泡和遗留模型之间必须有一个转译层。防止损坏层ACL这一DDD概念满足这一需求的理想情况。 |
2 | 自主性气泡上下文 如果你希望继承遗留代码,但又不希望创建太多遗留代码的气泡上下文,那么你就可以转而使用一个自主性气泡。尽管气泡上下文可以从遗留系统中得到其所有数据,但自主性气泡更独立,他有自己的数据存储并且能够脱离遗留代码或其他有界上下文运行。 |
3 | 将遗留系统作为服务公开 当有多个ACL需要创建的时候,一个常用的简单方法是,公开返回JSON的HTTP API,其正式名字是开放式主机模式。 这种模式的两个缺点: 第二,公开一种能轻易被多个使用方使用的格式可能会是一项挑战。 |
|
|
|
|
|
|
|
|
- 集成分布式有界上下文
企业正在使用云托管解决方案快速有效地控制其系统的成本。
现代系统
要分布式部署到另一个关键原因是容错功能。
-
- 集成用于分布式有界上下文的策略
不同的分布式解决方案,即若干不同的集成策略可用,会面临不同的问题。
分布式系统将非功能性的需求摆在台面:可扩展性,可用性和可靠性。
-
- 数据库集成 – 成本最低,但是可扩展性和可用性差
集成有界上下文的一种可行方式是让一个应用程序写入特定的数据库位置,另一个应用程序从中读取。
很可能你会希望在第一次迭代采用最小可行产品-MVP或者系统的非关键性执行部分采用这一方式。
-
- 平面文件集成
如果你的项目中没有使用数据库,那么可以选择使用“平面文件”集成。
平面文件集成更灵活,因为他是手工自动控制的。但灵活代表着更多的错误可能性,且全靠自己保证扩展性,高可用。文件格式的管理,没有标准查询语言等等。
-
- RPC – 其透明了网络的远程调用,但万不可忽略。可扩展性和可靠性较差
你能保持整体几乎不变,但又得到分布式系统的可扩展性好处。这就是使用RPC背后的驱动力。
当选用RPC的时候,你就拥有了很大的自由度,因为RPC自身就是一个可以使用各种方式实现的概念。
如果你打算与足够多的使用RPC的公司对话,那么你将发现使用大多数类型的Web服务。简单对象访问协议-SOAP,具象状态传输-REST,可扩展标记语言-XML等
RPC会在达成可扩展性和可靠性方面表现出一些深度缺陷。这就是需要使用异步性,响应式消息传递解决方案的原因。
-
- 消息传递 – 解决网络问题,且显著提高系统可扩展性。
即时再大的 公司,也会遭遇网络问题导致系统宕机不可用。
响应式解决方案试图通过将异步消息传递模式用于通信来提高可靠性,从而兼容故障。
与RPC不同的是,使用消息传递,代码风格将会变化,不会再提醒你网络有问题。
不仅如此,消息传递系统的整体设计和架构也将显著不同,而团队将面临令人敬畏的学习曲线的挑战。
-
- REST
如果你想得到消息传递解决方案的可扩展性和可靠性的好处,但又希望使用超文本传输协议HTTP而不是消息传递框架,则可以选择REST。
你也可以使用动词和标头,构建事件驱动系统。
- DDD使用分布式系统的挑战
开发团队需要理解构建分布式系统的方法,他们可以降低这些问题出现的可能性和严重性,并且让业务可以随着需求增长而扩展。
接受故障的出现并且准备好应对他们,这是构建分布式系统的关键部分,却并不是RPC所固有的部分。
尽管RPC感觉很像充分利用的面向对象编程和封装,它有一些分布式系统社区已知晓多年的明显缺点。
1 | RPC难以带来弹性 | 分布式计算谬论 其中已经一次次证明了网络既不可靠也不能免受宽带和延迟成本的影响,而这些通常又被RPC实现视为理所当然。 |
2 | 扩展RPC的代价更高 |
|
3 | RPC涉及紧密耦合 | 逻辑耦合:代码上下游逻辑要一致,可以互相理解。 临时耦合:同步执行,上下游的代码串行执行。
|
|
|
|
- 分布式事务将损害可扩展性和可靠性
事务是维护数据一致性的最佳实践。
遗憾的是,在构建分布式系统时,事务会带来很高的成本,因为需要涉及网络通信。因此,像过度长期占有数据库所或者局部故障这样的原因会对可扩展性和可靠性造成负面影响。因而当你构建分布式系统时,应该仔细考虑分布式事务,就像RPC一样。
-
- 有界上下文不必彼此保持一致
将分布式事务的事情,拆分为多个事宜执行,如果一个事宜失败,那么可以使用一种回滚成一个新操作或者将其他成功操作置为为无效即可。
通过避免分布式事务,你就能在不承受收益损失的情况下处理局部故障。
允许临时的不一致性在你的系统中存在并非一种激进方式。它是分布式系统中十分常见的概念,称为最终一致性。
-
- 最终一致性
尽管你的系统同可能处于不一致状态,但其目标总是要某一时刻让每块数据达成一致。
伴随着最终一致性的一个重要首字母缩写是BASE,它代表了基本可用,软件状态,最终一致性。这与ACID(原子性,一致性,隔离性,持久性)相对应。
主语最终一致性不要给用户的体验带来影响,且不会引起疑惑。
- 事件驱动响应式DDD
使用 异步的,事件驱动消息传递解决方案,来改进前面RPC示例的弹性和可扩展性问题。
响应式编程 <<< 异步消息传递
-
- 展示响应式解决方案的弹性和可扩展性
如上图例子,通过异步过程处理订单,性能问题解决了。
要解决弹性问题,每条消息都要放入一个队列中,直到接收者成功处理了它。
为了进一步理解响应式解决方案如何提高了可扩展性。可以通过将更多的Web应用程序实例添加到负载均衡器下来横向扩展,而无需改变用于有界上下文的硬件。
-
- 异步消息传递的挑战和取舍
是否采用响应式编程风格,需要进行取舍。
在构建响应式应用程序时,这里有一些你可能会面临的挑战:在调试异步解决方案中增加的困难,当其他人试图理解其工作机制时你的代码会更有更多间接性以及最终一致性。还有更多基础架构组件传递和重试所带来的额外复杂性。
不过以上,可以通过合理的命名以及代码结构约定,弄明白异步代码的处理过程不是很大的问题。还将概述一个概念框架,让你能够真正掌握最终一致性。
-
- RPC还有价值吗
你应该将RPC视作一个工具,它可能用于特定情形的最佳可选项。
1 | 即时上市的优势 需要快速上市,测试用户的可接受度,或赶在竞争对手前面。 技术复杂度,人员技术熟悉度等,RPC都是比较好的选项。 |
2 | 更易于雇佣和培训开发人员 RPC学习曲线地,且技能人员较多。 |
3 | 平台解耦 使用功能许多消息传递框架的一大缺点就是,他们并不能真正跨越不同开发运行时和操作系统的紧密集成。 |
4 | 外部集成 当调用外部服务,或者本身提供服务API的时候。 系统提供 HTTP式REST或者RPC API更多些。 |
|
|
- SOA 和 响应式DDD
如何使用SOA原则从你的有界上下文中构建结构化响应式解决方案。
-
- 将你的有界上下文视作SOA服务
如果响应式编程是一组导致松耦合软件组件的低层次技术指南,而SOA是促成松耦合业务功能的高层次概念,那么用于创建面向业务,可扩展性,有弹性的分布式系统的这一组合看起来就是完美的。
将你的有界上下文视作SOA服务,这样你就能将高层次的有界上下文映射到低层次,事件驱动的软件组件。
1 | 将有界上下文分解成业务组件 一个有界上下文可能有若干职责。通过将这些主要职责隔离成一个组件,你就会发现你具有了与业务人员更清晰的对话以及你已经看见过让隐式变显式所带来的DDD的所有其他好处。 |
| 将业务组件分解成组件 归根结底,组件的好处在于,业务可以明智地选择将紫金花费在很可能产生增加效益的地方,不过,硬件只是经济因素的一部分好处。另一个是将业务组件定位在与业务优先级和性能需要保持一致的不同网络上的坑能行。
就像软件开发中的许多其他术语一样,组件这个术语,是不明确且模糊不清的。 社区并没有为其取一个统一的名字,有的叫自主性组件,也有人叫其微服务。 |
| 进一步处理微服务架构 Netflix公司将一种其他公司如今开始应用的细粒度方法用于SOA,其名称为微服务架构-MSA。MSA的益处是可以即时上市和试验的优势。 如果你要为企业的系统进行大量变更,并且衡量每次变更的影响以推动新的和已有功能的演化,则你可以考虑使用MSA。 |
|
|
- 要点汇总
你可以使用松耦合,面向服务的SOA思想体系,通过将有界上下文视作SOA服务来帮助你设计你的有界上下文集成策略。
组合使用SOA和响应式编程会为平台提供以下可能性:将你的基础架构与业务优先级保持一致,应对可扩展性和可靠性挑战,以及通过团队与有界上下文保持一致来组织你的团队以降低同信开销。