好的软件架构使项目能够抵抗时间的考验。
作为开发者,我们难以避免在系统中使用外部库和框架。社区创造出奇妙的工具,使用它们是自然而然的事情。然而,一切都有不利的一面。
鲁莽地使用工具来构建系统会带来很多风险。业务规则与实现细节混合在一起。这可能导致系统脆弱,难以扩展和维护。原本应该在GUI上快速变化的结果变成了持续几个小时的bug大搜查。但大可不必这样。
软件架构提出模型和规则来确定系统中结构(比如类、接口和结构)以及它们之间的关系。这些规则促进了可复用性和对这些元素的关注点分离。这使得更改诸如DBMS或前端库之类的实现细节变得很容易。重构和bug修复会影响尽可能少的系统。添加新功能变得轻而易举。
在这篇文章中,我将解释罗伯特·c在2012年提出的一个架构模型。他是《代码整洁之道》(Clean Code 和 The Clean Coder)等经典著作的作者。今年10月,他将推出另一本书《架构整洁之道》。
这个模型和这本书的名字一样,它是建立在简单的(Clean)概念之上的:
将系统的组成划分为具有不同且定义良好的角色的层,并约束各实体间的关系。在层中分割应用程序并不是什么新鲜事。但我选择了这种方法,因为它是最简单的方法来理解和执行。它使得测试用例非常简单。
我们只需要确Interactors的工作正常,我们就可以走了。不要担心“Interactors”这个词对你来说很陌生,我们很快就会了解它们。
从内而外,我们将进一步探索每一层。我们将使用一个非常熟悉的示例应用程序:counters。它很好理解,所以我们可以专注于本文的主题。
您可以在这里找到该应用程序的demo,代码示例将在TypeScript中。一些代码gists使用React和Redux。一些关于这些解决方案的知识有助于理解它们。然而,整洁架构的概念很通用,即使不了解前面提到的工具你也能理解。
实体
实体在图中作为企业业务规则。实体包括公司通用的业务规则。它们代表了操作区域的基本实体。它们是具有最高抽象级别的组件。
在我们的计数器样本中,有一个很明显的实体:Counter本身。
我已经封装了代表计数器类型中的计数器的数字。这是为了让我们在未来改变它的时候尽可能减少系统的其他部分的影响。
用例
用例代表应用程序业务规则。它们代表单个应用程序的每个用例。该层的每个元素都提供了一个到外层的接口,并作为与系统其他部分通信的hub。它们负责用例的完整执行,通常被称为Interactors。
incrementingdecrementingcounterCounterGateway接口适配器 Interface Adapters
这个层包含系统的业务规则和允许它与外部世界交互的工具,如数据库和图形接口。该层中的元素充当中介,从一个层接收数据并将其转发给另一个层,根据需要调整数据。
counterincrementdecrementCounterCounterViewDataCounterViewData注意,CounterViewData是如何存储内部的显示逻辑的。
接口适配器的另一个示例是应用程序的Redux实现。负责向服务器请求的模块和本地存储的使用也将存在于这个层中。
框架与驱动
你的系统用来与外部世界沟通的工具构成最外层。我们通常不会在这个层中编写代码,包括React/ Redux、浏览器API等。
依赖的规则
这个划分层有两个主要目标。其中之一是明确系统各部分的责任。另一种方法是确保每个角色都尽可能独立地填充角色。为了实现这一点,有一条规则说明了元素之间应该如何相互依赖:
元素不能依赖于属于它自己的层外的任何元素。
例如,用例层中的一个元素无法了解与GUI或数据持久性相关的任何类或模块。同样,一个实体也不知道哪些用例可以使用它。
这条规则可能让你很困惑。以一个用例为例。它是由于用户与UI交互而触发的。它的执行涉及一些持久数据存储(如数据库)的更新。在不依赖于数据持久性的接口适配器的情况下,Interactor如何对更新程序进行相关调用?
答案在于我们之前提到过的一个元素:网关。他们负责建立用例所需要的接口来完成他们的工作。一旦他们建立了这个接口,就需要接口适配器来完成他们的合作,正如上图所示。我们有CounterGateway interface以及用下面的Redux来具体实现:
你可能不需要
当然,这个示例应用程序对于一个递增/递减计数器应用程序有点复杂。我想说明的是,你不需要所有这些来做一个小的项目或原型。但是当你的应用程序变得更大时,你肯定想要最大化可复用性和可维护性。好的软件架构使项目能够抵抗时间的考验。