65.9K
CodeProject 正在变化。 阅读更多。
Home

框架耦合

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (3投票s)

2016 年 3 月 31 日

CPOL

5分钟阅读

viewsIcon

7810

您是否以正确的方式使用框架?您是出于业务目的使用框架,还是反过来?您的业务类是否依赖于框架?

你能在你的 Spring、Guice、Hibernate、JPA 依赖上按下删除键,并且仍然能够测试和使用你的业务功能吗?如果不能,你可能面临一个巨大的问题——高度的框架耦合。

为什么它如此重要?

首先,框架和库的更新速度远比我们的软件快。我们希望尽可能频繁地升级它们,并尽可能轻松地更换它们。三年后,当前编写 Spring 应用程序的风格可能已经完全过时。或者我们可能想转向一个很酷的新框架。还记得过去人们编写的那些 EJB 应用程序吗?其中大部分仍然存在,有人必须维护和开发它们。更糟糕的是,现在可能有很多人在愤怒地将它们重写为 Spring 或 Jooby。而他们很可能犯了同样的错误。

其次,也是更具破坏性的是,高度的框架耦合会导致代码不可测试。你耦合的每一个额外框架,在测试时都必须考虑进去,这使得测试过程更加复杂,通常会花费更长的时间,在极端情况下甚至变得不可能。

微服务并没有改变什么

我听到的一种反对框架耦合是问题的观点是,我们编写的是微服务。这并没有改变什么。无论你选择何种高级架构,你可能都会编写相似数量的代码来涵盖所有业务功能。拥有一个庞大的设计糟糕的应用程序,和拥有 20 个设计糟糕的小型应用程序之间有什么区别?每个应用程序可能更容易管理,但你需要测试、发布和监控 20 个应用程序而不是 1 个。

然后有人告诉我,这些应用程序中的每一个都非常小,很容易重写。这是错误的思路。如果我有一打微服务,每个服务需要 3 个月来编写,而我每 3 年想重写一次,我就会陷入无休止的重写循环!而且,这可能不会因为“只是重写”而更快,因为 3 年后,团队将不再是同一批人——他们将不得不从头开始学习,或者从框架耦合的代码中学习。

背景

我第一次遇到这个问题是在 Uncle Bob 的文章 Screaming Architecture 中。我环顾我的项目,看到一堆 Spring “服务”、仓库和配置类。它们都在尖叫着 Spring。然后我开始思考,并意识到这不仅仅是类名。这是过去让我损失大量精力(和时间)的事情——当我想要升级框架版本、将其完全从系统中删除、在框架耦合的应用程序中进行测试,或者阅读多年前用公司内部框架编写的代码时。

解决方案

解决方案很简单,但对大多数开发人员来说是不舒服的。你必须改变你编写应用程序的方式。

为你的业务组件设定清晰的界限。确保没有任何业务边界被框架依赖所侵犯。Clean Coders 上有一段关于架构和边界的精彩视频:架构、用例和高级设计

每当你想要使用框架的某些功能时,都应该反转依赖(依赖反转原则)。有时,这可能需要一些额外的类,例如接口或适配器,但这绝对是值得的。这同样适用于 JPA 和其他持久化机制。你绝不应该使用持久化数据结构作为业务对象——数据结构和对象从定义上就是不同的!

一旦你反转了所有依赖,你可能想将业务相关代码放入单独的物理组件中,即单独的 JAR 文件。物理边界是防止不必要依赖的终极方法——除非你引入它们,否则你的代码将无法编译。
此时应该很自然的是,你应该努力使你的测试套件尽可能地独立于框架。我这里指的不是 JUnit 或 Cucumber 这样的框架。我指的是你的业务代码应该 100% 可测试,而无需使用你的非业务组件使用的任何框架,例如 Spring 或 Hibernate。
另一个应该遵循的更普遍的规则是:让你的框架易于使用且易于移除。 即使你可能不会更改应用程序中的所有框架,但几乎可以肯定你会想要升级它们的版本。让使用新技术成为一种享受,而不是处理旧技术的痛苦。

实际案例

我曾在一家具备航空公司解决方案的公司工作,参与了一个名为 Service Engine 的编排系统。开发人员构建了一个内部框架,用于使用 Spring、BPMN 引擎和内部数据库配置库(也基于 Spring)来编排服务流程。你可以通过 BPMN 文件定义编排规则,并以漂亮的图表查看整个工作流程。然后,在每个步骤中,你可以使用注解将有意义的数据从前一步注入。最后,你可以通过在配置类中添加一个 `abstract` 方法并对其进行注解来获取数据库中的任何配置键。听起来很酷,对吧?

一点也不。如果你想利用框架的任何功能,你必须依赖于所有这些功能。这带来了巨大且昂贵的后果。

首先,他们没有反转任何依赖,并决定将所有内容保留在同一个代码库中。庞大的代码库,其构建每天在 CI 服务器上失败,在我们的 IDE 中甚至更频繁地失败。

其次,系统完全不可测试。如何在这样一个框架堆中运行验收/集成测试而不运行所有这些?如何为每个测试的目的快速运行整个机械装置?如果你甚至不知道哪些是相关的类,如何满足所有依赖,如何模拟所有相关的类?所以他们绕过这条路——从外部进行测试。这简直是致命的。他们无法使用传统的 CI 机制运行验收/集成测试。他们无法模拟任何东西。如果下游服务出现故障,整个测试就会被阻塞——你无法更改单行代码并对其进行正确测试(而这些服务经常出现故障!)。当我离开公司时,一次回归测试阶段的估计时间大约是 2 周。太疯狂了!

结论

软件架构不是关于酷炫的东西。让你的业务远离框架。让框架服务于你应用程序的目的,而不是反过来。用你选择的语言进行编码,而不是框架。环顾你的应用程序,找出如何使其更独立于框架。持续确保任何框架或库都不会阻碍你的测试。

请在评论中告诉我你对此的看法以及你的项目情况。

© . All rights reserved.