Skip to main content

Overview

The Order Service is the entry point for customer purchases. It validates customer data, creates order records, and orchestrates the order fulfillment process by coordinating with Inventory and Tracking services.
Port: 8090 | Database: PostgreSQL (orderdb) | Eureka Name: msvc-order

Responsibilities

Order Creation

Accept and validate customer order requests with order items

Stock Coordination

Call Inventory Service via Feign to validate and reserve stock

Shipment Initialization

Trigger Tracking Service to create shipment records

Lifecycle Management

Manage order state transitions (PENDING → CONFIRMED → SHIPPED → DELIVERED/CANCELLED)

Why PostgreSQL?

PostgreSQL was chosen for the Order Service for specific technical reasons:
Orders are transactional in nature. PostgreSQL’s strong ACID guarantees ensure that:
  • Order and order items are created atomically
  • No partial orders exist in the database
  • Concurrent order creation is handled safely
  • Data integrity is maintained during failures
Order data has complex relational structures:
  • One-to-many relationship between Order and OrderItem
  • Foreign key constraints to maintain referential integrity
  • Join queries for order reporting and analytics
  • Support for complex queries across customers and orders
PostgreSQL is battle-tested for mission-critical transactional systems with excellent performance for read/write workloads.

Domain Model

Based on the technical specification, the Order Service implements the following data model:

Order Entity

// Expected domain model structure
public class Order {
    private Long id;                    // Primary key
    private String orderNumber;         // UUID for public tracking
    private Long customerId;            // Customer reference
    private LocalDateTime orderDate;    // Order creation timestamp
    private OrderStatus status;         // Order lifecycle state
    private BigDecimal totalPrice;      // Total order value
    private List<OrderItem> items;      // Order line items
}
Order States:
public enum OrderStatus {
    PENDING_CHECK,   // Initial state, awaiting stock validation
    CONFIRMED,       // Stock reserved, order confirmed
    SHIPPED,         // Order dispatched to customer
    DELIVERED,       // Successfully delivered
    CANCELLED        // Order cancelled (releases stock)
}

OrderItem Entity

public class OrderItem {
    private Long id;
    private Long productId;            // References Inventory Service product
    private Integer quantity;          // Quantity ordered
    private BigDecimal priceAtPurchase; // Price snapshot at order time
}
Price Snapshot: Order items store priceAtPurchase rather than referencing current product price. This ensures order history accuracy even when prices change.

Service Communication

Inter-Service Calls with OpenFeign

The Order Service orchestrates the order creation workflow using synchronous Feign calls:

Feign Client Example

package com.microservice.order.infrastructure.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * Feign client for Inventory Service communication.
 * Uses Eureka service discovery to locate inventory service.
 */
@FeignClient(name = "msvc-inventory", path = "/api/v1/inventory")
public interface InventoryClient {
    
    @PostMapping("/reserve")
    InventoryResponse reserveStock(
        @RequestBody InventoryRequest request);
    
    @PostMapping("/release")
    void releaseStock(@RequestBody InventoryRequest request);
}

/**
 * DTO for inventory stock requests.
 */
public class InventoryRequest {
    private List<StockItem> items;
    
    public static class StockItem {
        private Long productId;
        private Integer quantity;
    }
}

/**
 * DTO for inventory responses.
 */
public class InventoryResponse {
    private boolean success;
    private String message;
    private List<String> unavailableProducts;
}

Tracking Service Client

package com.microservice.order.infrastructure.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;

/**
 * Feign client for Tracking Service.
 */
@FeignClient(name = "msvc-tracking", path = "/api/v1/tracking")
public interface TrackingClient {
    
    @PostMapping("/shipment")
    TrackingResponse createShipment(
        @RequestBody TrackingRequest request);
}

public class TrackingRequest {
    private Long orderId;
    private String customerName;
    private String deliveryAddress;
    private String carrier;
}

public class TrackingResponse {
    private String trackingNumber;
    private String estimatedDelivery;
}

Expected API Endpoints

