Master Long‑Running Workflows in Spring Boot 3 with Temporal
This article walks through building a robust, long‑running order‑processing workflow in Spring Boot 3 using the open‑source Temporal workflow engine, covering environment setup, Maven dependencies, configuration, workflow and activity definitions, service and controller code, and testing of retry and recovery features.
Introduction
In enterprise Spring Boot applications, long‑running tasks such as order processing and payment settlement often suffer from fragile scheduling, duplicated retry logic, and tangled state management, leading to poor stability and high maintenance cost.
What is Temporal?
Temporal is an open‑source distributed workflow engine that lets developers model complex business logic as code‑defined workflows. It provides reliable event‑driven execution, automatic state persistence, built‑in retry policies, and structured state storage, eliminating the need for custom timers, database flags, or manual compensation logic.
Use‑Case Overview
The example implements an order‑processing workflow with the following steps:
Validate order
Charge payment
Reserve inventory
Notify shipping
Send confirmation email
Each step may take time or fail, requiring robust handling.
1. Environment Preparation
Download the Temporal server from the official site and start it in development mode: temporal server start-dev Optionally change the UI port: temporal server start-dev --ui-port 8080 To persist data between restarts, specify a database file:
temporal server start-dev --db-filename pack.db2. Add Maven Dependency
<dependency>
<groupId>io.temporal</groupId>
<artifactId>temporal-spring-boot-starter</artifactId>
<version>1.30.1</version>
</dependency>3. Configuration
spring:
temporal:
namespace: default
connection:
target: 127.0.0.1:7233
enable-https: false
workers:
- name: order-process-worker
task-queue: order-process-queue
workersAutoDiscovery:
packages:
- com.pack4. Define the Workflow
@WorkflowInterface
public interface OrderWorkflow {
@WorkflowMethod
void processOrder(String orderId);
}5. Implement the Workflow
@WorkflowImpl(workers = "order-process-worker")
public class OrderWorkflowImpl implements OrderWorkflow {
private final Activities activities = Workflow.newActivityStub(
Activities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofMinutes(1))
.setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(5).build())
.build());
@Override
public void processOrder(String orderId) {
activities.validateOrder(orderId);
activities.chargePayment(orderId);
activities.reserveInventory(orderId);
activities.notifyShipping(orderId);
activities.sendConfirmationEmail(orderId);
}
}6. Define Activities
@ActivityInterface
public interface Activities {
@ActivityMethod
void validateOrder(String orderId);
@ActivityMethod
void chargePayment(String orderId);
@ActivityMethod
void reserveInventory(String orderId);
@ActivityMethod
void notifyShipping(String orderId);
@ActivityMethod
void sendConfirmationEmail(String orderId);
}7. Implement Activities
@Component
@ActivityImpl(workers = "order-process-worker")
public class ActivitiesImpl implements Activities {
@Override
public void validateOrder(String orderId) {
System.out.printf("验证订单【%s】%n", orderId);
}
@Override
public void chargePayment(String orderId) {
System.out.printf("付款扣费【%s】%n", orderId);
}
@Override
public void reserveInventory(String orderId) {
System.out.printf("预留商品库存【%s】%n", orderId);
try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) {}
}
@Override
public void notifyShipping(String orderId) {
System.out.printf("通知物流供应商【%s】%n", orderId);
try { TimeUnit.SECONDS.sleep(30); } catch (InterruptedException e) {}
}
@Override
public void sendConfirmationEmail(String orderId) {
System.out.printf("向客户发送确认邮件【%s】%n", orderId);
}
}8. Start the Workflow
@Service
public class OrderService {
private final WorkflowClient workflowClient;
public OrderService(WorkflowClient workflowClient) { this.workflowClient = workflowClient; }
public void startWorkflow(String orderId) {
OrderWorkflow workflow = workflowClient.newWorkflowStub(
OrderWorkflow.class,
WorkflowOptions.newBuilder().setTaskQueue("order-process-queue").build());
WorkflowClient.start(workflow::processOrder, orderId);
}
}9. Expose via Controller
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) { this.orderService = orderService; }
@GetMapping("/process")
public ResponseEntity<?> process() {
orderService.startWorkflow("xxxooo");
return ResponseEntity.ok("success");
}
}10. Testing and Retry Demonstration
Calling /orders/process returns immediately while the workflow runs asynchronously. The Temporal UI shows the workflow progress. If the Temporal server is stopped during a long‑running activity, the built‑in retry policy automatically resumes the activity when the server is restarted, continuing from the last successful step.
Conclusion
Temporal together with Spring Boot provides a powerful, fault‑tolerant solution for long‑running business processes, eliminating boilerplate retry, state‑tracking, and compensation code, and allowing developers to focus on core business logic.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
