

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

# 最佳实践
<a name="best-practices"></a>

## 对业务领域进行建模
<a name="model-domain"></a>

从业务领域回到软件设计，确保你正在编写的软件符合业务需求。

使用领域驱动的设计 (DDD) 方法（例如[事件风暴）对业务领域进行建模](https://www.eventstorming.com/)。活动风暴采用灵活的研讨会形式。在研讨会期间，领域和软件专家共同探讨业务领域的复杂性。软件专家利用研讨会的交付内容开始软件组件的设计和开发过程。

## 从一开始就编写和运行测试
<a name="testing"></a>

使用测试驱动开发 (TDD) 来验证您正在开发的软件的正确性。TDD 在单元测试级别上效果最好。开发人员通过先编写测试来设计软件组件，然后调用该组件。该组件一开始没有实现，因此测试失败。下一步，开发人员实现组件的功能，使用带有模拟对象的测试夹具来模拟外部依赖项或端口的行为。测试成功后，开发人员可以通过实现真正的适配器来继续。这种方法可以提高软件质量并生成更具可读性的代码，因为开发人员了解用户将如何使用组件。Hexagonal 架构通过分离应用程序核心来支持 TDD 方法。开发人员编写的单元测试侧重于域核心行为。他们不必编写复杂的适配器来运行测试；相反，他们可以使用简单的模拟对象和固定装置。

使用行为驱动开发 (BDD) 来确保在功能层面上 end-to-end被接受。在 BDD 中，开发人员定义功能场景并与业务利益相关者进行验证。BDD 测试使用尽可能多的自然语言来实现这一目标。Hexagonal 架构通过其主适配器和辅助适配器的概念支持 BDD 方法。开发人员可以创建无需调用外部服务即可在本地运行的主适配器和辅助适配器。他们将 BDD 测试套件配置为使用本地主适配器来运行应用程序。

自动运行持续集成管道中的每项测试，以持续评估系统的质量。

## 定义域的行为
<a name="behavior"></a>

将域分解为实体、值对象和聚合（阅读有关[实现域驱动设计](https://www.oreilly.com/library/view/implementing-domain-driven-design/9780133039900/)的信息），并定义它们的行为。实现域的行为，以便在项目开始时编写的测试成功。定义调用域对象行为的命令。定义域对象在完成行为后发出的事件。

定义适配器可用于与域交互的接口。

## 自动测试和部署
<a name="automate-testing"></a>

在初步验证概念后，我们建议您花时间实施 DevOps实践。例如，持续集成和持续交付 (CI/CD) 管道以及动态测试环境可帮助您保持代码质量并避免部署过程中出现错误。
+ 在 CI 流程中运行单元测试，并在合并代码之前对其进行测试。
+ 构建 CD 流程，将应用程序部署到静态 dev/test 环境或动态创建的支持自动集成和 end-to-end测试的环境中。
+ 自动执行专用环境的部署流程。



## 使用微服务和 CQRS 扩展您的产品
<a name="scale"></a>

如果您的产品成功，请通过将软件项目分解为微服务来扩展您的产品。利用六角形架构提供的便携性来提高性能。将查询服务和命令处理程序拆分为单独的同步和异 APIs步。考虑采用命令查询责任分离 (CQRS) 模式和事件驱动架构。

如果您收到许多新功能请求，请考虑根据 DDD 模式扩展您的组织。正如本[组织规模](adapt-to-change.md#scaling)节前面所讨论的那样，以这样的方式组织您的团队，使他们拥有一个或多个功能作为受限上下文。然后，这些团队可以使用六角形架构来实现业务逻辑。

## 设计符合六角形建筑概念的项目结构
<a name="mapping"></a>

基础设施即代码 (IaC) 是云开发中广泛采用的做法。它允许您将基础架构资源（例如网络、负载均衡器、虚拟机和网关）定义和维护为源代码。这样，您就可以使用版本控制系统来跟踪架构的所有更改。此外，您还可以轻松创建和移动基础架构以进行测试。我们建议您在开发云应用程序时将应用程序代码和基础设施代码保存在同一个存储库中。这种方法可以轻松维护应用程序的基础架构。

我们建议您将应用程序分成三个与六角形架构概念对应的文件夹或项目：`entrypoints`（主适配器）、`domain`（域和接口）和`adapters`（辅助适配器）。

以下项目结构提供了在上设计 API 时采用这种方法的示例 AWS。按照之前的建议，该项目将应用程序代码 (`app`) 和基础设施代码 (`infra`) 保存在同一个存储库中。

```
app/  # application code
|--- adapters/  # implementation of the ports defined in the domain
     |--- tests/  # adapter unit tests
|--- entrypoints/  # primary adapters, entry points
     |--- api/  # api entry point
          |--- model/  # api model
          |--- tests/  # end to end api tests
|--- domain/  # domain to implement business logic using hexagonal architecture
     |--- command_handlers/  # handlers used to run commands on the domain
     |--- commands/  # commands on the domain
     |--- events/  # events emitted by the domain
     |--- exceptions/  # exceptions defined on the domain
     |--- model/  # domain model
     |--- ports/  # abstractions used for external communication
     |--- tests/  # domain tests
infra/  # infrastructure code
```

如前所述，域是应用程序的核心，不依赖于任何其他模块。我们建议您对`domain`文件夹进行结构化以包含以下子文件夹：
+ `command handlers`包含在域上运行命令的方法或类。
+ `commands`包含命令对象，这些对象定义了在域上执行操作所需的信息。
+ `events`包含通过域发出然后路由到其他微服务的事件。
+ `exceptions`包含域内定义的已知错误。
+ `model`包含域实体、值对象和域服务。
+ `ports`包含域与数据库或其他外部组件通信所使用的 APIs抽象。
+ `tests`包含在域上运行的测试方法（例如业务逻辑测试）。

主适配器是应用程序的入口点，如`entrypoints`文件夹所示。此示例使用该`api`文件夹作为主适配器。此文件夹包含一个 API`model`，它定义了主适配器与客户端通信所需的接口。该`tests`文件夹包含 API 的 end-to-end测试。这些是浅层测试，用于验证应用程序的组件是否已集成并协同工作。

辅助适配器（如`adapters`文件夹所示）实现域端口所需的外部集成。数据库存储库就是辅助适配器的一个很好的例子。当数据库系统发生变化时，您可以使用域定义的实现来编写新的适配器。无需更改域名或业务逻辑。`tests`子文件夹包含每个适配器的外部集成测试。