In modern trading platforms, the Order API isn’t just another microservice—it’s the central nervous system that connects user intent with market execution. When a trader submits a buy or sell order, this component validates every detail, routes the request to the exchange, and broadcasts the outcome across the entire system.
In our ongoing series about building a brokerage platform for Brazil’s B3 exchange, we’ve developed individual components like price synchronization, the matching engine, financial custody, and asset catalogs. Now, we’re focusing on the critical service that ties everything together: the Broker Order API.
What Does the Order API Do?
The trading-broker-order service serves as the single entry point for all trading activity. Its primary responsibility is to manage the complete lifecycle of an order—from initial submission to final execution confirmation. This includes validating user eligibility, checking asset availability, routing orders to the B3 matching engine, and notifying all subsystems of status changes.
To accomplish this, the service integrates with four key components:
- Asset API (REST/Feign) – Confirms the ticker exists and is tradable
- Wallet API (REST/Feign) – Verifies the user has sufficient balance
- B3 Matching Engine (RabbitMQ) – Forwards orders for execution
- Broker Wallet (Kafka) – Publishes lifecycle events to the ecosystem
End-to-End Order Flow Explained
Here’s how a typical order travels through the system:
- A user submits a POST request to
/api/v1/orderswith details like user ID, ticker, quantity, and side (BUY/SELL) - The Order API:
- Validates the ticker via Asset API
- Checks wallet balance via Wallet API
- Persists the order in MySQL with status
PENDING - Publishes the order to RabbitMQ for B3 execution
- Notifies the system via Kafka event
order-events-v1
- B3’s matching engine processes the order and returns a status (
FILLEDorREJECTED) via RabbitMQ - The Order API:
- Updates the order status in MySQL
- Publishes the final Kafka event
- The Wallet service reacts by blocking, liquidating, or reversing funds
This design uses two distinct messaging patterns: synchronous REST calls for validation and asynchronous RabbitMQ/Kafka for event propagation.
Technology Stack Behind the Implementation
The Order API leverages modern Java and Spring technologies:
- Java 21 + Spring Boot 3.3.5 – Core runtime and framework
- MySQL + Flyway – Persistent storage with schema migration
- Spring Kafka – Lifecycle event publishing
- Spring AMQP – RabbitMQ integration for order routing
- Spring Cloud OpenFeign – REST client for Asset and Wallet APIs
- SpringDoc OpenAPI – Interactive Swagger documentation
Core Implementation Principles
1. Domain Model: The Order Entity
The Order class models the state of a trading order at any point in its lifecycle:
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userId;
private String ticker;
@Column(nullable = false, precision = 19, scale = 4)
private BigDecimal quantity;
@Column(nullable = false, precision = 19, scale = 4)
private BigDecimal price;
@Column(precision = 19, scale = 4)
private BigDecimal executedPrice; // Null until B3 responds
@Enumerated(EnumType.STRING)
private OrderSide side; // BUY | SELL
@Enumerated(EnumType.STRING)
private OrderStatus status; // PENDING | FILLED | REJECTED | CANCELLED
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
public OrderEventDTO mapToEvent() {
return OrderEventDTO.builder()
.orderId(this.getId())
.userId(this.getUserId())
.ticker(this.getTicker())
.quantity(this.getQuantity())
.price(this.getPrice())
.executedPrice(this.getExecutedPrice())
.side(this.getSide().name())
.status(this.getStatus().name())
.eventTimestamp(LocalDateTime.now())
.build();
}
}A key design choice is making executedPrice nullable. Since the actual execution price comes from B3 only after order submission, enforcing non-null at creation would break domain integrity.
2. Ticker Validation: Asset Feign Client
Invalid tickers must be rejected early to prevent wasted processing. The AssetClient uses Feign to call the Asset API:
@FeignClient(name = "trading-broker-asset", url = "${app.services.asset-url}")
public interface AssetClient {
@GetMapping("/api/v1/assets/{ticker}")
AssetDTO getByTicker(@PathVariable("ticker") String ticker);
}Validation logic in OrderService:
- Throws
RuntimeExceptionif ticker is inactive or not found - Differentiates between business errors (
FeignException.NotFound) and technical failures (timeouts, 5xx) - Returns user-friendly messages for each failure type
This ensures traders receive immediate feedback on invalid inputs rather than silent rejections later in the flow.
3. Balance Validation: Wallet Feign Client
For buy orders, the system must confirm the user has enough funds. The WalletClient is similarly structured:
@FeignClient(name = "trading-broker-wallet", url = "${app.services.wallet-url}")
public interface WalletClient {
@GetMapping("/api/v1/wallets/summary")
WalletSummaryDTO getWalletSummary(@RequestParam("userId") String userId);
}The validation logic compares requested quantity against available balance, blocking the amount immediately when the order is created.
4. Event-Driven Communication Patterns
The system uses two messaging paradigms:
- RabbitMQ for real-time order routing to B3’s matching engine
- Kafka for durable, replayable order lifecycle events
Each state change triggers a Kafka event, allowing downstream services like the wallet to react appropriately—whether by freezing funds, completing settlements, or reversing failed trades.
Lessons from Production-Grade Trading Systems
Building an Order API isn’t just about connecting services—it’s about designing for failure, consistency, and transparency. Every component must gracefully handle network timeouts, invalid inputs, and exchange rejections while maintaining a clear audit trail.
As trading platforms evolve toward real-time settlement and fractional trading, APIs like this will need to support atomic operations across multiple ledgers and regulatory jurisdictions. The architecture we’ve outlined provides a solid foundation for such scalability.
The next step? Integrating risk management rules, compliance checks, and multi-exchange routing—each adding another layer of validation and coordination to this already complex system.
As always, the devil is in the details—and in trading systems, those details are measured in milliseconds and cents.
AI summary
Erfahren Sie, wie Sie eine robuste Order-API für Handelsplattformen mit Java, Spring Boot und asynchroner Kommunikation entwickeln. Optimieren Sie Order-Lebenszyklen und steigern Sie die Effizienz.
Tags