Order Service Java ππ±π
A microservice for managing orders in the Olara Tech platform.
π Overview
Order Service is a Spring Boot microservice responsible for handling order creation, management, and retrieval. It integrates with RabbitMQ for event-driven communication and uses PostgreSQL for data persistence. The service is designed for scalability, resilience, and easy integration within a microservices ecosystem.
π¦ Features
| Feature | Description |
|---|---|
| π Order Management | Create, update, retrieve, and delete orders |
| π’ Event Publishing | Publishes order events to RabbitMQ |
| β»οΈ Resilience | Circuit breakers and retries (Resilience4j) |
| π Service Discovery | Eureka integration for service registration |
| π Health Monitoring | Exposes health and metrics endpoints (Actuator) |
| ποΈ Database Migration | Flyway for schema versioning |
ποΈ Architecture
sequenceDiagram
participant User
participant App
participant Backend
participant Fineract(Wallet)
participant Broker(Alpaca/DriveWealth)
participant Market_Data(Polygon/IEX)
participant Tax_Engine(TaxBit)
%% ================== ORDER PLACEMENT ==================
Note over User,App: 1. Order Placement Flow
User->>App: Selects order type (Market/Limit/Stop)
App->>Market_Data: Fetch real-time price
Market_Data-->>App: Return bid/ask
User->>App: Enter details (Qty, Price, TIF)
App->>Backend: Submit order
%% ================== PRE-CHECKS ==================
Backend->>Fineract: Verify funds/shares
Backend->>Broker: Check compliance:
Broker->>Broker: Validate PDT, margin, restrictions
alt Approved
Broker-->>Backend: "Approved"
Backend->>Broker: Route to exchange
else Rejected
Broker-->>Backend: "Rejected: Reason"
Backend->>User: Show rejection notice
end
%% ================== ORDER CANCELLATION ==================
Note over User,App: 2. Cancel/Replace Flow
User->>App: Requests "Cancel Order"
App->>Backend: Cancel request
Backend->>Broker: Check order status
alt Not Filled
Broker->>Market_Data: Cancel order
Market_Data-->>Broker: Confirmation
Broker->>Fineract: Unlock funds
Fineract->>User: Update wallet
else Partially Filled
Broker-->>Backend: "Partial fill - Cancel remainder"
end
%% ================== ORDER REPLACEMENT ==================
User->>App: Requests "Modify Order"
App->>Backend: New order params
Backend->>Broker: Cancel-and-replace
Broker->>Market_Data: Process replacement
alt Success
Market_Data-->>Broker: New order ID
Broker->>User: Confirmation
else Failure
Broker->>Broker: Revert to original order
end
%% ================== POST-TRADE PROCESSING ==================
Note over Broker,Tax_Engine: 3. Post-Trade Flow (T+2)
Broker->>Fineract: Settlement file
Fineract->>Broker: Confirm positions
Broker->>Tax_Engine: Trade details
Tax_Engine->>Tax_Engine: Calculate wash sales, cost basis
Tax_Engine->>User: Annual 1099-B report
Hold "Alt" / "Option" to enable pan & zoom
- Controller: Handles HTTP requests and responses
- Service: Business logic for order processing
- Repository: JPA interface to PostgreSQL
- Event Publisher: Publishes events to RabbitMQ
π οΈ Technologies Used
- β Java 17+
- π± Spring Boot 3.x
- π PostgreSQL
- π RabbitMQ
- π‘οΈ Resilience4j
- π§ Eureka
- π Lombok
- ποΈ Flyway
βοΈ Setup Instructions
Prerequisites
- Java 17+
- Maven or Gradle
- PostgreSQL
- RabbitMQ
Quick Start
- Clone the repository
git clone https://github.com/olara-tech/order-service-java.git cd order-service-java - Configure the database in
src/main/resources/application.yml:spring: datasource: url: jdbc:postgresql://localhost:5432/order-service-db username: postgres password: your_password - Start RabbitMQ (Docker):
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management - Run the application:
./gradlew bootRun - Access the application:
- Swagger UI: http://localhost:10001/swagger-ui.html
- Actuator: http://localhost:10001/actuator
π Configuration Example
spring:
application:
name: order-service
datasource:
url: jdbc:postgresql://localhost:5432/order-service-db
username: postgres
password: your_password
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
management:
endpoints:
web:
exposure:
include: health,info
π API Endpoints & Examples
Orders
1. Get Order by Tenant ID, User ID, and Order ID
Request:
GET /orders/{orderId}/users/{userId}
Headers:
tenantId: <tenant-uuid>
Response:
{
"requestId": "string",
"timestamp": "2024-05-10T12:34:56.789Z",
"success": true,
"message": "Order fetched successfully.",
"data": {
"id": "b1e2c3d4-5678-1234-9abc-1234567890ab",
"symbol": "AAPL",
"type": "MARKET",
"side": "BUY",
"quantity": 10.0,
"limitPrice": null,
"stopPrice": null,
"timeInForce": "GTC",
"status": "PENDING",
"rejectionReason": null,
"brokerOrderId": "BRK-123456",
"createdAt": "2024-05-10T12:34:56.789Z",
"updatedAt": "2024-05-10T12:34:56.789Z",
"executedAt": null,
"executedPrice": null,
"executedQuantity": null,
"commission": null,
"isTaxOptimized": false
},
"metadata": null
}
2. Get All Orders (Paginated)
Request:
GET /orders?page=0&size=10&sort=createdAt,desc
Response:
{
"requestId": "string",
"timestamp": "2024-05-10T12:34:56.789Z",
"success": true,
"message": "Orders fetched successfully.",
"data": [
{
"id": "b1e2c3d4-5678-1234-9abc-1234567890ab",
"symbol": "AAPL",
"type": "MARKET",
"side": "BUY",
"quantity": 10.0,
"status": "PENDING",
"createdAt": "2024-05-10T12:34:56.789Z"
}
// ...more orders
],
"page": 0,
"size": 10,
"totalElements": 1,
"totalPages": 1,
"metadata": null
}
3. Get All Orders by Tenant ID and User ID
Request:
GET /orders/{userId}?page=0&size=10&sort=createdAt,desc
Headers:
tenantId: <tenant-uuid>
Response:
{
"requestId": "string",
"timestamp": "2024-05-10T12:34:56.789Z",
"success": true,
"message": "Orders fetched successfully.",
"data": [
{
"id": "b1e2c3d4-5678-1234-9abc-1234567890ab",
"symbol": "AAPL",
"type": "MARKET",
"side": "BUY",
"quantity": 10.0,
"status": "PENDING",
"createdAt": "2024-05-10T12:34:56.789Z"
}
// ...more orders
],
"page": 0,
"size": 10,
"totalElements": 1,
"totalPages": 1,
"metadata": null
}
4. Create a New Order
Request:
POST /orders
Content-Type: application/json
{
"data": {
"symbol": "AAPL",
"type": "MARKET",
"side": "BUY",
"quantity": 10.0,
"limitPrice": null,
"stopPrice": null,
"timeInForce": "GTC",
"isTaxOptimized": false,
"routingInstructions": ""
}
}
Response:
{
"requestId": "string",
"timestamp": "2024-05-10T12:34:56.789Z",
"success": true,
"message": "Order created successfully.",
"data": {
"id": "b1e2c3d4-5678-1234-9abc-1234567890ab",
"symbol": "AAPL",
"type": "MARKET",
"side": "BUY",
"quantity": 10.0,
"status": "PENDING",
"createdAt": "2024-05-10T12:34:56.789Z"
},
"metadata": null
}
Products
1. Get Product by ID
Request:
GET /products/{id}
Response:
{
"requestId": "string",
"timestamp": "2025-05-10T12:34:56.789Z",
"success": true,
"message": "Product fetched successfully.",
"data": {
"id": "c2f3e4d5-6789-2345-abcd-2345678901bc",
"name": "Apple iPhone 15",
"description": "Latest Apple smartphone.",
"imageUrl": "https://example.com/images/iphone15.jpg",
"category": "Electronics",
"status": "ACTIVE",
"created": "2025-05-10T12:34:56.789Z",
"updated": "2025-05-10T12:34:56.789Z",
"createdBy": "admin",
"updatedBy": "admin"
},
"metadata": null
}
2. Get All Products (Paginated)
Request:
GET /products?page=0&size=10&sort=created,desc
Response:
{
"requestId": "string",
"timestamp": "2025-05-10T12:34:56.789Z",
"success": true,
"message": "Products fetched successfully.",
"data": [
{
"id": "c2f3e4d5-6789-2345-abcd-2345678901bc",
"name": "Apple iPhone 15",
"description": "Latest Apple smartphone.",
"imageUrl": "https://example.com/images/iphone15.jpg",
"category": "Electronics",
"status": "ACTIVE",
"created": "2025-05-10T12:34:56.789Z"
}
// ...more products
],
"page": 0,
"size": 10,
"totalElements": 1,
"totalPages": 1,
"metadata": null
}
3. Create a New Product
Request:
POST /products
Content-Type: application/json
{
"data": {
"name": "Apple iPhone 15",
"description": "Latest Apple smartphone.",
"imageUrl": "https://example.com/images/iphone15.jpg",
"category": "Electronics",
"status": "ACTIVE"
}
}
Response:
{
"requestId": "string",
"timestamp": "2025-05-10T12:34:56.789Z",
"success": true,
"message": "Product created successfully.",
"data": {
"id": "c2f3e4d5-6789-2345-abcd-2345678901bc",
"name": "Apple iPhone 15",
"description": "Latest Apple smartphone.",
"imageUrl": "https://example.com/images/iphone15.jpg",
"category": "Electronics",
"status": "ACTIVE",
"created": "2025-05-10T12:34:56.789Z"
},
"metadata": null
}
4. Update a Product
Request:
PUT /products/{id}
Content-Type: application/json
{
"data": {
"name": "Apple iPhone 15 Pro",
"description": "Updated description.",
"imageUrl": "https://example.com/images/iphone15pro.jpg",
"category": "Electronics",
"status": "ACTIVE"
}
}
Response:
{
"requestId": "string",
"timestamp": "2025-05-10T13:00:00.000Z",
"success": true,
"message": "Product updated successfully.",
"data": {
"id": "c2f3e4d5-6789-2345-abcd-2345678901bc",
"name": "Apple iPhone 15 Pro",
"description": "Updated description.",
"imageUrl": "https://example.com/images/iphone15pro.jpg",
"category": "Electronics",
"status": "ACTIVE",
"created": "2025-05-10T12:34:56.789Z",
"updated": "2025-05-10T13:00:00.000Z"
},
"metadata": null
}
5. Delete a Product
Request:
DELETE /products/{id}
Response:
{
"requestId": "string",
"timestamp": "2025-05-10T13:10:00.000Z",
"success": true,
"message": "Product deleted successfully.",
"data": null,
"metadata": null
}
π Data Model Reference
Orders Table
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key, unique order identifier |
| user_id | UUID | ID of the user placing the order |
| tenant_id | UUID | ID of the tenant (organization/account) |
| account_id | VARCHAR(255) | Account identifier |
| symbol | VARCHAR(255) | Trading symbol (e.g., AAPL) |
| type | VARCHAR(50) | Order type (MARKET, LIMIT, etc.) |
| side | VARCHAR(50) | Order side (BUY, SELL) |
| quantity | NUMERIC(38, 10) | Quantity of the asset |
| limit_price | NUMERIC(38, 10) | Limit price (if applicable) |
| stop_price | NUMERIC(38, 10) | Stop price (if applicable) |
| time_in_force | VARCHAR(50) | Time in force (e.g., GTC, DAY) |
| status | VARCHAR(50) | Order status (PENDING, COMPLETED, etc.) |
| rejection_reason | VARCHAR(255) | Reason for rejection (if any) |
| broker_order_id | VARCHAR(255) | Broker/exchange order ID |
| client_order_id | VARCHAR(255) | Client reference order ID |
| executed_at | TIMESTAMP | When the order was executed |
| executed_price | NUMERIC(38, 10) | Price at which the order was executed |
| executed_quantity | NUMERIC(38, 10) | Quantity executed |
| commission | NUMERIC(38, 10) | Commission charged |
| is_tax_optimized | BOOLEAN | Whether the order is tax optimized |
| replaced_by_order_id | UUID | ID of the order that replaced this one (if any) |
| replaces_order_id | UUID | ID of the original order this one replaces (if any) |
| routing_instructions | VARCHAR(255) | Special routing instructions |
| created | TIMESTAMP | When the order was created |
| updated | TIMESTAMP | When the order was last updated |
| created_by | VARCHAR(255) | User who created the order |
| updated_by | VARCHAR(255) | User who last updated the order |
Product Response Fields
| Field | Type | Description |
|---|---|---|
| id | UUID | Unique product identifier |
| name | String | Product name |
| description | String | Product description |
| imageUrl | String | URL to product image |
| category | String | Product category |
| status | String | Product status (e.g., ACTIVE, INACTIVE) |
| created | String | ISO timestamp when the product was created |
| updated | String | ISO timestamp when the product was updated |
| createdBy | String | User who created the product |
| updatedBy | String | User who last updated the product |
PageBaseResponse
| Field | Type | Description |
|---|---|---|
| requestId | String | Unique identifier for the request |
| timestamp | Instant | When the response was created |
| success | boolean | Indicates if the operation was successful |
| message | String | Additional information about the response |
| data | T | The actual data payload (list or object) |
| page | int | Current page number (0-based) |
| size | int | Number of items per page |
| totalElements | long | Total number of elements across all pages |
| totalPages | int | Total number of pages |
| metadata | ResponseMetadata | Additional metadata (if any) |
BaseResponse
| Field | Type | Description |
|---|---|---|
| requestId | String | Unique identifier for the request |
| timestamp | Instant | When the response was created |
| success | boolean | Indicates if the operation was successful |
| message | String | Additional information about the response |
| data | T | The actual data payload (object or list) |
| metadata | Object | Additional metadata (if any) |
π§© Project Structure
order-service-java/
βββ docker-compose-order-service-local.yml
βββ Dockerfile
βββ README.md
βββ src/
βββ main/
β βββ java/com/olara/
β β βββ controller/
β β βββ dto/
β β βββ entity/
β β βββ enums/
β β βββ event/
β β βββ mapper/
β β βββ repository/
β β βββ service/
β βββ resources/
β βββ application.yml
β βββ db.migration/
βββ test/
βββ java/com/olara/
π€ Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -am 'Add new feature') - Push to the branch (
git push origin feature/your-feature) - Create a new Pull Request
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π¬ Contact
For questions, reach out to Olara Tech.
Happy Coding!