Order Management Service - Domain Architecture & Business Capabilities

Service Overview

The Order Management Service orchestrates the fulfillment order lifecycle from order receipt through shipment. It serves as the central coordinator for fulfillment operations, managing order validation, status tracking, and event-driven orchestration across the fulfillment network.

Architecture Pattern: Hexagonal Architecture (Ports & Adapters) Technology Stack: Spring Boot, Spring Data MongoDB, Spring Kafka, CloudEvents Integration Pattern: Event-Driven Choreography


Domain Model & Bounded Context

Bounded Context: Fulfillment Order Management

The Fulfillment Order Management bounded context focuses on the orchestration and lifecycle management of orders within the fulfillment network.

Context Boundaries

Responsibilities (What’s IN):

External Dependencies (What’s OUT):

Ubiquitous Language

Core Domain Terms:


Subdomain Classification

Core Domain: Order Fulfillment Orchestration

Strategic Importance: HIGH - Central coordination point

This is the heart of the order management service and provides significant business value through:

Subdomains:

1. Order Lifecycle Management (Core)

2. Order Validation & Promising (Core)

3. Order Visibility & Tracking (Supporting)

4. Order Modification & Cancellation (Supporting)


Domain Model

Aggregates

1. FulfillmentOrder (Aggregate Root)

Description: The central aggregate representing an order being fulfilled, managing its complete lifecycle from receipt to shipment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@AggregateRoot
public class FulfillmentOrder {
    private String orderId; // Aggregate ID
    private String customerId;
    private Address shippingAddress;
    private List<OrderItem> items;
    private OrderStatus status;
    private Priority priority;
    private ServiceLevel serviceLevel;
    private SLADeadline slaDeadline;
    private OrderTimestamps timestamps;
    private List<OrderStatusTransition> statusHistory;
    private int version; // Optimistic locking

    // Business methods
    public void validate(ValidationRules rules);
    public void invalidate(List<ValidationFailure> failures);
    public void release();
    public void cancel(CancellationReason reason);
    public void markPickingStarted();
    public void markPickingCompleted();
    public void markPackingCompleted();
    public void markShipped(TrackingInfo trackingInfo);

    // Invariants
    private void ensureNotAlreadyShipped();
    private void ensureCanBeCancelled();
    private void ensureValidStatusTransition(OrderStatus newStatus);
}

Order Status State Machine:

1
2
3
NEW β†’ RECEIVED β†’ VALIDATED β†’ RELEASED β†’ IN_PROGRESS β†’ PACKING β†’ SHIPPED
         ↓            ↓
    INVALIDATED  CANCELLED

Invariants:

Domain Events:


Entities

OrderItem

Description: Represents a line item within an order.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Entity
public class OrderItem {
    private String orderItemId;
    private String sku;
    private int quantityOrdered;
    private int quantityFulfilled;
    private BigDecimal unitPrice;
    private ItemStatus status;

    public void markPicked(int quantity);
    public void markShipped();
    public boolean isFullyFulfilled();
    public boolean isShortPicked();
}

enum ItemStatus {
    PENDING,
    ALLOCATED,
    PICKED,
    PACKED,
    SHIPPED,
    CANCELLED
}

OrderStatusTransition

Description: Records each status transition for audit and analysis.

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class OrderStatusTransition {
    private OrderStatus fromStatus;
    private OrderStatus toStatus;
    private LocalDateTime transitionedAt;
    private String transitionedBy; // User or System
    private String reason;

    public Duration getDurationInStatus() {
        // Calculate time spent in previous status
    }
}

Value Objects

Address

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ValueObject
public class Address {
    private String recipientName;
    private String addressLine1;
    private String addressLine2;
    private String city;
    private String state;
    private String postalCode;
    private String country;
    private AddressType type; // RESIDENTIAL, COMMERCIAL

    public boolean isValid();
    public boolean isInternational();
    public String toFormattedString();
}

SLADeadline

