Skip to content

About

Event-driven Spring Boot microservices with Kafka and Kubernetes-ready manifests.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Kafka Spring Microservices — Double-Entry Transfer Ledger

Author: Md Tanvir Alam (tanvir-ux)
Stack: Spring Boot 3.3 · Java 17 · Apache Kafka · PostgreSQL · Docker Compose · Kubernetes
License: MIT

Original demo of three cooperating Spring Boot services that move money through Kafka events and persist a double-entry ledger. Built to show production-shaped patterns: idempotent consumers, JWT-secured REST, transactional outbox-style publish-after-commit, Kubernetes-ready manifests.


Architecture

flowchart LR
  Client["Client / curl"] -->|JWT REST| TA["transfer-api"]
  TA -->|publish transfer.requested| K["Apache Kafka"]
  K -->|consume| LS["ledger-service"]
  LS -->|JPA double-entry| PG[("PostgreSQL")]
  LS -->|publish transfer.posted / transfer.failed| K
  K -->|consume| TA
  K -->|consume| NS["notification-service"]
  NS --> PG
  TA --> PG
Loading
sequenceDiagram
  autonumber
  participant C as Client
  participant API as transfer-api
  participant K as Kafka
  participant L as ledger-service
  participant N as notification-service

  C->>API: POST /api/v1/auth/token
  API-->>C: JWT
  C->>API: POST /api/v1/transfers (Bearer)
  API->>API: persist Transfer (PENDING)
  API->>K: transfer.requested
  API-->>C: 202 Accepted {transferId}

  K->>L: transfer.requested
  alt sufficient funds + valid accounts
    L->>L: debit source, credit destination (one TX)
    L->>K: transfer.posted
  else insufficient funds / unknown account / duplicate
    L->>K: transfer.failed
  end

  K->>API: posted | failed
  API->>API: update Transfer status
  K->>N: posted | failed
  N->>N: store Notification
Loading

Topics

Topic Producer Consumers Payload
transfer.requested transfer-api ledger-service TransferRequestedEvent
transfer.posted ledger-service transfer-api, notification-service TransferPostedEvent
transfer.failed ledger-service transfer-api, notification-service TransferFailedEvent

Double-entry rule

Every successful transfer writes two ledger_entry rows in one database transaction:

  • DEBIT fromAccount (asset outflow)
  • CREDIT toAccount (asset inflow)

Balances are updated in the same transaction. Replays are no-ops because transfer_id is unique on the ledger.


Modules

Module Port Responsibility
bank-events — Shared Jackson records for Kafka payloads
transfer-api 8080 JWT auth, initiate transfers, track status
ledger-service 8081 Post journal entries, publish posted/failed
notification-service 8082 Persist and list user notifications

Local run (Docker Compose)

Requires Docker with Compose v2.

docker compose up --build

Wait until Kafka is healthy, then:

# 1. Get a token (demo users: alice/password  bob/password)
TOKEN=$(curl -s -X POST http://localhost:8080/api/v1/auth/token \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice","password":"password"}' | jq -r .token)

# 2. Transfer 25.00 from Alice (ACC-1001) to Bob (ACC-2001)
curl -s -X POST http://localhost:8080/api/v1/transfers \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: demo-001' \
  -d '{"fromAccountId":"ACC-1001","toAccountId":"ACC-2001","amount":25.00,"currency":"USD"}'

# 3. Poll status
curl -s -H "Authorization: Bearer $TOKEN" \
  http://localhost:8080/api/v1/transfers/<transferId>

# 4. Notifications for Bob
curl -s http://localhost:8082/api/v1/notifications?accountId=ACC-2001

Seeded accounts (see ledger-service DataSeeder):

Account Owner Opening balance
ACC-1001 Alice 1,000.00 USD
ACC-2001 Bob 250.00 USD
ACC-3001 Treasury 10,000.00 USD

Run tests (no Docker required)

export JAVA_HOME=/path/to/jdk-17
mvn -q test

Tests use H2 in-memory databases and mocked KafkaTemplate / listener unit tests. They do not start a Kafka broker.


Kubernetes

Manifests live in k8s/. They assume an in-cluster Kafka bootstrap of kafka:9092 and Postgres at postgres:5432 (both included as demo Deployments — not HA).

kubectl apply -k k8s/
kubectl -n bank-demo rollout status deploy/transfer-api

Production clusters should replace the bundled Kafka/Postgres with a managed broker (MSK, Confluent, Strimzi) and a managed database. The service Deployments only need SPRING_KAFKA_BOOTSTRAP_SERVERS and SPRING_DATASOURCE_URL.


Security notes

  • HMAC JWT (HS256), 30-minute TTL, secret injected via env JWT_SECRET.
  • Demo users are in-memory (alice / bob). Swap for JDBC/OAuth2 in a real environment.
  • Actuator is exposed on /actuator/health only.

Project layout

See the repository tree. Each service has its own Dockerfile (multi-stage, Temurin 17).

About

Event-driven Spring Boot microservices with Kafka and Kubernetes-ready manifests.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages