feat: initial approval workflow engine with in-memory and JDBC storage

Lightweight linear approval engine (v0.1):

- ordo-api: domain model, OrdoEngine port, repository SPI with
  conditional updates (insertIfAbsent, completeIfPending) and a
  TransactionExecutor port for atomic multi-step writes
- ordo-core: DefaultOrdoEngine running register/start/approve/reject
  inside a transaction boundary; in-memory engine and repositories
- ordo-storage-jdbc: thread-bound JDBC transactions, normalized V1
  schema migration, Jackson-based ProcessContext JSON codec
- tests: unit tests plus H2 integration tests; PostgreSQL integration
  tests run against a local instance via ordo.test.pg.* properties and
  skip when unreachable

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
0264408
2026-09-08 16:56:04 +08:00
co-authored by Claude Code
commit 397a9c229b
58 changed files with 2779 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jetlumen</groupId>
<artifactId>ordo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>ordo-example</artifactId>
<dependencies>
<dependency>
<groupId>com.jetlumen</groupId>
<artifactId>ordo-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.jetlumen</groupId>
<artifactId>ordo-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,32 @@
package com.jetlumen.ordo.example;
import com.jetlumen.ordo.api.ApprovalStep;
import com.jetlumen.ordo.api.OrdoEngine;
import com.jetlumen.ordo.api.ProcessContext;
import com.jetlumen.ordo.api.ProcessDefinition;
import com.jetlumen.ordo.core.InMemoryOrdoEngine;
import java.util.List;
import java.util.Map;
public final class LeaveRequestExample {
private LeaveRequestExample() {
}
public static void main(String[] args) {
OrdoEngine ordo = new InMemoryOrdoEngine();
ordo.register(new ProcessDefinition("leave-request", "Leave request", List.of(
new ApprovalStep("manager", "Manager approval", "maria"),
new ApprovalStep("hr", "HR approval", "henry")
)));
var instance = ordo.start("leave-request", "alice", new ProcessContext(Map.of(
"requestId", "LEAVE-2026-001"
)));
var managerTask = ordo.findTasks(instance.id()).getFirst();
ordo.approve(managerTask.id(), "maria", "Approved by the manager.");
var hrTask = ordo.findTasks(instance.id()).get(1);
ordo.approve(hrTask.id(), "henry", "Leave record updated.");
}
}