不要从单体架构开始

… 当你的目标是微服务架构时

Photo of Stefan Tilkov

Stefan Tilkov 是 innoQ 的联合创始人兼首席顾问,innoQ 是一家在德国和瑞士设有办事处的技术咨询公司。他参与了大型分布式系统的设计工作超过二十年,使用各种技术和工具,从 C++ 和 CORBA 到 J2EE/Java EE 和 Web 服务,再到 REST 和 Ruby on Rails。他撰写了大量文章和一本书(“REST und HTTP”,德语),并且是全球各地会议的常客。

2015 年 6 月 9 日

在过去的几个月里,我反复听到,要构建成功的微服务架构,唯一的方法是从单体架构开始。 用 Simon Brown 的话说:如果你不能构建一个结构良好的单体架构,你怎么能认为你能构建一个结构良好的微服务集?最近——而且像往常一样,非常有说服力——这个论点的演绎来自 Martin Fowler 在这个网站上。由于我有机会对早期草稿发表评论,我花了一些时间思考这个问题。我确实这样做了,尤其是因为我通常发现自己同意他的观点,而且一些我通常赞同其观点的人似乎也同意他的观点。

我坚信,从单体架构开始通常是错误的做法。

开始构建一个新系统正是你应该考虑将其分解成多个部分的时候。我强烈反对 Sam Newman 表达的观点,即你可以推迟这个过程,他也是我 95% 的时间都赞同的人。

我仍然相信,与从头开始对一个新的、绿地系统进行分区相比,对一个现有的、"棕地"系统进行分区要容易得多。你拥有更多可利用的东西。你可以检查代码,你可以与使用和维护系统的人交谈。你同样也知道什么是“好的”——你有一个正在运行的系统需要更改,这使得你更容易知道何时可能出错,或者在决策过程中过于激进。

-- Sam Newman

在大多数情况下,以这种方式将现有的单体架构分解成多个部分将非常困难,如果不是完全不可能的话。(这并不意味着它总是不可行,但这是以后文章的主题。)我们有一些共同点,我同意你应该非常了解你正在构建系统的领域,然后再尝试对其进行分区:在我看来,理想的情况是构建现有系统的第二个版本

如果你真的能够构建一个结构良好的单体架构,那么你可能根本不需要微服务。这没问题!我绝对同意 Martin:如果你没有充分的理由,就不应该在你的系统中引入额外分布的复杂性。

(那么什么才是充分的理由呢?有很多理由,但对我来说,最重要的理由是允许在更大的系统中快速、独立地交付各个部分。在我看来,微服务的主要优势是通过在系统不同部分之间建立难以跨越的边界来实现并行开发。通过这样做,你让做错事变得困难——或者至少更困难——即连接不应该连接的部分,以及将需要连接的部分耦合得太紧密。理论上,如果你只是有纪律遵循明确的规则并在你的单体应用程序中建立明确的边界,你就不需要微服务来实现这一点;在实践中,我发现这种情况很少见。)

你可能会倾向于认为,你的单体架构中隐藏着许多很好地分离的微服务,只需提取出来即可。然而,实际上,很难避免创建大量的连接,无论是计划的还是非计划的。事实上,微服务方法的重点就是让创建这样的东西变得困难。

但是,如果你从单体架构开始,各个部分将彼此紧密耦合。这就是单体架构的定义。各个部分将依赖于它们都使用的平台的功能。它们将基于共享的抽象进行通信,因为它们都使用相同的库。它们将使用仅在它们托管在同一个进程中时才可用的方式进行通信。而这些仅仅是技术方面!更糟糕的是,各个部分将(几乎)自由地共享领域对象,依赖于相同的共享持久性模型,假设数据库事务随时可用,因此无需进行补偿……即使是轻松重构和移动事物的事实——所有这些都在你的 IDE 对单个项目的视图中很方便——也使得再次将事物拆分变得极其困难。将现有的单体架构拆分成独立的部分极其困难。

我坚信——我们最近的一些项目的经验也证实了这一点——在你开始时,你应该考虑你构建的子系统,并尽可能独立地构建它们。当然,你应该只有在你认为你的系统足够大以至于需要这样做时才这样做。如果只有你和你的同事在几周内构建一些东西,那么你可能不需要这样做。

但是,从一开始就采用一种方法,将你的系统分解成更小的部分,并将每个部分视为一个明确分离的独立系统,拥有自己的开发、部署和交付周期,以及(可能)自己的内部架构,这是一个非常强大的概念,可以帮助你首先交付一个系统。

那么,是否有实际经验来支持这一点呢?是的,我们最近参与了一些系统,这些系统表明这个概念是有效的——前提是你容忍这样一个事实,即我所说的东西很可能比你典型的微服务更大。最突出的例子是 Otto.de,我和他们的技术负责人一起做了 一个关于它的演讲(你可以在 他们的一位首席架构师的这篇精彩文章中阅读)。但还有很多其他例子,我仍然相信,在你知道你正在构建的领域非常、非常好的情况下,使用这种方法开始构建系统是一个好主意。

但在我看来,从这场讨论中还可以吸取另一个教训——这是一个更普遍的教训:警惕过于简单和明显的架构方案。这个方案——从将你的领域分解成独立的部分开始——也不例外。有时单体架构更好的选择,有时不是。如果你决定使用微服务方法构建东西,你需要意识到,虽然在每个独立部分中做出本地化决策会容易得多,但改变使这成为可能的边界会困难得多。小范围的重构变得更容易,大范围的重构变得困难得多。

正如架构讨论中总是会发生的那样,你无法回避这样一个事实,即你需要在每个具体案例中自己做出决定。


致谢

感谢 Martin Fowler 对这篇博文的早期版本提供的评论,以及过去几周关于这个主题的持续讨论。当然,我也很高兴他允许我在这个网站上发布它。我的同事 Daniel Lübke、Oliver Wolf、Alex Heusingfeld、Till Schulte-Coerne、Andreas Krüger 和 Eberhard Wolff 也提供了宝贵的反馈。特别感谢 Roman Stranghöner 创建了这些图形。

进一步阅读