Ohhnews

分类导航

$ cd ..
foojay原文

Java中的端口与适配器:保持核心整洁

#java#端口与适配器#mongodb#软件架构#领域模型

目录

简介 为什么“整洁核心”在 2026 年依然重要 六边形架构回顾 端口:定义核心需要什么 领域模型必须保持无知 MongoDB 作为适配器——而非存储库 领域模型与持久化模型之间的映射 测试:架构回报之处 使用 Spring Boot 但不被其主导 当 MongoDB 确实影响设计时 权衡与现实世界的约束 结论

简介

如果我们希望随着时间的推移演进我们的 Java 系统,那么架构比框架的选择更重要。很多时候,团队意识到得太晚,原本只是简单的持久层最终却影响并主导了整个应用程序的设计。MongoDB 注解最终出现在领域模型中,存储库抽象镜像了集合结构,业务逻辑成为基础设施不可或缺的一部分。

六边形架构,也称为端口和适配器,提供了一种旨在避免此问题的架构模型。它涉及正确分配各个应用层的职责。它鼓励我们将外部系统(数据库、消息代理、HTTP API、MCP 服务器)视为细节,而不是我们设计的中心支柱。

在本文中,我们专注于一个具体的、真实的场景:使用数据库(特别是 MongoDB)而不污染核心领域。目标不是理论上的优雅,而是解决方案的长期可维护性和可测试性。

为什么“整洁核心”在 2026 年依然重要

如果现代框架能为我们解决所有的架构问题,那就太好了。Spring Boot、Quarkus 和 Micronaut 提供了约定、注解和自动配置。所有这些都承诺从第一天起就提高生产力和组织性。而且,老实说,它们确实做到了它们所承诺的。

真正的问题是,框架针对的是快速启动和提供结构进行了优化,而不是随着时间的推移维护架构的完整性和整洁性。随着系统的演进,基础设施关注点的变化频率通常高于业务规则。数据库被替换或重新配置,部署模型发生变化,框架不断更新。另一方面,核心领域逻辑通常演进得更慢,并保留最大的业务价值。

整洁核心原则尊重并认识到这种不对称性。它将业务规则与基础设施隔离开来。这允许持久化策略、测试方法和框架在不侵入核心领域的情况下进行演进。端口和适配器明确应用了这种分离,并将其作为结构保证。

六边形架构回顾

六边形架构的核心非常简单:

  • 核心包含业务逻辑。
  • 外部世界通过端口与核心交互。
  • 适配器为特定技术实现这些端口。

[LOADING...]

这种架构之所以强大,在于其方向性的依赖关系:核心依赖于抽象,而基础设施依赖于核心。而不是相反。

端口:定义核心需要什么

端口告诉应用程序做什么,而不是怎么做。它代表领域功能,而不是持久层的抽象。

让我们考虑一个简化的订单处理领域。与其通过编写存储库层从 CRUD 风格的设计开始,不如从六边形设计开始。首先是用例:

$ java
public interface LoadOrder {
    Optional<Order> byId(OrderId id);
}

public interface SaveOrder {
    void save(Order order);
}

这些接口位于核心模块中。它们不连接到 MongoDB、Spring Data 或其他与持久化相关的技术或框架。这种组织有两个直接的优势:

  1. 端口基于用例,而不是数据。
  2. 基础设施实现可以被更改或优化,而不会影响核心。

领域定义需要什么,适配器决定如何做。

领域模型必须保持无知

仔细检查你的领域模型。如果它使用持久化注解或 Spring 数据类型,那么使用 MongoDB 或任何其他数据库就不再仅仅是一个技术细节。边界已经被跨越,技术已经开始塑造内部核心。

在六边形架构中,领域对象是简单的 Java 对象,其唯一任务是表达业务概念、不变量和生命周期转换。诸如集合名称、索引、序列化格式或查询能力之类的东西被完全废除。这些东西属于基础设施,在那里变化是预期的且可管理的。

这不是为了纯粹而纯粹:让领域保持“不知情”是实现真正隔离的关键。业务逻辑可以在没有数据库的情况下进行测试,无需了解特定框架即可进行评估,以及无需协调技术层之间的更改即可演进。

MongoDB 作为适配器——而非存储库

MongoDB 非常适合六边形架构,但只有当我们将其视为适配器而不是概念参考点时才如此。这种看待问题的方式要求我们违背框架推荐的方法,后者是以存储库的概念为中心的。

通过逆转这种方法,与其询问如何维护聚合,不如询问 MongoDB 如何满足核心内定义的特定端口,这更有用和重要。这种设计的改变将 MongoDB 重新定义为服务提供者,而不是设计引擎。

