A deterministic, event-driven matching engine built with Java 17, Spring Boot, Apache Kafka, MySQL, and LMAX Disruptor.
The project demonstrates how a trading order book can process commands through a single-writer state machine, publish settlement events, and recover safely from database snapshots and Kafka replay.
- Limit orders, market orders, and cancellations
- Price-time priority using in-memory order books
- Single-writer processing without locks in the matching path
- LMAX Disruptor ring buffer between Kafka ingestion and matching
BigDecimalfor financial calculations and serialization- Partitioned settlement events for downstream processing
- Snapshot persistence with safe Kafka offset recovery
- Duplicate-input and duplicate-output protection
- Docker Compose environment for MySQL and Kafka
flowchart LR
O["Kafka: offer.eth_btc"] --> C["OfferConsumer"]
C --> D["LMAX Disruptor"]
D --> H["Single Match Event Handler"]
H --> E["Matching Engine"]
E --> B["In-memory Order Book"]
E --> S["Kafka: settle.0-63"]
E --> Q["Kafka: quote_deals.eth_btc"]
H --> P["Consistent Snapshot Copy"]
P --> M["MySQL Snapshot Storage"]
The matching state is owned by one event-handler thread. Kafka callbacks only publish commands to the ring buffer; they never mutate the order book directly.
The engine stores the last safely processed Kafka offset in each snapshot. On restart it restores the order book and seeks to the next offset. Duplicate commands and already-published output IDs are ignored.
offer.eth_btc intentionally uses exactly one Kafka partition. A single ordered command stream is required because the current engine maintains one global market state and one input offset. The consumer fails fast if this invariant is violated.
Snapshot creation is initiated by a sentinel event in the same ring buffer. This ensures all earlier commands have completed before the in-memory state is copied. Database I/O then runs on a separate worker so it does not block matching.
See ARCH_OVERVIEW.md for detailed sequence diagrams.
- Java 17
- Spring Boot 3.2
- Spring Kafka
- LMAX Disruptor 4
- Spring Data JPA and JDBC
- MySQL 8
- JUnit 5 and Mockito
- Docker Compose
Requirements:
- JDK 17+
- Maven 3.9+
- Docker with Docker Compose
Start MySQL, Kafka, and the required topics:
cd docker
docker compose up -dThen start the engine from the project root:
mvn spring-boot:runThe default local configuration uses:
| Setting | Default | Environment variable |
|---|---|---|
| MySQL URL | jdbc:mysql://localhost:3306/matchengine |
DB_URL |
| MySQL user | user |
DB_USERNAME |
| MySQL password | password |
DB_PASSWORD |
| Kafka brokers | localhost:9092 |
KAFKA_BOOTSTRAP_SERVERS |
| HTTP port | 8080 |
SERVER_PORT |
Defaults are intended only for the local Docker environment. Use secrets or environment variables outside local development.
Publish a limit-order command to offer.eth_btc:
{
"id": 1001,
"method": "order.put_limit",
"params": {
"user_id": 42,
"side": 1,
"amount": "1.5000",
"price": "2500.0000",
"taker_fee_rate": "0.0010",
"maker_fee_rate": "0.0010"
}
}The order book can be inspected at:
GET http://localhost:8080/api/market/orderbook
Run the unit test suite with:
mvn testThe tests cover price priority, time priority, partial fills, market-buy quote consumption, cancellations, minimum order size, per-user open-order limits, safe snapshot metadata recovery, and the single-partition input invariant.
The project includes JMH benchmarks for the isolated in-memory matching core:
mvn -Pbenchmark clean compile exec:execLatest local measurement:
| Operation | Average time |
|---|---|
| Insert a passive limit order | 119.170 ns/op |
| Match one limit order immediately | 231.439 ns/op |
These are microbenchmark results, not end-to-end system latency. Kafka, JSON parsing, database snapshots, logging, networking, and settlement delivery are intentionally excluded. See BENCHMARKS.md for the environment, methodology, uncertainty, and reproduction details.
This repository is a focused reference implementation for a single market. A production deployment would additionally require authentication, authorization, schema validation, metrics, tracing, dead-letter handling, multi-market orchestration, integration tests, and end-to-end load and latency testing.
This project is available under the MIT License.