为什么需要组装应用程序?
了解组装应用程序的重要性。
简介
在实现了部分用例、Web 适配器和持久化适配器之后,我们需要将它们组合成一个可运行的应用程序。正如在章节 「组织代码」 中所讨论的那样,我们将依赖依赖注入机制,在程序启动时实例化类并将它们连接在一起。本章中,我们将探讨如何使用纯 Java 以及 Spring 和 Spring Boot 框架来实现这一点。
为什么我们不在需要的时候直接创建用例或适配器的实例呢?因为我们希望保持代码依赖关系的方向正确。请记住:所有依赖都应指向应用程序内部的领域代码,这样当外层发生变化时,领域代码无需随之修改。
依赖方向
如果一个用例需要调用持久化适配器,并且直接自行实例化该适配器,那么我们就创建了一个错误方向的代码依赖。
这正是我们为何要定义输出端口接口的原因。用例仅依赖于一个接口,并在运行时由外部提供该接口的具体实现。
这种编程方式还有一个积极的副作用:我们编写的代码更容易测试。如果一个类所需的所有对象都能通过构造函数传入,我们就可以在测试时传入模拟对象(mock)而非真实对象,从而轻松地为该类创建隔离的单元测试。
创建对象实例的责任
那么,谁应该负责创建这些对象实例呢?又该如何在不违反依赖规则的前提下完成这一任务?
答案是:必须存在一个与架构无关的配置组件,它对所有类都有依赖,以便在运行时实例化它们,如图所示。
中立组件
在 「反转依赖」 一章中介绍的“整洁架构”中,这个配置组件位于最外层的圈层,根据依赖规则,它可以访问所有内层。
该配置组件负责将我们提供的各个部分组装成一个可运行的应用程序。其职责包括:
- 创建 Web 适配器实例;
- 确保 HTTP 请求能够正确路由到相应的 Web 适配器;
- 创建用例实例;
- 将用例实例注入到 Web 适配器中;
- 创建持久化适配器实例;
- 将持久化适配器实例注入到用例中;
- 确保持久化适配器能够实际访问数据库。
此外,配置组件还应能读取各类配置源,例如配置文件或命令行参数。在应用组装过程中,它会将这些参数传递给各个应用组件,以控制行为,比如指定访问哪个数据库,或使用哪台服务器发送邮件。
这些职责相当繁重(即“变更的理由”很多)。这是否违反了单一职责原则?确实如此。但如果我们希望保持应用程序其余部分的清晰和解耦,就必须有一个外部组件来负责这些装配工作。而这个组件自然需要了解所有的组成部分,才能将它们整合成一个正常运行的应用系统。