Skip to content

yangboxin/mini-order-platform

Repository files navigation

Mini Order Platform

An Industrial-Grade Spring Boot Microservices System (Transactional Outbox, Kafka, Idempotency)


Overview

This project is a miniature yet industrial-grade microservices platform built with Spring Boot, PostgreSQL, and Kafka, designed to demonstrate real-world backend engineering concerns rather than toy CRUD examples.

It focuses on data consistency, event-driven architecture, fault tolerance, and operational robustness, following patterns commonly used in production systems.

This project is intentionally opinionated and emphasizes correctness under failure, eventual consistency, and observability.


High-Level Architecture

Core Services

  • order-service — handles order creation and persistence
  • payment-service — consumes order events and processes payments
  • common-lib — shared event contracts and primitives

Infrastructure

  • PostgreSQL (transactional storage + outbox)
  • Kafka (asynchronous messaging backbone)
  • Docker Compose (local orchestration)

Logical Flow

Client
  |
  v
Order Service
  |  (DB transaction)
  |-- Orders table
  |-- Outbox table
  |
  |  (async)
  v
Kafka (order.events)
  |
  v
Payment Service
  |-- Idempotent consumer
  |-- Processed-events table

Why this architecture?

  • Avoids dual-write problems
  • Ensures at-least-once delivery
  • Maintains business correctness under retries and crashes

Key Design Decisions

1. Transactional Outbox Pattern

Order creation and event emission are decoupled but atomically consistent.

Instead of publishing Kafka messages directly inside business logic:

  • The service writes:
    • Order record
    • Outbox event record
      in the same database transaction
  • A background publisher later delivers events to Kafka

This guarantees:

  • No event is lost if Kafka is temporarily unavailable
  • No event is published without its corresponding database state

Outbox lifecycle:

PENDING -> PROCESSING -> SENT
        \-> retry with backoff ->/

2. Multi-Instance Safe Outbox Publisher

The outbox publisher is designed to be safe under horizontal scaling.

Key mechanisms:

  • Database-level locking using FOR UPDATE SKIP LOCKED
  • Explicit PROCESSING state to claim events
  • Short-lived transactions to avoid blocking

Failure handling:

  • Exponential backoff using available_at
  • Retry counters and last error tracking
  • Clean, observable state transitions

3. Event-Driven Communication via Kafka

All cross-service communication is asynchronous and event-based.

  • Order Service publishes OrderCreatedEvent
  • Payment Service reacts independently
  • Services are loosely coupled
  • Failures in one service do not cascade

Events are explicitly versioned and self-describing:

  • eventId
  • eventType
  • eventVersion
  • occurredAt
  • payload

4. Idempotent Kafka Consumer (Payment Service)

Kafka provides at-least-once delivery, so consumers must be idempotent.

Idempotency is implemented via:

  • A processed_events table keyed by event_id
  • Atomic “try-start” semantics
  • First consumer wins, duplicates are ignored

This ensures:

  • No double payment
  • No duplicate side effects
  • Safe replays and offset resets

5. Failure as a First-Class Concern

The system explicitly handles:

  • Kafka downtime
  • Consumer restarts
  • Duplicate message delivery
  • Database transaction rollbacks

Failures are:

  • Logged with context
  • Retried with backoff
  • Observable via database state

Technology Stack

  • Java 17
  • Spring Boot
  • Spring Data JPA
  • Apache Kafka
  • PostgreSQL
  • Flyway
  • Jackson
  • Maven
  • Docker Compose

Project Structure

mini-order-platform/
├── common-lib/
│   └── shared event contracts
│
├── order-service/
│   ├── api/           REST controllers
│   ├── domain/        Domain models
│   ├── persistence/  JPA entities and repositories
│   ├── outbox/        Transactional outbox
│   └── messaging/    Kafka producer
│
├── payment-service/
│   ├── messaging/    Kafka consumer
│   ├── idempotency/  Deduplication logic
│   └── persistence/
│
└── docker-compose.yml

Local Development

Start infrastructure:

docker-compose up -d

Run services:

mvn -pl order-service spring-boot:run
mvn -pl payment-service spring-boot:run

Observability and Operations

  • Health checks via Spring Actuator
  • Explicit logging around:
    • Event publication
    • Consumer processing
    • Duplicate detection
  • Database tables act as operational truth:
    • Outbox state
    • Processed events
    • Retry attempts

What This Project Intentionally Does Not Include

  • Distributed transactions (XA)
  • Synchronous inter-service REST calls
  • Exactly-once guarantees
  • In-memory shortcuts for reliability

All correctness is based on explicit, observable state.


Possible Extensions

  • Dead-letter topics
  • Schema registry
  • OpenTelemetry tracing
  • Kubernetes deployment
  • OAuth2 / service-to-service authentication

Architecture Diagram (Textual)

Client -> Order Service -> Database (Orders + Outbox)
                  |
                  v
               Kafka
                  |
                  v
            Payment Service -> Processed Events

Why This Project Exists

This project demonstrates:

  • How real systems behave under failure
  • How Spring Boot is used beyond CRUD
  • How to design for eventual consistency
  • How to reason about distributed systems trade-offs

The goal is not feature count, but engineering correctness.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages