限界上下文与架构

限界上下文对 领域驱动架构 有直接到影响

识别限界上下文上下文映射 都是重要过程

限界上下文

逻辑架构 以及 物理架构参考模型

上下文映射

体现了系统架构的通信模型

限界上下文不仅仅作用于领域层 以及 应用层

是架构设计 而非仅仅领域设计的关键因素

限界上下文 是一个 垂直的 架构边界

针对后端架构层次的垂直拆分

例如:

订单上下文的内部就包含了应用层、领域层和基础设施层,每一层的模块都是面向业务进行划分,甚至可能是一一对应的。

依赖的技术组件以及选型 属于架构设计 但不属于 限界上下文考虑内容

基础设施层需要访问的外部资源,以及为了访问它需要重用的框架或平台,与技术决策和选型息息相关,仍然属于架构设计的考量范围,但它们不属于限界上下文的代码模型

NoSQL 数据库还是关系数据库

消息队列是采用 Pull 模式还是 Push 模式。

技术选型上,我们需要确定具体是哪一种数据库和消息队列中间件,同时还需要确定访问它们的框架。

限界上下文的通信边界

进程边界隔离(两个限界上下文属于不同的进程 比如 运力 以及 人员)

进程内以及进程间存在不同的处理方式:

  • 通信

  • 消息的序列化

  • 资源管理

  • 事务与一致性处理

  • 部署

通信边界的不同 影响了 系统对各个组件的 重用方式 以及 共享方式

1 进程内的通信边界

限界上下文通信方式 为进程内通信

运行时多个上下文模型运行在同一个进程中

通过 实例化的方式 重用 其他上下文中的功能

Java中 限界上下文模型存在 两种设计方式:

1 命名空间级别

所有上下文 都处于同一模块中 通过命名空间(分包)对各个上下文进行隔离

2 模块级别

不同上下文属于同一工程的不同模块 module

越容易重用 越容易产生耦合

对于同一进程的边界,编写代码时要格外遵守 各个上下文的逻辑边界,确定限界上下文各自对外公开的接口,避免上下文之间产生过多的依赖

如何避免上下文之间产生过多的依赖?

每个上下文有自己的防腐层,用来 减少 外部上下文变化带来的影响

防腐层 对进程内的多个上下文进行隔离

进程内通信 属于单体架构

不能针对某一个限界上下文进行水平伸缩

尽管通过分module或 分包 我们守住了模型的边界 但是各个上下文间是相互耦合的 各个限界上下文间相互影响 团队内协调成本增加

第2种方式 :上下文处于不同的module的解耦会更加彻底,倘若

限界上下文的划分足够合理,也能提高它们对变化的应对能力。

例如,当限界上下文 A 的业务场景发生变更时,我们可以只修改和重编译限界上下文 A 对应的 Jar 包,其余 Jar 包并不会受到影响。

多个上下文处于同一进程 由于它们都运行在同一个 Java 虚拟机中,意味着当变化发生时整个系统需要重新启动和运行。

2 进程间的通信边界

多个限界上下文不在同一个进程里 限界上下文间的通信是跨进程的,不能直接调用另一个限界上下文中的方法 RPC通信

对于进程间 通信 需要考虑 上下文依赖的 外部资源 形成两种不同风格的架构:

1 数据库共享架构

2 零共享架构

1 数据库共享架构

考虑上下文划分时, 分开考虑代码模型 以及 数据库模型 -> 多个被进程分离的上下文 共享一个数据库 进程分离 数据库共享

由于未进行分库 数据库层面 更容易保证事物的 ACID属性 可以看做是对 一致性约束的妥协

共享数据库带来的问题:

数据库变化的方向业务的变化方向不一致,体现:

1 耦合:限界上下文的代码模型是解耦的, 但是 数据库层面 存在强耦合的关系

上下文对应的库表 受到 其他上下文 对应库表的影响

2 水平伸缩: 实现 某上下文的 应用服务器 可以进行水平 扩展( 不受到其他上下文对应的应用服务器影响) 但是 某上下文 对应的数据库表 无法做到 独立于其他上下文的库表 实现水平扩展

Netflix 提出微服务最佳实践:每个微服务的数据单独存储

但服务的分离 不绝对代表 数据应该分离

原因:对数据进行分库设计时, 如果仅仅站在业务边界角度思考 导致分库粒度太小 带来不必要的跨库关联

建议:将“数据库共享”模式视为一种过渡方案,采用演进式的设计。

为 便于 在 演进设计中 将分表 重构为 分库 应该 避免 在两个限界上下文的表之间 建立 外键约束关系(或其他关联关系 比如 userorg表(当然user 与org 属于同一上下文))

两个分处不同限界上下文的服务需要操作同一张数据表(这张表被称之为“共享表”)时,就传递了一个信号,即我们的设计可能出现了错误:

  • 遗漏了一个限界上下文,共享表对应的是一个被重用的服务:买家在查询商品时,商品服务查询价格表中的当前价格,而在提交订单时,订单服务也会查询价格表中的价格,计算当前的订单总额;共享价格数据的原因是我们遗漏了价格上下文,通过引入价格服务就可以解除这种不必要的数据共享。

  • 职责分配出现了问题操作共享表的职责应该分配给已有的服务:舆情服务与危机服务都需要1 从邮件模板表中获取模板数据,然后2 再调用邮件服务组合模板的内容发送邮件;实际上从邮件模板表获取模板数据的职责应该分配给已有的邮件服务。

  • 共享表对应两个限界上下文的不同概念:仓储上下文与订单上下文都需要访问共享的产品表,但实际上这两个上下文需要的产品信息并不相同,应该按照限界上下文的边界分开为各自关心的产品信息建表(两个上下文 各自建立 自己关心 的 xx信息表)

产生以上错误 的根本原因: 没有通过业务建模,而是在数据库层面隐式地进行建模

2 零共享架构

两两限界上下文共享的外部资源彻底斩断

凉凉限界上下文间 通过 http接口 或者 tcp接口 实现数据获取

架构的表现形式为:

每个限界上下文都有自己的代码库、数据存储以及开发团队,每个限界上下文选择的技术栈和语言平台也可以不同,限界上下文之间仅仅通过限定的通信协议和数据格式进行通信

复杂度:

1限界上下文之间的通信是跨进程的,我们需要考虑通信的健壮性

2数据库是完全分离的,当需要关联之间的数据时,需得跨限界上下文去访问,无法享受数据库自身提供的关联福利

3由于每个限界上下文都是分布式的,如何保证数据的一致性也是一件棘手的问题

当整个系统都被分解成一个个可以独立部署的限界上下文时,运维与监控的复杂度也随之而剧增

不同进程间通信协议的确定:

1 基于 HTTP 的 REST 服务,也可以

2 通过 RPC 访问远程对象,又或者

3 利用消息中间件传递消息

是一种分布式调用,自然存在分布式系统与身俱来的缺陷: 如网络不可靠 数据不一致

针对下游服务可能不可用的情况:考虑服务调用的熔断来及时应对故障,避免因单一故障点带来整个微服务架构的连锁反应(不能因为下游依赖的某一个服务 不可用 导致本服务不可用 及时熔断)

数据一致性问题:

业务上是否要求严格的一致性?

最终一致性(BASE): 可靠事件模式、补偿模式、TCC模式 等

发布了84 篇原创文章 · 获赞 6 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/csdn_9527666/article/details/105282751