Embedded Workflow Orchestration with Spring Boot and Flowable BPMN
A comprehensive guide to embedding BPMN 2.0 workflow orchestration inside Spring Boot applications using Flowable, enabling sophisticated business process automation with data engineering pipeline patterns.
Table of Contents
Introduction
Modern enterprise applications often require sophisticated workflow orchestration to manage complex business processes. Whether you are building data engineering pipelines, approval workflows, or multi-step processing systems, embedding a workflow engine directly into your application provides flexibility, auditability, and separation of concerns that procedural code cannot match.
The spring-flowable project demonstrates how to embed a BPMN 2.0 workflow engine inside a Spring Boot application using Flowable, enabling declarative workflow definitions that execute as part of your microservice architecture.
Key Insight: Embedding Flowable in Spring Boot gives you the power of a full BPM engine with the simplicity of a self-contained microservice.
What is Flowable?
Flowable is a lightweight, open-source business process engine written in Java. It implements the BPMN 2.0 specification and provides:
- Process Definition: Define workflows using industry-standard BPMN 2.0 notation
- Process Execution: Execute complex workflows with parallel paths, loops, and conditional logic
- State Management: Automatic persistence and recovery of process state
- Integration: Seamless Spring Boot integration with auto-configuration
Why Embedded Workflow Engines?
Traditional workflow engines run as standalone servers, requiring additional infrastructure and network communication. Embedded engines offer distinct advantages:
| Aspect | Standalone Engine | Embedded Engine |
|---|---|---|
| Deployment | Separate service | Single JAR |
| Latency | Network round-trip | In-process calls |
| Transactions | Distributed | Local ACID |
| Scaling | Centralized | Per-service |
| Complexity | Higher | Lower |
Architecture Overview
The spring-flowable project implements a data engineering workflow pattern where different processing steps execute based on the type of execution:
Microservices Architecture
This architecture demonstrates a loop-based workflow where:
- The process evaluates the execution type
- Executes the appropriate step (Load, Move, or Process)
- Checks if there is more work to be done
- Continues looping or completes
- Handles exceptions by sending notification emails
Project Structure
The project follows standard Spring Boot conventions with Flowable integration:
spring-flowable/
├── src/
│ └── main/
│ ├── java/com/gonnect/flowable/
│ │ ├── SpringFlowableApplication.java
│ │ ├── FlowService.java
│ │ ├── LoadService.java
│ │ ├── MoveService.java
│ │ └── ProcessService.java
│ └── resources/
│ └── processes/
│ └── data-navigator-flows.bpmn20.xml
├── pom.xml
└── flow.png
Core Dependencies
The project uses Flowable's Spring Boot starter for seamless integration:
<dependencies>
<!-- Spring Boot Core -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Flowable Process Engine -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
<version>6.4.1</version>
</dependency>
<!-- Flowable IDM for Identity Management -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-idm-spring</artifactId>
<version>6.4.1</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-idm-spring-configurator</artifactId>
<version>6.4.1</version>
</dependency>
<!-- Groovy Scripting Support -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-groovy-script-static-engine</artifactId>
<version>6.4.1</version>
</dependency>
<!-- Embedded Database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
Understanding the Implementation
The Main Application
The Spring Boot application demonstrates how to start process instances programmatically:
@SpringBootApplication
public class SpringFlowableApplication {
public static void main(String[] args) {
SpringApplication.run(SpringFlowableApplication.class, args);
}
@Bean
CommandLineRunner basics(final RuntimeService runtimeService) {
return new CommandLineRunner() {
@Override
public void run(String... strings) throws Exception {
// Start the waiter process
runtimeService.startProcessInstanceByKey(
"waiter",
Collections.singletonMap("customerId", (Object) 243L)
);
// Start the data navigator workflow
runtimeService.startProcessInstanceByKey(
"data-navigator-flows",
Collections.singletonMap("execution", "1")
);
}
};
}
}
The RuntimeService is Flowable's primary API for starting and managing process instances. Process variables (like customerId and execution) are passed as a map and become available throughout the workflow.
The Flow Service
The FlowService acts as a decision service, determining which path the workflow should take:
@Service
public class FlowService {
private boolean isMoveStep;
private boolean isProcessStep;
private boolean isLoadStep = true;
private boolean isException;
private boolean isNextDataEngineeringFlow;
public boolean isMoveStep(String execution) {
System.out.println("isMoveStep = " + isMoveStep);
return isMoveStep;
}
public boolean isProcessStep(String execution) {
System.out.println("isProcessStep = " + isProcessStep);
return isProcessStep;
}
public boolean isLoadStep(String execution) {
System.out.println("isLoadStep = " + isLoadStep);
return isLoadStep;
}
public boolean isException(String execution) {
System.out.println("isException = " + isException);
return isException;
}
public boolean isNextDataEngineeringFlow(String execution) {
System.out.println("isNextDataEngineeringFlow = " + isNextDataEngineeringFlow);
return isNextDataEngineeringFlow;
}
}
This service is invoked by BPMN gateway conditions to route the workflow. In production, these decisions would be based on actual business logic, database queries, or external service calls.
Processing Steps
Each processing step is a simple Spring service that Flowable invokes via service tasks:
@Service
public class LoadService {
public void execute(String execution) {
System.out.println("LoadService#execute(execution)");
// Implement data loading logic
}
}
@Service
public class MoveService {
public void execute(String execution) {
System.out.println("MoveService#execute(execution)");
// Implement data movement logic
}
}
@Service
public class ProcessService {
public void execute(String execution) {
System.out.println("ProcessService#execute(execution)");
// Implement data processing logic
}
}
BPMN Process Definition
The workflow is defined in a BPMN 2.0 XML file that Flowable automatically deploys on startup. Here is a conceptual representation of the data navigator flow:
Microservices Architecture
BPMN Elements Used
| Element | Purpose |
|---|---|
| Start Event | Entry point for the process |
| Exclusive Gateway | Decision point (XOR logic) |
| Service Task | Invokes Spring beans |
| End Event | Process completion |
| Sequence Flow | Connects elements with conditions |
Data Engineering Pipeline Pattern
The spring-flowable project demonstrates a common data engineering pattern where different operations execute based on context:
Microservices Architecture
This pattern provides:
- Separation of Concerns: Each step handles one responsibility
- Restartability: Failed workflows resume from the last successful step
- Visibility: Process state is queryable at any time
- Auditability: Full history of workflow execution
Advanced Flowable Features
Process Variables
Pass and access data throughout the workflow:
// Starting with variables
Map<String, Object> variables = new HashMap<>();
variables.put("customerId", 243L);
variables.put("orderAmount", 1500.00);
runtimeService.startProcessInstanceByKey("orderProcess", variables);
// Accessing in a service task
@Service
public class OrderProcessor {
public void process(DelegateExecution execution) {
Long customerId = (Long) execution.getVariable("customerId");
Double amount = (Double) execution.getVariable("orderAmount");
// Process the order
}
}
Async Execution
For long-running tasks, enable asynchronous execution:
<serviceTask id="longRunningTask"
flowable:async="true"
flowable:delegateExpression="${longRunningService}"/>
Event Listeners
React to process events:
@Component
public class ProcessEventListener implements FlowableEventListener {
@Override
public void onEvent(FlowableEvent event) {
if (event.getType() == FlowableEngineEventType.PROCESS_COMPLETED) {
ProcessInstance instance = ((FlowableProcessEvent) event)
.getProcessInstance();
log.info("Process {} completed", instance.getId());
}
}
}
Running the Application
Clone and run the application:
# Clone the repository
git clone https://github.com/mgorav/spring-flowable.git
cd spring-flowable
# Build with Maven
./mvnw clean install
# Run the application
./mvnw spring-boot:run
On startup, Flowable automatically:
- Creates the necessary database tables in H2
- Deploys BPMN process definitions from
resources/processes/ - Makes the
RuntimeServiceavailable for process management
Monitoring and Management
Flowable provides comprehensive APIs for process management:
@RestController
@RequestMapping("/api/processes")
public class ProcessController {
@Autowired
private RuntimeService runtimeService;
@Autowired
private HistoryService historyService;
@GetMapping("/running")
public List<ProcessInstanceInfo> getRunningProcesses() {
return runtimeService.createProcessInstanceQuery()
.active()
.list()
.stream()
.map(this::toInfo)
.collect(Collectors.toList());
}
@GetMapping("/history")
public List<HistoricProcessInstanceInfo> getHistory() {
return historyService.createHistoricProcessInstanceQuery()
.finished()
.orderByProcessInstanceEndTime()
.desc()
.list()
.stream()
.map(this::toHistoryInfo)
.collect(Collectors.toList());
}
}
Best Practices
Process Design
- Keep processes simple: Break complex workflows into sub-processes
- Use meaningful IDs: Process and task IDs should be descriptive
- Handle exceptions: Include error boundary events and compensation handlers
- Version processes: Use deployment versioning for upgrades
Integration Patterns
- Delegate to Spring beans: Use
delegateExpressionfor service tasks - Use async where appropriate: Long-running tasks should be async
- Leverage events: React to process events for monitoring and integration
- Test thoroughly: Use Flowable's testing support
@SpringBootTest
public class WorkflowTest {
@Autowired
private RuntimeService runtimeService;
@Test
public void testDataNavigatorFlow() {
ProcessInstance instance = runtimeService
.startProcessInstanceByKey("data-navigator-flows",
Collections.singletonMap("execution", "1"));
assertNotNull(instance);
// Assert process completed successfully
}
}
Conclusion
The spring-flowable project demonstrates the power of embedding BPMN workflow orchestration directly into Spring Boot applications. This approach offers:
- Simplified Deployment: Single JAR with workflow capabilities
- Transactional Consistency: Workflow state participates in local transactions
- Spring Integration: Seamless injection of Spring beans into workflow tasks
- Standardization: BPMN 2.0 compliance for industry-standard process modeling
- Flexibility: Suitable for data engineering pipelines, approval workflows, and complex business processes
By leveraging Flowable's lightweight engine with Spring Boot's auto-configuration, developers can implement sophisticated workflow orchestration without the overhead of managing separate BPM infrastructure.