

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 适应变化
<a name="adapt-to-change"></a>

软件系统往往会变得复杂。造成这种情况的原因之一可能是业务需求经常发生变化，几乎没有时间相应地调整软件架构。另一个原因可能是投资不足，无法在项目开始时设置软件架构，以适应频繁的变化。不管是什么原因，软件系统都可能变得复杂，以至于几乎不可能做出改变。因此，从项目一开始就构建可维护的软件架构非常重要。良好的软件架构可以轻松适应变化。

本节介绍如何使用可轻松适应非功能或业务需求的六角形架构来设计可维护的应用程序。

## 使用端口和适配器适应新的非功能要求
<a name="ports-adapters"></a>

作为应用程序的核心，域模型定义了满足业务需求所需的外部世界操作。这些操作是通过抽象定义的，这些抽象被称为*端口*。这些端口由单独的适配器实现。每个适配器负责与其他系统的交互。例如，您可能有一个适配器用于数据库存储库，另一个适配器用于与第三方 API 交互。该域不知道适配器的实现，因此很容易将一个适配器替换为另一个适配器。例如，应用程序可能会从 SQL 数据库切换到 NoSQL 数据库。在这种情况下，必须开发一个新的适配器来实现由域模型定义的端口。该域不依赖于数据库存储库，而是使用抽象进行交互，因此无需在域模型中进行任何更改。因此，六角形架构可以轻松适应非功能性需求。

## 通过使用命令和命令处理程序来适应新的业务需求
<a name="commands-handlers"></a>

在经典的分层架构中，域取决于持久层。如果要更改域，则还必须更改持久层。相比之下，在六角形架构中，该域不依赖于软件中的其他模块。域是应用程序的核心，所有其他模块（端口和适配器）都取决于域模型。该域使用[依赖反转原理](https://stackify.com/dependency-inversion-principle/)通过端口与外界通信。依赖关系反转的好处是，你可以自由地更改域模型，而不必害怕破坏代码的其他部分。由于域名模型反映了您要解决的业务问题，因此更新域模型以适应不断变化的业务需求不是问题。

在开发软件时，关注点分离是需要遵循的重要原则。要实现这种分离，你可以使用[稍微修改过的命令模式](https://www.cosmicpython.com/book/chapter_10_commands.html)。这是一种行为设计模式，其中完成操作所需的所有信息都封装在命令对象中。然后，这些操作由命令处理程序处理。命令处理程序是接收命令、更改域状态然后向调用者返回响应的方法。您可以使用不同的客户端（例如同步队列 APIs 或异步队列）来运行命令。我们建议您对域上的每个操作都使用命令和命令处理程序。通过采用这种方法，您可以通过引入新的命令和命令处理程序来添加新功能，而无需更改现有的业务逻辑。因此，使用命令模式可以更轻松地适应新的业务需求。

## 使用服务外墙或 CQRS 模式解耦组件
<a name="cqrs"></a>

在六角形架构中，主适配器负责将来自客户端的传入读写请求松散耦合到域中。有两种方法可以实现这种松散耦合：使用服务立面模式或使用命令查询责任分离 (CQRS) 模式。

[服务外观模式](https://refactoring.guru/design-patterns/facade)提供了一个前置接口，用于为客户端（例如表示层或微服务）提供服务。服务外观为客户提供多种读取和写入操作。它负责将传入的请求传输到域，并将从该域收到的响应映射到客户端。对于具有单一职责和多项操作的微服务来说，使用服务外观很容易。但是，在使用服务外墙时，很难遵循[单一责任和开放式](https://itnext.io/solid-principles-explanation-and-examples-715b975dcad4)封闭原则。单一责任原则规定，每个模块只能对软件的单一功能负责。开放-封闭原则规定，代码应开放以供扩展，关闭以供修改。随着服务外观的扩展，所有操作都收集在一个接口中，更多的依赖关系被封装到该接口中，更多的开发人员开始修改相同的外观。因此，我们建议只有在开发期间服务显然不会扩展太多时才使用服务外观。

在六角形架构中实现主适配器的另一种方法是使用 [CQRS 模式，该模式](https://www.cosmicpython.com/book/chapter_12_cqrs.html)使用查询和命令将读取和写入操作分开。如前所述，命令是包含更改域状态所需的所有信息的对象。命令由命令处理程序方法执行。另一方面，查询不会改变系统的状态。它们的唯一目的是将数据返回给客户。在 CQRS 模式中，命令和查询是在单独的模块中实现的。这对于遵循[事件驱动架构](https://aws.amazon.com/event-driven-architecture/)的项目尤其有利，因为命令可以作为异步处理的事件来实现，而查询可以使用 API 同步运行。查询也可以使用针对其进行优化的其他数据库。CQRS 模式的缺点是，与服务外墙相比，实施所需的时间更长。对于计划长期扩展和维护的项目，我们建议使用 CQRS 模式。命令和查询为应用单一责任原则和开发松散耦合软件提供了一种有效的机制，尤其是在大型项目中。

从长远来看，CQRS有很大的好处，但需要初始投资。因此，我们建议您在决定使用 CQRS 模式之前仔细评估您的项目。但是，您可以从一开始就使用命令和命令处理程序来构建应用程序，而无需将 read/write 操作分开。如果您稍后决定采用该方法，这将帮助您轻松地为 CQRS 重构项目。

## 组织规模
<a name="scaling"></a>

六角形架构、域驱动设计和（可选）CQRS 相结合，使您的组织能够快速扩展您的产品。根据[康威定律](https://www.thoughtworks.com/insights/articles/demystifying-conways-law)，软件架构往往会演变以反映公司的通信结构。从历史上看，这种观察结果具有负面含义，因为大型组织通常根据数据库、企业服务总线等技术专业知识来组建团队。这种方法的问题在于，产品和功能开发总是涉及跨领域的问题，例如安全性和可扩展性，这需要团队之间的持续沟通。基于技术特征构建团队会在组织中造成不必要的孤岛，从而导致沟通不畅、缺乏所有权和忽视大局。最终，这些组织问题反映在软件架构中。

另一方面，[Inverse Conw](https://www.thoughtworks.com/en-de/radar/techniques/inverse-conway-maneuver) ay Manever根据促进软件架构的领域来定义组织结构。例如，跨职能团队负责一[组特定的有限上下文，这些上下文](https://www.scaledagileframework.com/agile-teams/)是通过使用 DDD 和[事件](https://www.eventstorming.com/)风暴来识别的。这些有界限的上下文可能反映了产品的非常具体的特征。例如，账户团队可能负责付款上下文。每个新功能都分配给一个新团队，该团队的职责具有高度凝聚力和松散的职责，因此他们只能专注于该功能的交付，从而缩短上市时间。团队可以根据功能的复杂性进行扩展，因此可以将复杂的功能分配给更多的工程师。