A production-ready RESTful Workflow Engine built with Spring Boot 3 that orchestrates configurable multi-step approval pipelines. Designed with clean architecture, JWT-based security, and full CI/CD automation.
- Overview
- Features
- Architecture
- Tech Stack
- API Reference
- How Approval Flow Works
- Getting Started
- Running with Docker
- Testing & Coverage
- CI/CD Pipeline
- Project Structure
The Workflow Engine is a backend system that automates multi-step approval processes for organizational workflows such as leave applications and expense claims. Requests move through ordered approval steps, each gated by role-based authorization. Every action is recorded in a complete audit trail.
Use cases it solves:
- Leave approval pipelines (e.g., LEAVE β Approver β Admin)
- Expense claim routing with sequential sign-off
- Any domain requiring ordered, role-gated approvals
| Feature | Details |
|---|---|
| π JWT Authentication | Stateless auth using JJWT 0.12.5 with BCrypt password hashing |
| π‘οΈ Role-Based Access Control | REQUESTER, APPROVER, ADMIN roles with method-level @PreAuthorize |
| π Multi-Step Approval Engine | Configurable ordered steps per request type; engine auto-advances or finalizes |
| π¦ Request Lifecycle | Full PENDING β APPROVED / REJECTED state management |
| π Audit History | Immutable per-request approval history with actor, action, and timestamp |
| β 90%+ Test Coverage | Enforced by JaCoCo; build fails below threshold |
| π³ Dockerized | Multi-stage Docker build with Eclipse Temurin JRE 17 Alpine image |
| π€ CI/CD | GitHub Actions pipeline with Maven cache, JaCoCo report upload, and SonarCloud scan |
| ποΈ JPA Auditing | BaseEntity with createdBy / createdAt auto-populated via AuditorAware |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client (HTTP) β
βββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββΌβββββββββββββ
β JWT Auth Filter β β validates Bearer token
ββββββββββββββ¬βββββββββββββ
β
βββββββββββββββββββββββΌβββββββββββββββββββββββ
β REST Controllers β
β AuthController RequestController β
βββββββββββββββββββββββ¬βββββββββββββββββββββββ
β
βββββββββββββββββββββββΌβββββββββββββββββββββββ
β Service Layer β
β RequestService | UserService β
ββββββββ¬βββββββββββββββββββββββββββ¬ββββββββββββ
β β
βββββββββββββββΌβββββββ ββββββββββββββΌβββββββββββββ
β Approval Engine β β Auth / User Mgmt β
β β Step lookup β β β Register / Login β
β β Role validation β β β Role assignment β
β β History record β βββββββββββββββββββββββββββ
β β State advance β
βββββββββββββββ¬βββββββ
β
βββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ
β JPA Repositories (H2 / any RDBMS) β
β RequestRepo | ApprovalStepRepo | HistoryRepo | ... β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
User βββ ManyToMany βββΊ Role
Request βββ OneToMany βββΊ ApprovalHistory
ApprovalStep (type + stepOrder + role) β workflow config table
| Layer | Technology |
|---|---|
| Language | Java 17 |
| Framework | Spring Boot 3.5 |
| Security | Spring Security 6 + JJWT 0.12.5 |
| Persistence | Spring Data JPA + H2 (dev) |
| Validation | Spring Validation (Bean Validation 3) |
| Build | Maven + Maven Wrapper |
| Testing | JUnit 5 + Spring Security Test + Testcontainers |
| Coverage | JaCoCo (β₯ 90% line coverage enforced) |
| Code Quality | SonarCloud + Qodana |
| Containerization | Docker (multi-stage, Eclipse Temurin 17 Alpine) |
| CI/CD | GitHub Actions |
| Method | Endpoint | Access | Description |
|---|---|---|---|
POST |
/auth/register-user |
Public | Register a new user with roles |
POST |
/auth/login |
Public | Authenticate and receive a JWT |
Login response:
{
"id": 1,
"email": "user@example.com",
"token": "<JWT>",
"roles": ["ROLE_REQUESTER"]
}All endpoints below require a valid
Authorization: Bearer <token>header.
| Method | Endpoint | Role Required | Description |
|---|---|---|---|
POST |
/requests |
REQUESTER |
Create a new workflow request |
GET |
/requests/{id} |
REQUESTER, APPROVER, ADMIN |
Fetch request details |
POST |
/requests/{id}/approve?userId=&role= |
APPROVER, ADMIN |
Approve current step |
POST |
/requests/{id}/reject?userId=&role= |
APPROVER, ADMIN |
Reject the request |
GET |
/requests/history/{id} |
REQUESTER, ADMIN |
Get full approval audit trail |
Create request body:
{
"type": "LEAVE",
"username": "john.doe"
}1. REQUESTER creates a request β status: PENDING, currentStepOrder: 1
2. APPROVER (step 1) calls /approve
βββ Engine checks: role matches ApprovalStep for (type, stepOrder=1)?
βββ β
YES β records history, advances to stepOrder: 2
βββ β NO β throws "Unauthorized for this step"
3. ADMIN (step 2) calls /approve
βββ Engine checks: any next step exists?
βββ YES β advance step order
βββ NO β status: APPROVED β
4. Any approver calls /reject at any step β status: REJECTED β
5. GET /requests/history/{id} β full ordered audit trail
The approval_steps table is the configuration table β you define the workflow by inserting rows:
| request_type | step_order | role |
|---|---|---|
| LEAVE | 1 | APPROVER |
| LEAVE | 2 | ADMIN |
| EXPENSE | 1 | APPROVER |
- Java 17+
- Maven 3.8+ (or use
./mvnw)
git clone https://github.com/jaysharmagithub/workflowengine.git
cd workflowengine
# Run the application
./mvnw spring-boot:runThe app starts on http://localhost:8080 by default (configurable via PORT env var).
# 1. Register a user
curl -X POST http://localhost:8080/auth/register-user \
-H "Content-Type: application/json" \
-d '{"firstName":"Jay","lastName":"Sharma","email":"jay@example.com","password":"pass123","roles":[{"name":"ROLE_REQUESTER"}]}'
# 2. Login
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"jay@example.com","password":"pass123"}'
# 3. Create a workflow request (use token from step 2)
curl -X POST http://localhost:8080/requests \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"type":"LEAVE","username":"jay@example.com"}'# Build the image
docker build -t workflowengine .
# Run the container
docker run -p 8080:8080 workflowengineThe multi-stage Dockerfile:
- Stage 1 β Builds the JAR using
maven:3.8.4-openjdk-17 - Stage 2 β Runs with slim
eclipse-temurin:17-jre-alpinefor minimal image size
# Run all tests with coverage report
./mvnw clean verify- Coverage report generated at
target/site/jacoco/index.html - Build fails if line coverage drops below 90% (enforced via JaCoCo
<check>rule) - Excludes:
entity,dto,configpackages, and the main application class
Every push to main and every Pull Request triggers:
ββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββββββ βββββββββββββββββ
β Checkout βββββΊβ Setup JDK 17 βββββΊβ mvn clean verify βββββΊβ Upload JaCoCo β
β β β (Temurin) β β (Tests + Coverage) β β Report β
ββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββββββ βββββββββββββββββ
β
βββββββββΌββββββββ
β SonarCloud β
β Scan β
βββββββββββββββββ
Maven dependency cache is keyed to pom.xml hash for fast builds.
workflowengine/
βββ .github/
β βββ workflows/
β βββ ci.yml # CI pipeline
β βββ qodana_code_quality.yml # Static analysis
βββ src/main/java/com/techpulseIt/workflowengine/
β βββ config/ # JPA Auditing config & AuditorAware
β βββ controller/
β β βββ AuthController.java # /auth endpoints
β β βββ RequestController.java # /requests endpoints
β βββ dto/ # Response DTOs
β βββ entity/
β β βββ BaseEntity.java # Audit fields (id, createdBy, createdAt)
β β βββ User.java
β β βββ Role.java
β β βββ Request.java # Workflow request
β β βββ ApprovalStep.java # Workflow config (type + order + role)
β β βββ ApprovalHistory.java # Immutable audit trail
β βββ exception/ # Global exception handler + custom exceptions
β βββ repository/ # Spring Data JPA repositories
β βββ request/ # Request body POJOs
β βββ response/ # Unified API response wrappers
β βββ security/
β β βββ SecurityConfig.java # Spring Security filter chain
β β βββ CorsConfig.java
β β βββ jwt/ # JWT filter, entry point, utilities
β βββ service/
β βββ RequestService.java # Core approval engine logic
β βββ UserServiceImpl.java
β βββ RoleServiceImpl.java
βββ src/test/ # Unit & integration tests
βββ Dockerfile # Multi-stage Docker build
βββ pom.xml
- Stateless security β No HTTP sessions; every request is authenticated via JWT, making the service horizontally scalable.
- Data-driven workflow β The approval chain is defined in the
approval_stepstable, not in code, so new workflows can be added without redeployment. - Self-approval prevention β The engine explicitly blocks a requester from approving their own request.
- Fail-fast coverage gate β JaCoCo enforces β₯ 90% line coverage at build time, preventing untested code from merging.
- Clean layer separation β Controllers handle HTTP concerns, Services own business logic, Repositories are pure data access.
This project is licensed under the MIT License.