(本文下半篇已更新至微信公众号) 如今,我们经常会听人说“单体架构(Monolith)已经过时,它是 IT 中的万恶之源”。我们也会经常听到微服务(Microservices)架构是解决所有这些庞然大物的“银弹”。但其实 IT 中几乎没有银弹,每个决策都需要取舍。
微服务架构最受欢迎的优势之一是良好的模块分离。您可以独立地部署每个服务,而且服务更容易伸缩。此外,每个团队都可以拥有自己的代码库,并使用自己选择的技术。我们可以很容易、很快地重写整个服务。相应的,采用微服务架构后,我们可能会遇到网络、延迟、有限的带宽等问题。您必须处理通信层中的潜在错误,从开发人员和运维人员的角度来看,调试和维护非常困难。我们还必须与服务 API 的兼容性问题作斗争。此外还有一些潜在的性能缺陷。许多领域的总体复杂性更大,从开发到管理都是如此。
总之,不管有什么优点,构建好的微服务体系结构是很困难的。如果你不相信我的意见,那么你应该可以相信Martin Fowler[1]:
我听说过的从零开始构建微服务系统的案例,几乎都以严重的问题告终。
—— Martin Fowler
几乎所有成功的微服务都是从单体架构开始的,这个单体架构变得过于庞大,最终拆分被拆分。
—— Martin Fowler
让我们假设一个疯狂的想法:我们正在开发一个新的应用程序,我们想从一个单体架构开始,这是否意味着我们必须忍受所有单体架构的缺点?
并非这样。事实上,许多这样的问题通常不是因为单体架构,而是因为缺乏良好的编程实践和设计。
让我们看看与微服务架构版本相比,一个设计良好的示例单体应用程序看起来如何。在本文中,我们将把它命名为“整洁单体”。两者都是使用 整洁架构 的规则构建的。
整洁架构
简单来说,整洁架构(Clean architecture)假设您的服务应该分为4层:
-
领域层(Domain) – 我们的领域逻辑和实体 -
应用层(Application) – 像领域层的胶水一样。例如,从存储层获取实体,调用该实体上的某个方法,将该实体保存在存储层中。此外,它还应该对所有横切关注点(如日志、事务、监控等)负责,它还负责提供视图。 -
接口层(Interfaces) – 该层允许我们使用应用程序,例如 REST API、 CLI Interface、 Queue 等。 -
基础设施层(Infrastructure) – 数据库适配器, REST客户端, 通常实现Domain/Application的接口。

整洁架构是一个非常类似六边形架构/端口和适配器架构/洋葱架构的概念,其中的一些你可能已经听说过。
整洁架构的关键概念是任何层都不能知道外层的信息。例如,领域层不应该知道应用层或基础设施层(例如,它不知道领域实体在 MySQL 中是持久化的)。此外,应用层也不应该知道调用的细节(REST API、 CLI、 Queue listener等等)。
但如何实现呢? 答案是控制反转(IoC)。简而言之: 将实现细节隐藏在接口之后。
鲍勃大叔[2]已经描述得很好了。在文章的底部,我将提供一个整洁架构的完整解释的链接。
单体架构 vs 微服务架构
我们使用一个简单的商店程序作为示例。这个商店程序将允许我们: 列出产品,下订单,在远程支付提供商中初始化付款,接收有关付款的通知并标记订单付款。让我们看一下架构图。
我为微服务架构和单体架构分别创建了图表,它们都是基于整洁架构设计的。
微服务架构图:译注:如果图片不清晰,可查看原图
https://threedots.tech/media/microservices-or-monolith/microservices.png
通常来说,单体架构看起来像大泥球:

但当我们以整洁架构的精神来设计时,它是这样的:
原图地址 https://threedots.tech/media/microservices-or-monolith/monolith.png
你看出来这个例子中两种架构的不同之处了吗?如果仔细观察,可以发现只有在接口层/基础设施层中有些细微的差异。领域层和应用层基本上是相同的(这是我们使用 DDD[3] 时所期望的)。我用橙色箭头标记了不同之处,以使其更加明显;)
微服务可以避免这种情况,因为我们在服务之间有一个天然的屏障(例如,因为代码库是分离的) ,所以我们通过设计来强制模块分离。但是我们也可以通过单体应用程序和整洁架构实现一些分离,因为我们只能在基础设施和接口层之间进行通信。例如,我们不允许在我们的限界上下文[4]中使用来自其他限界上下文的领域层/应用层/基础设施层的任何内容。
使这条规则有效是极其重要的。实施它的最佳方法是拥有一个可以验证它并将其插入 CI 的工具。
我已经开发了一个工具,可以在Go中做到这一点: go-cleanarch[5]。
前段时间我发现了一个类似的 PHP 工具,但是我找不到它了。类似这样的工具有可能存在于其他语言中。如果你知道任何像这样的工具,请发送到我的 Twitter:@roblaszczak,我会把它放在这里:)
不幸的是,有模块并不意味着我们的架构是良好的。良好的模块分离对于正常工作的单体应用和微服务应用是至关重要的。为了使它更好,我们应该检查限界上下文的概念(在文章的底部)。而另一个伟大的工具 —— 事件风暴,将向你展示你的限界上下文在哪里(物理上!),展示你的领域是如何工作的。
有很多人说持久性只是一个实现上的细节[7](框架和驱动部分)。他们还说,应该实现持久层,以便在不影响基础设施以外的任何其他层的情况下替换驱动。
让我们再进一步: 假设应用程序是微服务架构还是单体架构只是一个实现上的细节。
如果这是真的,我们可以用单体架构来开发我们的应用程序,当时机来临,迁移到微服务架构也不会有太多的工作,不涉及接口层/基础设施层以外的层级。类似的,我们可以选择在文件系统或内存中实现数据库驱动程序,以推迟有关选择数据库的决定。
当然,在迁移到微服务时可能仍然需要一些优化,但与整体重构(或者更糟糕的重新实现)相比,这将是相当容易的。
我不想只是毫无根据的瞎说,下面是模式中显示的应用程序源代码的一些片段。
译者的话
原文篇幅过长,从这里开始,后面就是具体的代码实现示例了,为了保障阅读体验,我从这里将文章进行了拆分。如果你等不及想要阅读后面的内容了,可以在下方找到原文地址。
此外作者还提供了示例代码仓库,你也可以在下方找到。示例使用Go语言,单个仓库包含了微服务架构+单体架构的代码,就像作者在文中说的一样,它们的结构惊人的相似。
示例源代码
https://github.com/ThreeDotsLabs/monolith-microservice-shop
原文地址: https://threedots.tech/post/microservices-or-monolith-its-detail/
参考资料
Martin Fowler: 一名英国的软件工程师,也许你看过他出版的其中一本书:《重构:改善既有代码的设计》,豆瓣评分9.3。
[2]
鲍勃大叔: 著有《代码整洁之道》《架构整洁之道》等
[3]
Domain Driven Design: 领域驱动设计
[4]
限界上下文: Bounded Context,领域驱动设计内的概念
[5]
go-cleanarch: https://github.com/roblaszczak/go-cleanarch
[6]
Futuramo: https://futuramo.com/
[7]
整洁架构: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
原文始发于微信公众号(梦真日记):为什么说微服务架构或单体架构仅仅是实现上的细节(一)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/167835.html