1
2
3
4
5
6
7
8
9
10
11
12
@ValueObject
public class SLADeadline {
    private LocalDateTime orderReceivedAt;
    private LocalDateTime expectedShipmentBy;
    private LocalDateTime expectedDeliveryBy;
    private Priority priority;

    public boolean isBreached();
    public boolean isAtRisk(double thresholdPercentage);
    public Duration remainingTime();
    public double percentageElapsed();
}

OrderTimestamps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ValueObject
public class OrderTimestamps {
    private LocalDateTime createdAt;
    private LocalDateTime receivedAt;
    private LocalDateTime validatedAt;
    private LocalDateTime releasedAt;
    private LocalDateTime pickingStartedAt;
    private LocalDateTime pickingCompletedAt;
    private LocalDateTime packingCompletedAt;
    private LocalDateTime shippedAt;

    public Duration getProcessingTime();
    public Duration getPickingDuration();
    public Duration getPackingDuration();
}

Priority

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ValueObject
public enum Priority {
    STANDARD(3, Duration.ofDays(3)),
    RUSH(2, Duration.ofDays(2)),
    EXPRESS(1, Duration.ofHours(24)),
    CRITICAL(0, Duration.ofHours(4));

    private final int level;
    private final Duration sla;

    // Business methods
    public boolean hasHigherPriorityThan(Priority other);
    public Duration getAllocationTTL();
}

Domain Services

OrderValidationService

Responsibility: Orchestrate multi-step order validation process.

1
2
3
4
5
6
7
8
9
10
@DomainService
public class OrderValidationService {

    public ValidationResult validate(FulfillmentOrder order);

    private ValidationResult validateProducts(List<OrderItem> items);
    private ValidationResult validateInventoryAvailability(List<OrderItem> items);
    private ValidationResult validateAddress(Address address);
    private ValidationResult validateBusinessRules(FulfillmentOrder order);
}

OrderOrchestrationService

Responsibility: Coordinate order processing across services.

1
2
3
4
5
6
7
8
9
@DomainService
public class OrderOrchestrationService {

    public void processNewOrder(FulfillmentOrder order);
    public void handleValidationResult(String orderId, ValidationResult result);
    public void handleInventoryAllocated(String orderId, AllocationResult result);
    public void handlePickingCompleted(String orderId, PickingResult result);
    public void handleShipmentCreated(String orderId, ShipmentInfo shipment);
}

SLATrackingService

Responsibility: Monitor order progress against SLAs.

1
2
3
4
5
6
7
8
@DomainService
public class SLATrackingService {

    public SLAStatus calculateSLAStatus(FulfillmentOrder order);
    public List<FulfillmentOrder> findAtRiskOrders(double thresholdPercentage);
    public List<FulfillmentOrder> findBreachedOrders();
    public SLAMetrics calculateSLAMetrics(LocalDate from, LocalDate to);
}

Application Layer

Ports (Interfaces)

Input Ports (Use Cases)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Commands
public interface CreateFulfillmentOrderCommand {
    FulfillmentOrder execute(CreateOrderRequest request);
}

public interface CancelFulfillmentOrderCommand {
    void execute(String orderId, CancellationReason reason);
}

public interface ReleaseOrderCommand {
    void execute(String orderId);
}

// Queries
public interface GetOrderStatusQuery {
    OrderStatus execute(String orderId);
}

public interface GetOrderDetailsQuery {
    FulfillmentOrder execute(String orderId);
}

public interface GetOrderHistoryQuery {
    List<OrderStatusTransition> execute(String orderId);
}

public interface ListOrdersQuery {
    List<FulfillmentOrder> execute(OrderSearchCriteria criteria);
}

Output Ports (Dependencies)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Repository ports
public interface FulfillmentOrderRepository {
    Optional<FulfillmentOrder> findById(String orderId);
    void save(FulfillmentOrder order);
    List<FulfillmentOrder> findByStatus(OrderStatus status);
    List<FulfillmentOrder> findByCustomerId(String customerId);
}

