六边形架构中application包应放什么?ArchUnit测试疑问
六边形架构中Application包的职责与内容规划
问题背景
我正在实现遵循DDD原则的简易六边形架构:
domain包存放领域模型(Domain Models)和领域服务(Domain Services)adapter包包含入站、出站适配器- 最初将ports放在
application包,配置ArchUnit的ApplicationService路径后,出现domain包访问application包的依赖问题:Class <...domain.services...> implements interface <...application.ports.in...> - 将ports移到
domain包后解决了依赖问题,但疑惑application包及ApplicationService的用途,不知道该包应放置什么内容。
当前架构结构:
- adapter |- inbound |- outbound - domain |- model |- services |- ports - application |- ??? - Application.kt
当前ArchUnit测试代码:
val `hexagonal architecture is respected`: ArchRule = onionArchitecture() .domainModels("$rootPackage.domain.model..") .domainServices("$rootPackage.domain.services..", "$rootPackage.domain.ports..") .applicationServices("$rootPackage.application..") .adapter("Adapter", "$rootPackage.adapter..") .withOptionalLayers(true)
核心解答:Application包的职责与内容
application包是领域层与外部适配器之间的协调层,不包含核心业务逻辑,主要负责流程编排、外部请求适配、全局事务/异常处理等,具体可放置以下内容:
1. 应用服务(Application Services)
这是application包的核心,作为入站适配器(如API控制器、消息消费者)和领域层的中间层,做以下事情:
- 接收外部请求,转化为领域层能处理的调用(比如把DTO转成领域实体)
- 编排多个领域服务的调用流程(比如创建订单时,先调用库存领域服务扣减库存,再调用订单领域服务创建订单)
- 管理事务边界(比如用
@Transactional注解包裹整个业务流程) - 调用出站适配器的实现(通过领域层的ports)
- 将领域层的结果转化为外部需要的响应格式(比如把领域实体转成DTO)
举个例子:OrderApplicationService的createOrder方法,会接收CreateOrderRequest,调用InventoryDomainService.checkAndDeductStock,再调用OrderDomainService.createOrder,最后调用OrderRepositoryPort.save完成持久化。
2. 请求/响应DTO
定义外部系统与应用层交互的数据结构:
- 入站DTO:比如API控制器接收的
CreateOrderRequest,隔离外部请求格式与领域模型 - 出站DTO:比如返回给前端的
OrderResponse,避免领域模型的内部结构暴露给外部
3. 全局异常处理
统一处理领域层抛出的业务异常,转化为外部适配器能识别的错误格式:
- 比如把领域层的
StockInsufficientException转化为HTTP 400响应 - 集中处理异常,避免在每个适配器中重复编写异常处理逻辑
4. 全局配置(可选)
如果有跨适配器或跨领域的全局配置,可放在这里(比如全局的事务配置、消息队列配置),不过这类配置也可以放在启动类所在的根包下,根据项目习惯调整。
5. 应用层端口(可选)
如果需要定义更上层的调用契约(比如某些复杂的跨领域操作入口),可以在这里定义应用层的端口,但通常入站适配器直接调用应用服务即可,不需要额外定义端口。
调整后的架构示例
- adapter |- inbound |- rest (API控制器,调用应用服务) |- messaging (消息消费者,调用应用服务) |- outbound |- persistence (实现领域层的仓储端口) |- notification (实现领域层的通知端口) - domain |- model (领域实体、值对象,核心业务逻辑) |- services (领域服务,处理跨实体的业务逻辑) |- ports |- in (领域层对外暴露的操作入口,如`CreateOrderUseCase`) |- out (领域层依赖的外部资源抽象,如`OrderRepositoryPort`) - application |- services (应用服务,如`OrderApplicationService`) |- dto (请求/响应对象,如`CreateOrderRequest`、`OrderResponse`) |- exception (全局异常处理类,如`ApplicationExceptionHandler`) - Application.kt (项目启动类)
ArchUnit配置说明
当前的ArchUnit配置是正确的:
domainServices包含domain.ports..,因为ports是领域层的一部分(依赖倒置原则,领域定义自身的依赖抽象)applicationServices指向application..,应用服务属于应用层,依赖领域层,不会出现反向依赖问题
内容的提问来源于stack exchange,提问作者Danny Steinbrecher
相关产品推荐
相关产品推荐

