Aurum is a transaction ledger API built with Java 21, Spring Boot and PostgreSQL. It manages customer accounts, funding, withdrawals, transfers and reversals through an immutable double-entry ledger. Database constraints, row locking, idempotency records and reconciliation protect the ledger when requests are concurrent, retried or interrupted.
I built Aurum to learn how financial backends apply double-entry accounting, ACID transactions, concurrency control, idempotent API design, role-based access control (RBAC), audit trails, failure recovery, database benchmarking and container-based delivery.
- Every transaction contains balanced debit and credit entries in one currency.
- Ledger transactions and entries are append-only; corrections create linked reversal transactions with compensating entries.
- Amounts use integer minor units, so INR
2500represents INR 25.00. - PostgreSQL commits the ledger entries, balance projection, idempotency result and audit event in one transaction.
- Database triggers reject ledger mutations and unbalanced postings.
- Deterministic
SELECT ... FOR UPDATElocking prevents lost updates and overspending. - Database-backed idempotency returns the original transaction for an exact retry and rejects a reused key with a different payload.
- Transfers retry PostgreSQL deadlocks and serialization failures with bounded jitter.
- CUSTOMER, OPERATOR, AUDITOR and ADMIN roles control routes and account-level access.
- Accounts have durable owners and support ACTIVE, FROZEN and irreversible CLOSED states.
- Actor-attributed audit events record sensitive user, account, ledger and rebuild operations.
- Scheduled reconciliation compares balance projections with balances derived from the ledger and stores immutable run reports.
- JUnit, MockMvc, Testcontainers and jqwik cover HTTP contracts, PostgreSQL constraints, concurrency, rollback, failure injection and generated operation sequences.
- SQL and HTTP benchmark harnesses report query plans, throughput and latency percentiles.
- Spring Boot Actuator exposes health checks and fixed-cardinality ledger metrics.
- GitHub Actions verifies every push and pull request and publishes tagged images to GHCR.
| Area | Technology |
|---|---|
| Application | Java 21, Spring Boot 3.5, Spring MVC, Spring JDBC |
| Data | PostgreSQL 16, Flyway |
| Security | Spring Security, HTTP Basic, PBKDF2 password hashing |
| Testing | JUnit 5, MockMvc, Testcontainers, jqwik |
| Observability | Spring Boot Actuator, Micrometer |
| Delivery | Maven, Docker, Docker Compose, GitHub Actions, GHCR |
- Java 21 JDK
- Maven 3.6.3 or newer
- Docker with Compose v2
jqfor the command-line walkthrough below
Start PostgreSQL and the application:
docker compose up -d postgres
mvn spring-boot:runThe API is available at http://localhost:8080; health is available at
http://localhost:8080/actuator/health. Flyway applies the schema migrations during startup.
The local configuration creates four bootstrap users:
| Username | Password | Role |
|---|---|---|
customer |
customer-local |
CUSTOMER |
operator |
operator-local |
OPERATOR |
auditor |
auditor-local |
AUDITOR |
admin |
admin-local |
ADMIN |
These credentials are for local use. Configure different usernames and passwords through the
AURUM_*_USERNAME and AURUM_*_PASSWORD environment variables for a shared environment.
Create two INR accounts owned by the bootstrap customer:
alice=$(curl -fsS -X POST localhost:8080/api/v1/accounts \
-u operator:operator-local \
-H 'Content-Type: application/json' \
-d '{"ownerName":"Alice","ownerUsername":"customer","currency":"INR"}' | jq -r .id)
bob=$(curl -fsS -X POST localhost:8080/api/v1/accounts \
-u operator:operator-local \
-H 'Content-Type: application/json' \
-d '{"ownerName":"Bob","ownerUsername":"customer","currency":"INR"}' | jq -r .id)Fund Alice with INR 1,000.00:
curl -fsS -X POST "localhost:8080/api/v1/accounts/$alice/fund" \
-u operator:operator-local \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: fund-alice-001' \
-d '{"amountMinor":100000,"currency":"INR","reference":"initial funding"}'Transfer INR 250.00 from Alice to Bob:
curl -fsS -X POST localhost:8080/api/v1/transfers \
-u customer:customer-local \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: transfer-alice-bob-001' \
-d "{\"sourceAccountId\":\"$alice\",\"destinationAccountId\":\"$bob\",\"amountMinor\":25000,\"currency\":\"INR\",\"reference\":\"account transfer\"}"Read both balances and verify the projections against the ledger:
curl -fsS -u customer:customer-local \
"localhost:8080/api/v1/accounts/$alice/balance"
curl -fsS -u customer:customer-local \
"localhost:8080/api/v1/accounts/$bob/balance"
curl -fsS -u auditor:auditor-local \
localhost:8080/api/v1/reconciliationSending the same transfer again with the same key and payload returns the original transaction. Changing the payload while reusing the key returns HTTP 409.
| Area | Operations |
|---|---|
| Accounts | Create, read balance, freeze, unfreeze and close |
| Ledger | Fund, withdraw, transfer, reverse and read history |
| Reconciliation | Compare projections, inspect run reports and rebuild projections |
| Administration | Create users, change roles and read audit events |
| Operations | Health, application info and Micrometer metrics |
See the REST API guide for request fields, access rules and error codes.
Run the standard verification suite:
mvn verifyThe suite starts PostgreSQL 16 through Testcontainers and covers the application through real SQL and HTTP paths. The two larger benchmarks run separately:
mvn -Dtest=SqlBenchmark test
mvn -Dtest=HttpLoadBenchmark testThe SQL harness builds a 100,000-transaction fixture and captures EXPLAIN ANALYZE plans. Its
reference optimization run reduced median hot-account history latency from 145.875 ms to 1.401 ms.
Benchmark method and context are documented in SQL benchmarking and
HTTP load testing.
Build the application and local image:
mvn verify
docker build -t aurum:local .The image runs the Java process as user 10001 and expects PostgreSQL connection settings through
environment variables. Pushing a version tag such as v1.0.0, or manually starting the publish
workflow, builds and publishes ghcr.io/<owner>/<repository> with provenance and an SBOM.
- Architecture and accounting model
- REST API
- Reliability and failure recovery
- Authentication and authorization
- Audit events
- Reconciliation
- Metrics and observability
- Testing strategy
- SQL benchmarking
- HTTP load testing
- Operations and delivery
The documentation index provides a guided reading order.
Generated classes, reports and JAR files are written under the ignored target/ directory. Remove
them with mvn clean. Stop the local database with docker compose down; add --volumes only when
you also want to delete the local Aurum database.