// External service ports
public interface ProductCatalogClient {
    boolean allProductsExist(List<String> skus);
    List<ProductInfo> getProducts(List<String> skus);
}

public interface InventoryClient {
    ATPCheckResult checkAvailability(List<OrderItem> items);
}

public interface EventPublisher {
    void publish(DomainEvent event);
}

Infrastructure Layer

Adapters

Inbound Adapters

REST Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@RestController
@RequestMapping("/api/v1/fulfillment_orders")
public class FulfillmentOrderController {

    @PostMapping
    public ResponseEntity<OrderResponse> createOrder(
        @RequestBody CreateOrderRequest request
    );

    @GetMapping("/{orderId}")
    public ResponseEntity<OrderResponse> getOrder(
        @PathVariable String orderId
    );

    @PostMapping("/{orderId}/cancel")
    public ResponseEntity<Void> cancelOrder(
        @PathVariable String orderId,
        @RequestBody CancellationRequest request
    );

    @GetMapping
    public ResponseEntity<List<OrderResponse>> listOrders(
        @RequestParam(required = false) String customerId,
        @RequestParam(required = false) OrderStatus status,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "20") int size
    );

    @GetMapping("/{orderId}/history")
    public ResponseEntity<List<StatusTransitionResponse>> getHistory(
        @PathVariable String orderId
    );
}

Event Listener - Inventory Events

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class InventoryEventListener {

    @KafkaListener(topics = "fulfillment.inventory.v1.events")
    public void handleInventoryAllocated(InventoryAllocatedEvent event) {
        // Update order status, potentially release for fulfillment
    }

    @KafkaListener(topics = "fulfillment.inventory.v1.events")
    public void handleAllocationFailed(AllocationFailedEvent event) {
        // Invalidate order due to insufficient inventory
    }
}

Event Listener - Warehouse Events

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class WarehouseEventListener {

    @KafkaListener(topics = "fulfillment.warehouse.v1.events")
    public void handlePickingCompleted(PickingCompletedEvent event) {
        // Update order status to packing
    }

    @KafkaListener(topics = "fulfillment.warehouse.v1.events")
    public void handlePackingCompleted(PackingCompletedEvent event) {
        // Update order status to ready for shipment
    }
}

Event Listener - Shipment Events

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class ShipmentEventListener {

    @KafkaListener(topics = "fulfillment.shipment.v1.events")
    public void handleShipmentCreated(ShipmentCreatedEvent event) {
        // Update order with tracking information
    }

    @KafkaListener(topics = "fulfillment.shipment.v1.events")
    public void handleShipmentDelivered(ShipmentDeliveredEvent event) {
        // Mark order as delivered
    }
}

Outbound Adapters

MongoDB Adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Repository
public class MongoFulfillmentOrderRepository implements FulfillmentOrderRepository {

    private final MongoTemplate mongoTemplate;

    @Override
    public Optional<FulfillmentOrder> findById(String orderId) {
        return Optional.ofNullable(
            mongoTemplate.findById(orderId, FulfillmentOrder.class)
        );
    }

    @Override
    public void save(FulfillmentOrder order) {
        mongoTemplate.save(order);
    }

    @Override
    public List<FulfillmentOrder> findByStatus(OrderStatus status) {
        Query query = new Query(Criteria.where("status").is(status));
        return mongoTemplate.find(query, FulfillmentOrder.class);
    }
}

Kafka Event Publisher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class KafkaEventPublisher implements EventPublisher {

    private final KafkaTemplate<String, CloudEvent> kafkaTemplate;
    private static final String TOPIC = "fulfillment.order.v1.events";

    @Override
    public void publish(DomainEvent event) {
        CloudEvent cloudEvent = CloudEventBuilder.v1()
            .withId(event.getId())
            .withType(event.getType())
            .withSource(URI.create("order-management-service"))
            .withData(event.toJson().getBytes())
            .build();

        kafkaTemplate.send(TOPIC, cloudEvent);
    }
}

