【微服务】以模块化单体架构开发微服务应用
@TOC
推荐超级课程:
我们知道,起初,单体应用有显著的优势:它们更容易开发和部署。从开发人员的角度来看,这种简单性是有益的。一切都是集中的,可以快速更新任何部分的业务逻辑并立即看到结果。这种开发的便利性是许多公司最初选择构建单体应用的原因。
一个单体应用并不总是一个脏乱的凝块。它可以有良好的结构,有着明确的层和模块。但当一个团队开始成长时,问题开始出现。许多人需要在一个代码库上共同工作。到了那一刻,就会出现部署阻塞的问题,一个团队破坏了另一个团队的功能,更新依赖项的困难等等…这样一来,单体应用的开发和部署变得越来越具有挑战性。
因此,许多公司最终考虑过渡到微服务架构。以允许不同的团队独立开发、部署和扩展他们应用的部分。但是,这就要求将一个单体应用切分成许多独立的服务。并且经常这是一个非常复杂的任务。这种复杂性主要是因为单体应用最初并没有考虑未来转向微服务的可能。
从单体应用转向微服务架构不仅仅是将单体划分为独立的部分以便部署为微服务。单体应用的分层结构并不自动意味着不同业务环境之间存在清晰、明确的边界。通常,来自不同边界环境的实体之间是密切耦合的。有时,没有明确的边界环境,属于不同边界环境的不同实体可能被表示为一个单一实体。为了有效地将其拆分为微服务,必须识别和修改每个使用此组合实体的实例,这是一项具有挑战性的任务。
在本文中,我不会深入探讨如何将单体架构拆分为微服务,因为这是另一场讨论的话题。相反,我将探讨如何以最终将其分离成微服务为目标开始新应用的开发。
模块化单体的概念建议将应用的开发作为一个单一的代码库和部署单元开始,但在业务环境之间建立明确和清晰的边界。这种方法鼓励将应用程序视为一组模块,这些模块可以从一开始就独立部署。
为什么这种方法会有益处呢?
因为它有助于从一开始就思考有关业务环境、它们的隔离和职责分离。未来,这可以帮助更轻松地割裂应用,无需经历太大痛苦和重大数据更改。
为什么我们不从一开始就开发微服务应用?
以微服务架构开始应用的开发可能导致显著更高的开发成本。此外,在开发过程中,需求和业务逻辑经常发生变化,因此,业务环境的边界可能也会发生变化。在微服务架构中调整这些边界要复杂得多且代价更高。这不仅仅涉及更新代码。由于每个微服务都有自己的数据库,迁移数据并更新微服务可能是一个具有挑战性和复杂的任务。
现在,让我们探讨以模块化单体的方式开发应用的方法。在这种架构中,最初的目标是识别独立的业务环境、隔离它们,并建立明确的通信模式。这需要额外的努力和时间,但提供了更大的灵活性,可以在单个代码库和数据库中重构业务环境、调整其边界和修改通信模式。相比微服务架构,这种方法成本更低。
这种方法的关键是要从一开始了解有界环境的范围。通常,看起来是两个不同功能的单独业务环境,在后来可能被证明是如此紧密地相互联系,以至于它们无法作为独立单元有效地运行。在这种情况下,将它们视为单一的业务环境会更加实际。
此外,在模块化单体中,一个有界上下文不仅仅是围绕一个实体的单个控制器和业务逻辑。它涵盖了需要独立且不可分割的业务功能。 (如何识别这类业务功能的细节将在另一篇文章中讨论。)
通过在一开始在单一代码库和数据库中隔离业务环境,我们可以预防许多未来问题。这种策略还提供了在识别不同业务环境中的实体的实践经验。例如,一个最初看起来是单一的实体可能需要分割为单独的部分,每个部分驻留在不同的业务环境中。
模块单体架构有效地处理了这些复杂性。如下图所示,对模块单体中模块的责任进行更改成本要低得多。无需数据迁移,而且通常,最终用户可能甚至不会注意到任何变化。从开发的角度来看,这代表了一个实质性的好处,并提供了灵活性。
随着模块单体中的某些业务环境增长并达到实质性规模,它们最终可以被提取出来并独立运行。到那时,它们的通信模式和边界可能已经建立得非常好,有助于实现平稳和工作量最小的过渡。
需要解决的一个关键问题是模块(业务环境)之间的通信组织。在传统的单体架构中,通常通过直接调用另一个类的方法来触发业务逻辑。然而,在我们的场景中,这种方法意味着一个类依赖于另一个类的存在,这并不理想。我们的目标是独立的上下文。为了实现这一点,我们应该使用事件而不是直接方法调用。我们的有界上下文不应该直接调用彼此。相反,它们应该发布事件并订阅来自其他上下文的事件,确保它们的独立性。
保持数据一致性并确保没有事件丢失也至关重要。有各种技术可以管理这种情况,适当的解决方案将取决于您的具体需求。以下是一些您可以利用的选项:
— 发出事件(要小心,您可能会丢失事件,对于某些系统来说,这不是一个选项)
— 将事件放入数据库并从另一个模块中获取(这会消耗数据库的额外资源,当您决定分离一个模块时,您将需要更改此部分)
— 将事件发布到队列中(这允许您在没有任何痛苦的情况下分开模块,但是对于消息代理会有额外的成本)
这不是技术列表的详尽内容。您甚至可以使用事务外箱模式。我只是想给您一个解决这一沟通问题的想法
总之:每个解决方案都会产生新问题。作为工程师,您需要比较并选择最适合您问题的解决方案。许多应用程序可能作为单体应用程序长时间存在。但是如果您清楚地了解最终需要处理微服务,那么尝试考虑模块化单体架构作为一个良好的起点是很不错的。