Architecture
How the four services fit together: from a client order, through risk and consensus, to a matched trade streamed back out, and what happens when a server dies mid-trade.
Components
Open Exchange is four coordinated services:
- match: the matching engine. A 3-node Aeron cluster (Raft consensus) running the order book, the fast-path matcher, and SBE egress. repo →
- oms (order management): sits between clients and the cluster. Auth, pre-trade risk, order-lifecycle tracking, synthetic order types, and PostgreSQL read models. repo →
- assets (the Assets Engine): the money ledger as its own 3-node Aeron cluster. Every hold, settle, and release is a replicated state-machine transition, recorded in a replayable per-node money journal. repo →
- admin-gateway: a Go service that manages process lifecycle, rolling updates, snapshots, backups, and cluster health. repo →
- trading-ui: a React reference UI with live order book, trades, charts, and order entry. repo →
System diagram
Order flow
A new order travels a fixed path:
- A client submits over REST (
:8080) or gRPC (:9090) to the OMS, with a bearer token. - The OMS authenticates and runs the 7-check risk pipeline; the Assets Engine places the money hold.
- The accepted order is submitted to the Aeron cluster ingress and replicated via Raft.
- Every node's engine consumes the same replicated log deterministically: no wall clock, no randomness, byte-identical state on all three. That determinism is what makes failover safe, and a corpus test guards it on every commit.
- Trades and order-status updates are emitted as SBE egress.
- The market gateway streams book depth, trades, and status to everyone over WebSocket (
:8081); the OMS settles fills against the Assets Engine, wherefilledQty == SUM(executions)is an invariant the failover test asserts.
Access tiers
What the diagram draws solid is the B2C path that ships today: the OMS in front (auth, risk, lifecycle) and the market gateway publishing once to the edge. The dashed slots are the point of the layering. The raw Aeron ingress that our load generator drives at ~0.3 µs p50 is the same door a sponsored B2B/HFT client would use once an admission layer is added, and FIX plus richer gRPC connectors are on the public roadmap. Adding an access tier means adding a gateway process at the boundary — the engine, the money path, and the consensus core don't change.
When a server dies
Kill the leader mid-load (CI does, on every commit) and the remaining two nodes elect a new leader in seconds and keep matching; the OMS detects the gap, resnapshots its open orders from the cluster, and reconciles anything in flight. Beyond replication there are three more layers of durability: periodic snapshots plus the Aeron archive for fast recovery, a continuous backup agent with heartbeat-monitored freshness, and an OMS that rebuilds its state from PostgreSQL on startup. The combination has survived a literal power-loss drill with effectively zero data loss.
The order book
The engine uses an array-backed order book: price levels are an AA tree stored in flat primitive arrays, and orders are a pooled, doubly-linked FIFO per level. There are no object references and no allocation on the hot path, so the matching thread pays no GC load barriers. Memory scales with resting orders rather than the configured price range. The full write-up is in the deep-dive.
Tech stack & compatibility
| Layer | Technology |
|---|---|
| Matching engine | Java 21, Aeron Cluster 1.52, Agrona 2.5 |
| Serialization | Simple Binary Encoding (SBE) 1.39 — trading schema v7, settlement journal (id 3), money schema v2 |
| Event processing | LMAX Disruptor 4.0 |
| OMS | Java 21, gRPC 1.82, Protobuf 4.35, PostgreSQL 14+ (tested on 16), Redis 7 |
| Assets Engine | Java 21, Aeron Cluster 1.52 (its own Raft group) |
| Network I/O | Netty 4.2 |
| Admin gateway | Go 1.23, chi router |
| Reference UI | React 19, TypeScript, Vite, lightweight-charts |
| Build | Maven, Make, npm (Node 22) |
One stack version spans all five repos (e.g. v0.4.0-beta everywhere); mixing repo versions is unsupported. The full umbrella document, including the exact wire-compatibility matrix, is docs/ARCHITECTURE.md in the match repo.