4. 服务边界定义:微服务架构下的服务边界原则,如何将业务能力映射为Temporal Workflow与Activity
在微服务架构中,服务边界的合理划分是系统稳定性和演进能力的基础。Temporal Workflow 与 Activity 的映射并非简单的代码拆分,而是需要遵循领域驱动设计(DDD)的限界上下文原则,将业务能力与分布式协调逻辑解耦。
4.1 服务边界划分的核心原则
微服务边界的定义应遵循以下原则,以确保每个服务具备高内聚、低耦合的特性:
| 原则 | 说明 | 反例 |
|---|---|---|
| 限界上下文 | 每个服务对应一个明确的业务领域边界,内部模型自治 | 订单服务直接操作库存数据库表 |
| 数据主权 | 服务拥有其数据的唯一写权限,其他服务通过API访问 | 多个服务共享同一张数据库表 |
| 业务能力 | 服务边界由业务能力而非技术分层决定 | 将“用户验证”拆成独立的HTTP服务 |
| 变更频率 | 将变更速率不同的逻辑隔离到不同服务 | 核心交易逻辑与报表逻辑混在一起 |
| 故障隔离 | 一个服务的故障不应级联扩散到其他服务 | 同步调用链路过深,无熔断机制 |
实践建议: 在划分服务边界时,优先使用事件风暴(Event Storming)识别聚合根与限界上下文。每个聚合根通常对应一个Temporal Workflow,而聚合根内部的操作对应Activity。
4.2 业务能力到Temporal Workflow与Activity的映射模型
将业务能力映射到Temporal原语时,需要遵循以下对应关系:
- Workflow:对应一个完整的业务用例或流程编排。它负责定义步骤顺序、补偿逻辑、超时策略,以及跨服务的协调。Workflow 本身不应包含业务计算逻辑,只做编排。
- Activity:对应一个不可分割的业务操作或外部调用。Activity 是幂等的、可重试的,并且通常在一个微服务内部执行。Activity 可以访问数据库、调用外部API或执行本地计算。
下图展示了从业务能力到Temporal原语的映射关系:
4.3 映射策略:从业务事件到Workflow定义
将业务能力映射为Temporal Workflow时,推荐采用以下步骤:
- 识别业务事件:列出所有触发业务流程的事件(如“订单已提交”、“支付已确认”)。
- 定义Workflow接口:每个业务事件对应一个Workflow类型,Workflow名称应体现业务意图(如
OrderFulfillmentWorkflow)。 - 分解Activity:将Workflow内的每个步骤拆分为Activity,确保每个Activity只做一件事,且具备幂等性。
- 确定Activity归属服务:根据限界上下文,将Activity分配到对应的微服务中。Activity的实现类可以位于不同服务的代码库中。
- 定义重试与超时策略:为每个Activity设置合理的重试间隔和超时时间,避免Workflow因临时故障而失败。
关键原则: Workflow 代码应保持轻量,不包含任何 I/O 操作或线程阻塞调用。所有与外部系统的交互都必须封装在 Activity 中。这保证了 Workflow 的执行历史可以被完整持久化,并且 Worker 可以安全地重放。
4.4 跨服务Activity的通信机制
当Activity需要调用其他微服务时,Temporal提供了多种通信模式:
| 模式 | 适用场景 | 实现方式 |
|---|---|---|
| 同步请求-响应 | 需要立即获取结果的查询操作 | Activity 内部使用 HTTP/gRPC 调用下游服务 |
| 异步消息 | 不需要立即响应的操作,如发送通知 | Activity 将消息写入消息队列,由下游服务消费 |
| 子Workflow | 跨服务的复杂编排,需要独立的生命周期管理 | 父Workflow通过 ChildWorkflowStub 启动子Workflow |
| 信号(Signal) | 等待外部事件或用户输入 | Workflow 调用 Workflow.await 等待特定信号 |
下图展示了Activity跨服务调用的典型架构:
4.5 代码示例:订单履约Workflow
以下示例展示如何将订单履约的业务能力映射为Temporal Workflow与Activity:
// 1. 定义 Workflow 接口
public interface OrderFulfillmentWorkflow {
@WorkflowMethod
OrderResult fulfillOrder(OrderInput input);
}
// 2. 实现 Workflow 编排
public class OrderFulfillmentWorkflowImpl implements OrderFulfillmentWorkflow {
private final PaymentActivity paymentActivity =
Workflow.newActivityStub(PaymentActivity.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(10))
.setRetryOptions(RetryOptions.newBuilder()
.setMaximumAttempts(3)
.build())
.build());
private final InventoryActivity inventoryActivity =
Workflow.newActivityStub(InventoryActivity.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(5))
.build());
private final NotificationActivity notificationActivity =
Workflow.newActivityStub(NotificationActivity.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofSeconds(3))
.build());
@Override
public OrderResult fulfillOrder(OrderInput input) {
// Step 1: 预留库存
boolean reserved = inventoryActivity.reserve(input.getItems());
if (!reserved) {
throw new ApplicationFailureException("库存不足");
}
// Step 2: 处理支付
PaymentResult payment = paymentActivity.process(input.getPaymentInfo());
// Step 3: 发送通知
notificationActivity.sendOrderConfirmation(input.getUserId());
return new OrderResult(payment.getTransactionId());
}
}
// 3. 定义 Activity 接口(位于不同微服务中)
public interface PaymentActivity {
@ActivityMethod
PaymentResult process(PaymentInfo info);
}
public interface InventoryActivity {
@ActivityMethod
boolean reserve(List<Item> items);
}
public interface NotificationActivity {
@ActivityMethod
void sendOrderConfirmation(String userId);
}
常见误区: 不要将多个业务步骤合并到一个 Activity 中。例如,将“支付处理”和“库存预留”放在同一个 Activity 里,会导致重试时重复执行已完成的操作,破坏幂等性。每个 Activity 应只封装一个不可分割的业务操作。
4.6 服务边界映射检查清单
在完成映射后,使用以下清单进行验证:
- 单一职责:每个 Activity 是否只做一件事?
- 幂等性:Activity 重复执行是否会产生副作用?
- 数据隔离:Activity 是否只访问所属服务的数据?
- 超时覆盖:每个 Activity 是否设置了合理的超时时间?
- 错误处理:Workflow 是否定义了明确的补偿逻辑(如 Saga 模式)?
- 版本兼容:Activity 签名变更时,是否考虑了 Workflow 历史重放的兼容性?
遵循以上原则和映射策略,可以确保微服务架构下的Temporal Workflow具备清晰的边界、良好的可维护性和高可用性。