Business Capabilities

L1: Order Fulfillment Orchestration

Manage the complete lifecycle of fulfillment orders from initial receipt through final shipment, coordinating with inventory, warehouse, and shipment services.


L2: Order Lifecycle Management

L3.1: Order Creation & Receipt

L3.2: Order Validation

L3.3: Order Invalidation Handling

L3.4: Order Release for Fulfillment

L3.5: Order Status Tracking

L3.6: Order Completion


L2: Order Modification & Cancellation

L3.7: Order Cancellation

L3.8: Order Modification (Future)


L2: Order Prioritization & SLA Management

L3.9: Order Priority Classification

L3.10: SLA Tracking & Monitoring

L3.11: SLA Compliance Reporting


L2: Order Visibility & Tracking

L3.12: Order Status Query

L3.13: Order Event History

L3.14: Bulk Order Reporting


L2: Event-Driven Orchestration

L3.15: Order Event Publishing

L3.16: Inventory Event Consumption

L3.17: Warehouse Event Consumption

L3.18: Shipment Event Consumption


Integration Patterns

Context Mapping

E-commerce/OMS (Upstream - Open Host Service)

Product Catalog (Upstream - Customer/Supplier)

Inventory (Partner - Partnership)

Warehouse Operations (Downstream - Customer/Supplier)

Shipment & Transportation (Downstream - Customer/Supplier)


Event Schemas

FulfillmentOrderReceivedEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "specversion": "1.0",
  "type": "com.paklog.order.received",
  "source": "order-management-service",
  "id": "evt-order-12345",
  "time": "2025-10-18T10:30:00Z",
  "datacontenttype": "application/json",
  "data": {
    "orderId": "ORD-67890",
    "customerId": "CUST-123",
    "items": [
      {"sku": "PROD-001", "quantity": 2, "unitPrice": 29.99},
      {"sku": "PROD-002", "quantity": 1, "unitPrice": 49.99}
    ],
    "shippingAddress": {
      "recipientName": "John Doe",
      "addressLine1": "123 Main St",
      "city": "San Francisco",
      "state": "CA",
      "postalCode": "94105",
      "country": "US"
    },
    "priority": "STANDARD",
    "receivedAt": "2025-10-18T10:30:00Z"
  }
}

FulfillmentOrderValidatedEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
  "specversion": "1.0",
  "type": "com.paklog.order.validated",
  "source": "order-management-service",
  "id": "evt-order-valid-123",
  "time": "2025-10-18T10:30:30Z",
  "datacontenttype": "application/json",
  "data": {
    "orderId": "ORD-67890",
    "customerId": "CUST-123",
    "items": [
      {"sku": "PROD-001", "quantity": 2},
      {"sku": "PROD-002", "quantity": 1}
    ],
    "priority": "STANDARD",
    "warehouseId": "WH01",
    "validatedAt": "2025-10-18T10:30:30Z",
    "slaDeadline": "2025-10-21T17:00:00Z"
  }
}

FulfillmentOrderCancelledEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "specversion": "1.0",
  "type": "com.paklog.order.cancelled",
  "source": "order-management-service",
  "id": "evt-order-cancel-456",
  "time": "2025-10-18T11:00:00Z",
  "datacontenttype": "application/json",
  "data": {
    "orderId": "ORD-67890",
    "customerId": "CUST-123",
    "cancelledBy": "CUSTOMER",
    "cancellationReason": "CUSTOMER_REQUEST",
    "previousStatus": "VALIDATED",
    "cancelledAt": "2025-10-18T11:00:00Z"
  }
}

Quality Attributes

Reliability

Performance

Scalability


Summary

The Order Management Service is the central orchestrator of the fulfillment network:

Business Impact: 10,000+ orders/hour, >95% SLA compliance, >99% perfect order rate, 4-6 hour average cycle time.