六边形架构(Ports and Adapters)复杂业务工作流编排方案咨询
六边形架构下复杂工作流编排的解决方案
问题背景
我正在使用六边形架构(Ports and Adapters)实现复杂业务工作流,但在合理编排工作流方面遇到困难。
业务工作流需求
该工作流需处理以下步骤:
- REST API 请求触发初始事件
- 事件存储:持久化事件
- 决策逻辑:根据事件决定执行单个事件或触发事件链
- 规则加载:根据事件加载业务规则列表
- 规则执行:每个规则计算值并持久化结果
对应三个核心用例:
- EventHandling:处理入站事件并确定事件链
- RuleLoading + Execution:加载适用规则并编排其执行
- Specific Rules with Calculation:单个规则执行、值计算及持久化
当前架构困境
在六边形架构中,不确定工作流编排逻辑的放置位置,以及如何在不违反端口适配器原则的前提下协调这三个用例。
已尝试的两种方案及问题
方案1:应用服务编排
@Service class EventWorkflowApplicationUseCase( private val eventHandlingUseCase: EventHandlingUseCase, private val ruleLoadingExecutionUseCase: RuleLoadingExecutionUseCase, private val specificRuleCalculationUseCase: SpecificRuleCalculationUseCase ): EventWorkflowPort { override fun processEventWorkflow(request: EventWorkflowCommand): EventWorkflowResult { // Step 1: Handle event and determine chain val eventChain = eventHandlingUseCase.execute(EventHandlingCommand.from(request)) // Step 2: Load and execute rules val ruleResults = ruleLoadingExecutionUseCase.execute( RuleLoadingCommand.from(eventChain) ) // Step 3: Execute specific rule calculations val calculationResults = ruleResults.rules.map { rule -> specificRuleCalculationUseCase.execute( SpecificRuleCommand.from(rule) ) } return EventWorkflowResult(calculationResults) } }
问题:应用服务成为处理多关注点的“上帝服务”,直接调用其他用例,违反单一职责原则,导致用例间紧耦合。
方案2:基于端口的组合
// Separate ports for each concern interface EventManagementPort { fun storeEvent(command: StoreEventCommand): StoredEvent fun determineEventChain(event: StoredEvent): EventChain } interface RuleManagementPort { fun loadRules(eventChain: EventChain): List<Rule> fun executeRule(rule: Rule): RuleResult } // Orchestrating application service @Service class EventWorkflowApplicationService( private val eventManagement: EventManagementPort, private val ruleManagement: RuleManagementPort ) : EventWorkflowPort { override fun processEventWorkflow(command: EventWorkflowCommand): EventWorkflowResult { val storedEvent = eventManagement.storeEvent(StoreEventCommand.from(command)) val eventChain = eventManagement.determineEventChain(storedEvent) val rules = ruleManagement.loadRules(eventChain) val results = rules.map { rule -> ruleManagement.executeRule(rule) } return EventWorkflowResult(results) } }
问题:产生大量小端口,编排逻辑仍在应用服务中,且可能导致应用服务间调用入站端口,引发循环依赖,违反六边形架构原则。
咨询问题
在六边形架构中,复杂工作流编排逻辑应放置在何处?是否有其他更优方案?恳请提供符合六边形架构原则的工作流处理指导!
符合六边形架构的工作流编排方案
核心思路:将编排逻辑内聚为领域层工作流协调器
六边形架构的核心是领域层与外部隔离,编排逻辑本质是业务流程的一部分,应放在领域层而非应用服务层。通过定义领域服务作为工作流协调器,封装编排逻辑,同时依赖领域端口(而非应用用例或外部端口),保证领域层的独立性。
具体实现步骤
1. 定义领域端口(Domain Ports)
这些端口是领域层对外的依赖抽象,仅暴露领域所需的能力,避免细粒度端口:
// 领域端口:事件持久化与决策 interface EventPersistencePort { fun storeEvent(event: DomainEvent): StoredDomainEvent } interface EventChainDecisionPort { fun determineChain(event: StoredDomainEvent): EventChain } // 领域端口:规则加载与执行 interface RuleRepositoryPort { fun loadApplicableRules(chain: EventChain): List<BusinessRule> } interface RuleExecutionPort { fun executeRule(rule: BusinessRule): RuleCalculationResult }
2. 领域层工作流协调器(Domain Workflow Coordinator)
在领域层创建专门的工作流服务,封装编排逻辑,依赖上述领域端口:
// 领域服务:工作流编排 class EventProcessingWorkflow( private val eventPersistence: EventPersistencePort, private val chainDecision: EventChainDecisionPort, private val ruleRepository: RuleRepositoryPort, private val ruleExecutor: RuleExecutionPort ) { fun processEvent(eventCommand: InboundEventCommand): WorkflowProcessingResult { // 1. 持久化事件(调用领域端口) val domainEvent = eventCommand.toDomainEvent() val storedEvent = eventPersistence.storeEvent(domainEvent) // 2. 确定事件链(调用领域端口) val eventChain = chainDecision.determineChain(storedEvent) // 3. 加载规则(调用领域端口) val applicableRules = ruleRepository.loadApplicableRules(eventChain) // 4. 执行规则并收集结果(调用领域端口) val calculationResults = applicableRules.map { rule -> ruleExecutor.executeRule(rule) } return WorkflowProcessingResult(calculationResults) } }
3. 应用服务层的角色转变
应用服务不再负责编排,仅作为入口适配器与领域层的桥梁,处理请求转换、事务管理等跨领域关注点:
@Service class EventWorkflowApplicationService( private val eventProcessingWorkflow: EventProcessingWorkflow ) : EventWorkflowPort { override fun processEventWorkflow(request: EventWorkflowCommand): EventWorkflowResult { // 仅做请求转换和调用领域工作流 val inboundCommand = request.toInboundEventCommand() val domainResult = eventProcessingWorkflow.processEvent(inboundCommand) return domainResult.toApplicationResult() } }
4. 适配器实现领域端口
外部依赖(如数据库、规则引擎)通过适配器实现领域端口,注入到领域工作流中:
// 事件持久化适配器(实现领域端口) @Repository class JpaEventPersistenceAdapter : EventPersistencePort { override fun storeEvent(event: DomainEvent): StoredDomainEvent { // JPA持久化逻辑 } } // 规则执行适配器(实现领域端口) @Component class DroolsRuleExecutionAdapter : RuleExecutionPort { override fun executeRule(rule: BusinessRule): RuleCalculationResult { // Drools规则执行逻辑 } }
方案优势
- 符合六边形架构原则:编排逻辑在领域层,依赖抽象端口,与外部实现完全隔离
- 单一职责:应用服务仅做请求适配,领域工作流专注于业务流程编排,各端口专注单一能力
- 低耦合:领域层不依赖外部实现,适配器可独立替换(如换用不同规则引擎)
- 可测试性:领域工作流可通过Mock领域端口轻松进行单元测试
内容的提问来源于stack exchange,提问作者JavaNullPointer
相关产品推荐
相关产品推荐

