的六边形架构通常 Netflix
本文论述了Netflix是如何基于六边形架构去开发一款全新运行的。
随着 Netflix 原创内容的逐年增长,咱们要构建一些可优化整个创作环节效率的运行。咱们的一个大型部门,Studio 工程团队曾经构建泛滥运行,去协助从剧本制造到内容播出的全套流程,触及的环节涵盖剧本内容失掉、买卖谈判和供应商治理,以及日程布置、简化消费流程等。
1. 从开局就高度集成
大约一年前,咱们的 Studio 流程团队开局开发一款跨多个业务畛域的全新运行。
过后,咱们面临一项无心思的应战:
一方面咱们须要从头开局构建运行的外围,另一方面咱们所需的数据散布在泛滥不同的系统之中。
对咱们运行程序的行为和业务逻辑而言,已有数据十分关键。咱们从一开局就须要高度集成。
2. 可切换数据源
早期的一款运行程序用来为咱们的产品引入可见性,它被设计为单体架构。在畛域常识体系尚未建设的状况下,单体架构可以成功极速开发和极速变卦。起初,经常使用它的开发人员超越 30 人,有超越 300 个数据库表。
随着期间流逝,运行程序从触及面宽泛的服务演化成高度专业化的产品。在这样的背景下,团队选择将单体架构解构为一系列公用服务。
做出这一决策并非性能疑问,而是要对一切这些畛域设置界限,并让各个专属团队能独立开发针对每个特定畛域的服务。
咱们的新运行所需的少量数据照旧是之前的单体提供的,但咱们知道这个单体将在某一蠢才解开来。咱们不能确定详细期间,但知道这一时辰无法防止,所以须要做好预备。
3. 应用六边形架构
咱们须要有在不影响业务逻辑的前提下切换数据源的才干,因此咱们须要让它们坚持解耦形态。
咱们选择基于六边形架构的准则来构建运行。
六边形架构的思想是将输入和输入都放在设计的边缘局部。不论咱们地下的是 REST 还是 GraphQL API,也不论咱们从何处失掉数据——是经过数据库、经过 gRPC 还是 REST 地下的微服务 API,或许仅仅是一个便捷的 CSV 文件——都不应该影响业务逻辑。
这种形式让咱们能将运行程序的外围逻辑与外部的关注点隔分开来。外围逻辑隔离后,象征着咱们可以轻松更改数据源的细节,而不会形成严重影响或须要在代码库重写少量代码。
咱们还看到,在运行中具备明晰边界的另一大优势就是测试战略——咱们的大少数测试在验证业务逻辑时,都不须要依赖那些很容易变动的协定。
4. 定义外围概念
自创六边形架构,定义咱们业务逻辑的三大略念区分是实体、存储库和交互器。
实体(Entities)指的是域对象(例如一部影片或一个拍摄地点),它们不知道自身的存储位置(不像是 Ruby on Rails 中的 Active Record 或许 Java Persistence API 那样)。
存储库(Repositories)是失掉实体及创立和更改实体的接口。它们保留一系列方法,用来与数据源通讯并前往单个实体或实体列表。(例如 UserRepository)
交互器(Interactors)是用来编排和执行域举措(domain action)的类——可以思考服务对象或用例对象。它们成功复杂的业务规定和针对特定域举措(例如上线一部节目)的验证逻辑。
有了这三大类对象,咱们就可以在定义业务逻辑时无需通晓或许关心数据的存储位置,也不用理会业务逻辑是怎么触发的。业务逻辑之外是数据源和传输层:
数据源(Data Sources)是针对不同存储成功的适配器(Adaptor)。数据源或许是 SQL 数据库的适配器(Rails 中的 Active Record 类或 Java 中的 JPA)、弹性搜查适配器、REST API,甚至是诸如 CSV 文件或 Hash 之类的便捷适配器。数据源实如今存储库上定义的方法,并存储失掉和推送数据的成功。
传输层(Transport Layer)可以触发交互器来执行业务逻辑。咱们将其视为系统的输入。微服务最经常出现的传输层是 HTTP API 层和一组用来处置恳求的控制器(Controller)。将业务逻辑提取到交互器后,咱们就不会耦合到特定的传输层或控制器成功上。交互器不只可以由控制器触发,还能由事情、cron 作业或从命令行触发。
六边形架构的依赖图向内收缩
在传统的分层架构中,咱们一切的依赖项都会指向一个方向,上方的每一层都会依赖自己上方的层。传输层会依赖交互器,而交互器会依赖耐久存储层。
在六边形架构中,一切依赖项都指向中心方向。咱们的外围业务逻辑对传输层或数据源无所不知。但传输层依然知道如何经常使用交互器,数据源也知道如何对接存储库接口。
这样,咱们就可以为未来切换到其余 Studio 系统的更改做好预备,并且当须要迈出这一步时,咱们很容易就能成功切换数据源的义务。
5. 切换数据源
切换数据源的需求比咱们预期来得更早一些——咱们的单体架构突然遇到一个读取瓶颈,并且须要将某个实体的特定读取切换到一个在 GraphQL 聚合层上地下的新版微服务上。这个微服务和单体坚持同步,数据相反,并且它们从各个服务中读取时发生的结果也是分歧。
咱们设法在 2 小时内就将数据读取从一个 JSON API 切换到一个 GraphQL 数据源上。
咱们之所以能如此快地成功这一操作,关键归功于六边形架构。咱们没有让任何耐久存储细节走漏到业务逻辑中。咱们创立了一个成功存储库接口的 GraphQL 数据源。因此,只要要做便捷的一行代码更改,即可开局重新的数据源读取数据。
经过适当的形象,很容易更改数据源
到这个时刻,咱们就知道经常使用六边形架构没错了。
单行代码更改有一大优势,那就是它可以减小颁布危险。假设下游微服务在初始部署时失败,回滚也会十分容易。这也让咱们能解耦部署和激活作业,由于可以经过性能来选择经常使用哪个数据源。
6. 暗藏数据源细节
这种架构的一大优势是让咱们能封装数据源的成功细节。
咱们遇到这样一种状况:有一次性,咱们须要一个尚不存在的 API 调用——有一个服务用一个 API 来失掉单个资源,但没有成功批量失掉。与提供该 API 的团队交换后,咱们得悉这个批量失掉端点须要一些期间才干交付。因此,咱们选择在这个端点构建的同时,经常使用另一种打算来处置这个疑问。
咱们定义了一个存储库方法,该方法可以在给定多个记载标识符的状况下失掉多个资源——并且该方法在数据源的初始成功会向下游服务发送多个并发调用。咱们知道这是一个暂时的处置打算,数据源成功的下一步改良是在批量 API 构建终了后切换到新 API 上。
咱们的业务逻辑不须要了解特定的数据源限度
这样的设计让咱们能继续开发以满足业务需求,同时不会积攒太多技术债,也无需预先更改任何业务逻辑。
7. 测试战略
当咱们开局尝试六边形架构时,就知道须要提出一种测试战略。要优化开发速度的先决条件就是领有牢靠且十分快的测试套件。咱们不以为这是精益求精,而是必要条件。
咱们选择在三个不同的层上测试运行:
咱们测试了交互器,业务逻辑的外围存在于此,但与任何类型的耐久层或传输层有关。咱们用上了依赖注入,并 mock 恣意类型的存储库交互。在这里咱们详细测试业务逻辑,大局部测试都位于此处。
咱们测试数据源,以确定它们能否与其余服务正确集成,它们能否对接上存储库接口,并审核它们在发生失误时的行为。咱们试着尽量缩小这些测试的数量。
咱们具备遍布整个栈的集成规范,从咱们的 Transport/API 层到交互器、存储库、数据源以及关键的下游服务所有蕴含在内。这些规范测试的是咱们能否正确“布线”了一切。假设一个数据源是一个外部 API,咱们将命中该端点并记载照应(并将其存储在 git 中),从而让咱们的测试套件可以在每次后续调用时极速运转。咱们不会在这一层启动宽泛测试,通常每个域举措只要一个成功场景和一个失败场景。
咱们不会测试存储库,由于它们是数据源成功的便捷接口;并且咱们很少测试实体,由于它们是定义了属性的普通对象。咱们会测试实体能否有其余方法(这里不触及耐久层)。
咱们还有改良空间,比如咱们未来可以不 ping 所依赖的任何服务,而是 100%依赖合同测试。有了上述形式编写的测试套件,咱们可以 100 秒外在单个环节中运转大约 3000 个 specs。
能轻松在任何机器上运转的测试套件,它用起来十分棒,咱们的开发团队可以在不终止的前提下做日常性能测试。
8. 提前决策
如今咱们可以轻松将数据源切换到不同的微服务上。关键的一大好处是,咱们能提前一些对于能否以及如何存储运行程序外部数据的决策。依据性能用例,咱们甚至可以灵敏确定数据存储的类型——可以是相关型也可以是文档型。
当这个名目开局时,咱们对正在构建的这个系统的了解是十分少的。咱们不应该将自己锁定在一个会造成名目悖论和不理智决策的架构中。
咱们如今做的决策合乎咱们需求,并且让咱们能极速执行。六边形架构的最大优势在于,它可以让咱们的运行程序灵敏顺应未来需求。