从这个假设出发,MongoDB 适配器将领域对象转换为特定的持久化表示,执行针对文档存储优化的查询,并管理索引、投影和模式演进。核心完全不知道 BSON 是什么、集合是什么或用于查询的语言。这种分离保持了灵活性和可扩展性。

领域模型与持久化模型之间的映射

六边形架构常因其模式重复而受到批评。在实践中,这种重复既是刻意的,也是有益的。

MongoDB 适配器引入了特定的持久化模式:

$ java
@Document("orders")
class OrderDocument {
    @Id
    private String id;
    private String status;
    private BigDecimal totalAmount;
}

相应的领域模型保持与注解和框架依赖的分离:

$ java
public class Order {
    private final OrderId id;
    private OrderStatus status;
    private Money totalAmount;

    public void markAsShipped() {
        if (status != OrderStatus.PAID) {
            throw new IllegalStateException("Only paid orders can be shipped");
        }
        this.status = OrderStatus.SHIPPED;
    }
}

这两种表示之间的映射发生在适配器内部:

$ java
public class OrderMapper {
   public static Order toDomain(OrderDocument doc) {
       return new Order(
               new OrderId(doc.getId()),
               OrderStatus.valueOf(doc.getStatus()),
               new Money(doc.getTotalAmount())
       );
   }

   public static OrderDocument toDocument(Order order) {
       return new OrderDocument(
               order.id().value(),
               order.status().name(),
               order.totalAmount().amount()
       );
   }
}

这种类型的映射是一种临时的分界线,因为它允许将更改本地化,并防止核心卷入持久化的细节。

测试:架构回报之处

这种架构的真正优势在于测试方法。拥有整洁的核心:

  • 用例可以使用内存适配器进行测试。
  • 不需要数据库。
  • 测试要快得多且可重复。

MongoDB 适配器可以单独测试,使用 TestContainer、嵌入式 Mongo 或在端口上执行的契约测试。

关键在于测试策略遵循架构设计。

使用 Spring Boot 但不被其主导

紧接上文,只有当允许 Spring 定义架构而不是为我们提供支持工具时,它才会成为一个问题。

让我们尝试创建一个组织良好的配置,其中 Spring Boot 用作协调所有活动的适配器:

$ java
@Configuration
class OrderAdapterConfiguration {
    @Bean
    LoadOrder loadOrder(MongoOrderRepository repository) {
        return new MongoLoadOrderAdapter(repository);
    }

    @Bean
    SaveOrder saveOrder(MongoOrderRepository repository) {
        return new MongoSaveOrderAdapter(repository);
    }
}

核心模块没有 Spring 依赖。组件扫描在边界处停止,依赖注入允许将端口连接到适配器。仅此而已。

Spring 保持在边缘,这正是支持核心模块的基础设施应该所在的位置。

当 MongoDB 确实影响设计时

MongoDB 具有独特的功能,自然会 influence 适配器的设计。MongoDB 提供了以下功能:

  • 面向文档的存储
  • 反规范化
  • 聚合管道

应用六边形架构的关键恰恰在于做出这些决定的位置以及应用这些特征的位置。

这些决定转移到:

  • 查询适配器
  • 投影模型
  • 读取优化的文档

而绝不是在领域实体或具有业务逻辑的接口中。

权衡与现实世界的约束

使用六边形架构设计系统并不容易。添加端口、适配器以及强制的、明确的边界意味着添加代码、间接层,最重要的是,更高的认知负荷,特别是对于那些首次应用这种风格的人来说。

真正的优势不是立竿见影的,而是随着时间的推移显现出来的。围绕整洁核心构建的系统往往更容易测试、更安全、更易于演进,并且更能抵御基础设施的变化。当持久化技术、框架或部署模型发生变化时,影响始终保持在边缘,而不是在代码内部传播。

如果我们需要设计一个能够经受时间考验的系统,那么引入的开销是一个非常值得付出的代价。

结论

应用端口和适配器架构并不是画六边形,而是明确职责和依赖关系。

为了维护核心的完整性并保留业务逻辑的表现力,将 MongoDB 和任何持久化技术及框架视为仅仅是适配器非常重要。这避免了导致新应用程序变成与所采用的框架和技术紧密绑定的巨型单体的缓慢恶化。

整洁的核心不是学术练习,而是对 Java 系统可维护性和可演进性的实际投资。

如果你已经在使用 MongoDB 和 Spring Boot,尝试从系统的一小部分开始:选择一个用例,定义其端口,并将 MongoDB 依赖项移到适配器后面。你会看到架构如何逐渐改善,其可维护性也是如此。

所有代码可在以下 链接 中找到。

本文 Java 中的端口和适配器:保持核心整洁 首次出现在 foojay 上。