如何在Go应用的整洁架构中集成Temporal Workflow?
整洁架构下Go应用集成Temporal的最佳实践
核心定位:Temporal属于适配器层
明确一点:Temporal是保障工作流可靠执行的基础设施,本质是业务逻辑的执行调度器,不属于业务逻辑层。业务逻辑层(用例层)只负责定义业务规则,不关心执行的可靠性、重试、持久化等细节——这些都是Temporal的职责,归属于适配器/基础设施层。
代码结构设计
1. 业务逻辑层(用例层):完全独立于Temporal
用例层只定义业务规则,依赖抽象接口而非Temporal的具体类型,彻底避免框架侵入。修改你之前的UseCase接口,移除workflow.Context这类Temporal专属类型:
// internal/usecase/usecase.go package usecase import "your-app/internal/entity" // 用例接口:仅关注业务输入输出,无外部框架依赖 type EntityWorkflow interface { ProcessEntity(entity *entity.Entity) (*entity.Entity, error) } // 具体用例实现:纯业务逻辑,无Temporal相关代码 type EntityWorkflowImpl struct{} func (e *EntityWorkflowImpl) ProcessEntity(entity *entity.Entity) (*entity.Entity, error) { // 核心业务逻辑:校验、计算、调用领域服务等 if entity.Status == "invalid" { return nil, fmt.Errorf("entity validation failed") } entity.Status = "processed" return entity, nil }
2. 适配器层:Temporal工作流作为业务逻辑调度器
在适配器层实现Temporal的工作流与活动,这里才会依赖Temporal API,核心职责是调度用例层的业务逻辑:
// internal/adapter/temporal/workflow.go package temporal import ( "go.temporal.io/sdk/workflow" "your-app/internal/usecase" "your-app/internal/entity" ) // Temporal工作流:仅负责调度,不包含业务规则 func EntityWorkflow(ctx workflow.Context, req entity.Entity) (*entity.Entity, error) { // 通过依赖注入获取用例实例(避免硬编码初始化) uc := usecase.NewEntityWorkflowImpl() return uc.ProcessEntity(&req) } // 拆分的活动(可选):同样仅做调度 func ProcessEntityActivity(ctx workflow.Context, entity *entity.Entity) (*entity.Entity, error) { uc := usecase.NewEntityWorkflowImpl() return uc.ProcessEntity(entity) }
3. 交付层:触发Temporal工作流的入口
交付层(HTTP、Kafka、gRPC等)仅负责接收外部请求、转换为业务实体,再调用Temporal客户端触发工作流,不包含任何业务逻辑:
Kafka消费者示例
// internal/delivery/kafka/consumer.go package kafka import ( "context" "encoding/json" "go.temporal.io/sdk/client" "your-app/internal/entity" "your-app/internal/adapter/temporal" ) type EntityConsumer struct { temporalClient client.Client } func NewEntityConsumer(tc client.Client) *EntityConsumer { return &EntityConsumer{temporalClient: tc} } func (c *EntityConsumer) ConsumeMessage(ctx context.Context, msg []byte) error { // 解析Kafka消息为业务实体 var req entity.Entity if err := json.Unmarshal(msg, &req); err != nil { return err } // 触发Temporal工作流 opts := client.StartWorkflowOptions{ ID: "entity-workflow-" + req.ID, TaskQueue: "entity-task-queue", } _, err := c.temporalClient.ExecuteWorkflow(ctx, opts, temporal.EntityWorkflow, req) return err }
gRPC对外暴露示例
如果需要通过Protocol Buffers对外提供服务,在gRPC handler中完成请求转换与工作流触发:
// internal/delivery/grpc/handler.go package grpc import ( "context" "go.temporal.io/sdk/client" pb "your-app/proto" "your-app/internal/entity" "your-app/internal/adapter/temporal" ) type EntityHandler struct { pb.UnimplementedEntityServiceServer temporalClient client.Client } func NewEntityHandler(tc client.Client) *EntityHandler { return &EntityHandler{temporalClient: tc} } func (h *EntityHandler) ProcessEntity(ctx context.Context, req *pb.ProcessEntityRequest) (*pb.ProcessEntityResponse, error) { // 转换gRPC请求为业务实体 entityReq := entity.Entity{ ID: req.Id, Status: req.Status, // 其他字段映射 } // 触发Temporal工作流 opts := client.StartWorkflowOptions{ ID: "entity-workflow-" + req.Id, TaskQueue: "entity-task-queue", } _, err := h.temporalClient.ExecuteWorkflow(ctx, opts, temporal.EntityWorkflow, entityReq) if err != nil { return nil, err } return &pb.ProcessEntityResponse{Status: "accepted"}, nil }
关键接口设计原则
- 用例层绝对不依赖Temporal类型:
workflow.Context、client.Client等框架类型不能出现在用例接口中,用例仅接受业务实体与标准context.Context(如需)。 - Temporal适配器依赖用例抽象:适配器层的工作流/活动调用用例的抽象接口,而非具体实现,确保用例可独立测试,无需启动Temporal。
- 交付层只做请求转换与触发:无论哪种触发源,都仅负责外部请求到业务实体的转换,以及调用Temporal客户端,不处理业务逻辑。
测试策略
- 用例层测试:直接测试用例实现,模拟依赖的领域服务,无需Temporal环境。
- Temporal适配器测试:用Temporal测试框架验证工作流/活动的调度逻辑(重试、超时等),Mock用例实现。
- 交付层测试:Mock Temporal客户端,验证请求转换与触发逻辑的正确性。
内容的提问来源于stack exchange,提问作者Drive In Your Mind
相关产品推荐
相关产品推荐

