微服务:逻辑边界不是物理边界


微服务的好处之一就是定义边界。俗话说,好篱笆造就好邻居(中国谚语:亲兄弟明算账)。

定义边界是一件好事,但我们到底如何定义它们呢?微服务,由 Adrian Cockcroft 定义:
具有有边界的上下文的松散耦合的面向服务的架构

这意味着我们拥有由有边界上下文定义的服务,它来自领域驱动设计,并且这些服务之间是松散耦合的。通常,这意味着事件驱动或消息驱动的架构。

我通常将服务(而不是微观部分)定义为一组业务能力的权威。

服务有什么作用?该服务提供哪些功能?至少在微服务的所有包袱中出现了定义边界的想法。

不幸的是,当开发人员实现微服务时,它们迫使边界成为物理边界。但当谈论服务提供什么功能时,我谈论的是逻辑边界。它们不是同一件事。 

物理边界不是逻辑边界
逻辑边界(服务提供的内容)也位于其自己的源存储库(git 存储库)中,该源存储库被构建并转换为在某些环境中运行的部署物理工件,例如进程、容器等。
或者,您可能有一个单一存储库,其中所有源都位于单个存储库下,但最终结果仍然与构建逻辑边界并将其转入其自己的部署工件相同。
如果逻辑边界也是物理边界,那么您最终可能会通过网络进行服务到服务的通信。
这可以是通过 HTTP、gRPC 或通过网络调用的任何其他类型的同步请求/响应。

那么,这有什么问题吗?
问题是:这里面有一些陷阱,包括延迟、故障等等。这最终是一个分布式整体。

逻辑边界
那么逻辑边界和物理边界有什么区别呢?

描述这一点的最佳方式可能是考虑系统的全部范围。你可能有很多不同的方面。可能有一些前端/客户端/UI、后端、数据库和其他基础设施(例如缓存)。

逻辑边界是跨越所有这些层的垂直切片。逻辑边界拥有与其提供的功能相关的一切。这包括 UI、后端 API、它所保存的数据库以及它拥有的任何其他基础设施。

Philippe Kruchten 的4+1 架构视图模型:有一个逻辑视图、一个开发视图(源代码、存储库)和一个物理视图(部署)。
逻辑视图可以与物理视图相同,但重点是:它们不一定必须相同:这也意味着逻辑边界不必独立部署。

一旦你意识到这一点,你就会发现你可以根据你的需要以不同的方式组合东西。

  • 您可以在单个单一存储库中拥有多个逻辑边界,该存储库是作为单个部署单元构建的。该单一部署单元可以是单一进程。
  • 这意味着,如果我们在逻辑边界之间同步通信,我们可以在进程中进行通信,而不是进行网络调用。它只是函数在进程中调用函数。

这里并不是建议将所有逻辑边界组合成一个可部署单元。而是在说明逻辑边界不是物理边界。他们不必是一对一的,你有选择。

  • 一个逻辑边界可以有一个源存储库,在构建时会创建两个不同的部署单元,比方说一个容器
  • 一个逻辑边界可能跨越多个源存储库,每个源存储库都内置到自己单独的容器中。
  • 可以拥有一个分布在构建到单个容器中的多个源存储库中的单个逻辑边界。
  • 更好的说明是,如果带有后端的移动应用程序有两个不同的逻辑边界。服务 A 可以提供后端和移动前端。同一个朋友可能由另一个逻辑边界组成来构建适用于 Android 的 APK。
  • 这适用于基础设施。每个服务都应该拥有自己的数据,但这并不意味着它需要拥有基础设施。您可以共享基础设施而不共享数据。单个数据库实例可以托管每个逻辑边界拥有的不同模式。

如果我们利用事件驱动的架构或消息传递,我们可以删除任何 rpc 或进程内调用。即使在单个进程中,我们也可以使用消息传递来松散地耦合逻辑边界。
如果出于各种原因(部署节奏等)需要,我们可以选择通过独立部署逻辑边界来更改物理方面。

总之
从十年前的单体到微服务,现在又回到了整合或转向模块化单体。为什么?逻辑边界不是物理边界。一旦我们最终做出这种区分,它就开辟了许多可能性。