Base URL: http://localhost:8090/api/v1/orders
Creates a new order and coordinates with inventory and tracking services.Request Body:
{
  "customerId": 12345,
  "items": [
    {
      "productId": 1,
      "quantity": 2
    },
    {
      "productId": 5,
      "quantity": 1
    }
  ],
  "deliveryAddress": "123 Main St, City, State 12345",
  "carrier": "DHL"
}
Success Response (201 Created):
{
  "orderId": 789,
  "orderNumber": "ORD-550e8400-e29b-41d4-a716-446655440000",
  "customerId": 12345,
  "status": "CONFIRMED",
  "totalPrice": 349.97,
  "orderDate": "2026-03-08T14:30:00",
  "trackingNumber": "TRK-f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "items": [
    {
      "productId": 1,
      "quantity": 2,
      "priceAtPurchase": 99.99
    },
    {
      "productId": 5,
      "quantity": 1,
      "priceAtPurchase": 149.99
    }
  ]
}
Error Response (400 Bad Request):
{
  "error": "Insufficient stock",
  "unavailableProducts": [1],
  "message": "Product ID 1 has insufficient stock"
}
Retrieves order information by ID.Response:
{
  "orderId": 789,
  "orderNumber": "ORD-550e8400-e29b-41d4-a716-446655440000",
  "customerId": 12345,
  "status": "SHIPPED",
  "totalPrice": 349.97,
  "orderDate": "2026-03-08T14:30:00",
  "trackingNumber": "TRK-f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
Retrieves all orders for a specific customer.Response:
[
  {
    "orderId": 789,
    "orderNumber": "ORD-550e8400-e29b-41d4-a716-446655440000",
    "status": "DELIVERED",
    "totalPrice": 349.97,
    "orderDate": "2026-03-08T14:30:00"
  }
]
Cancels an order and releases reserved stock.Response: 204 No ContentSide Effects:
  • Order status updated to CANCELLED
  • Inventory Service called to release reserved stock
Updates order status to SHIPPED and consumes reserved stock.Response: 204 No Content

Database Configuration

server:
  port: 8090

spring:
  application:
    name: msvc-order
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://order_db:5432/orderdb
    username: postgres
    password: password
  jpa:
    hibernate:
      ddl-auto: create
    database: postgresql
    database-platform: org.hibernate.dialect.PostgreSQLDialect
  config:
    import: "optional:configserver:http://localhost:8888"

eureka:
  instance:
    hostname: eureka-server
  client:
    service-url:
      defaultZone: http://${eureka.instance.hostname}:8761/eureka/
The service connects to PostgreSQL running in Docker container order_db on port 5432.

Order Workflow

1

Client Submits Order

Customer sends order creation request with items and delivery details.
2

Order Persisted with PENDING_CHECK

Order Service saves order to PostgreSQL with initial status.
3

Stock Reservation

Order Service calls Inventory Service via Feign to reserve stock for all items.
4

Stock Validation

If stock is insufficient, order status changes to CANCELLED and error is returned.If stock is available, Inventory Service reserves the quantities and order continues.
5

Shipment Creation

Order Service calls Tracking Service to initialize shipment tracking.
6

Order Confirmed

Order status updated to CONFIRMED. Client receives order ID and tracking number.
7

Order Shipped

When order ships, status updates to SHIPPED. Inventory Service confirms reservation (reduces total stock).
8

Delivery Confirmation

Tracking Service updates delivery status. Order marked as DELIVERED.

Architecture Pattern

While the Order Service is simpler than Inventory Service, it should still follow hexagonal architecture principles:
com.microservice.order/
├── domain/
│   ├── model/
│   │   ├── Order.java
│   │   ├── OrderItem.java
│   │   └── OrderStatus.java (enum)
│   └── exception/
│       └── DomainException.java
├── application/
│   ├── port/
│   │   ├── input/
│   │   │   └── CreateOrderUseCase.java
│   │   └── output/
│   │       ├── OrderRepository.java
│   │       ├── InventoryClient.java
│   │       └── TrackingClient.java
│   └── service/
│       └── CreateOrderService.java
└── infrastructure/
    ├── adapter/
    │   ├── input/
    │   │   └── rest/
    │   │       └── OrderController.java
    │   └── output/
    │       ├── persistence/
    │       │   ├── OrderEntity.java
    │       │   ├── OrderItemEntity.java
    │       │   └── OrderRepositoryAdapter.java
    │       └── client/
    │           ├── InventoryFeignClient.java
    │           └── TrackingFeignClient.java
    └── config/

Error Handling

Scenario: Inventory Service returns 400 indicating insufficient stock.Handling:
  • Order status set to CANCELLED
  • Return 400 to client with specific product IDs that are unavailable
  • No tracking shipment created
Scenario: Inventory Service is down (Feign timeout/circuit breaker).Handling:
  • Order remains in PENDING_CHECK status
  • Return 503 Service Unavailable to client
  • Client should retry
  • Consider implementing retry logic with exponential backoff
Scenario: Tracking Service fails after stock is reserved.Handling:
  • Order status remains CONFIRMED (stock is reserved)
  • Tracking number can be generated later via background job
  • Return partial success to client with warning
  • Implement compensating transaction to create tracking later

Future Enhancements

Saga Pattern

Implement distributed transactions with saga pattern for better failure handling and compensation.

Event-Driven Architecture

Replace synchronous Feign calls with asynchronous events using Kafka or RabbitMQ.

Circuit Breaker

Add Resilience4j circuit breaker to handle downstream service failures gracefully.

CQRS

Separate read and write models for better scalability and performance.

Inventory Service

Called to validate and reserve stock

Tracking Service

Called to initialize shipment tracking

Architecture Overview

Learn about the overall system design

Build docs developers (and LLMs) love