Skip to main content

Overview

The Order class represents the central aggregate in the ordering module. It manages order lines, parties, pricing, and orchestrates interactions with fulfillment, inventory, and payment services through the order lifecycle.

Class Definition

package com.softwarearchetypes.ordering;

class Order {
    private final OrderId id;
    private OrderStatus status;
    private final List<OrderLine> lines;
    private final OrderParties parties;
    private final List<Reservation> reservations;
    private final OrderServices services;
}

Constructor

id
OrderId
required
Unique identifier for the order
status
OrderStatus
required
Current status of the order (DRAFT, CONFIRMED, PROCESSING, FULFILLED, etc.)
lines
List<OrderLine>
required
Collection of order lines, each representing a product with quantity and specifications
parties
OrderParties
required
Parties involved in the order (buyer, receiver, delivery contact, etc.)
services
OrderServices
required
External services for pricing, inventory, payment, and fulfillment

Key Methods

id()

Returns the order identifier.
public OrderId id()
return
OrderId
The unique order identifier

status()

Returns the current order status.
public OrderStatus status()
return
OrderStatus
Current status: DRAFT, CONFIRMED, PROCESSING, FULFILLED, CLOSED, or CANCELLED

lines()

Returns an immutable copy of order lines.
public List<OrderLine> lines()
return
List<OrderLine>
Immutable list of order lines

addLine()

Adds a new line to the order. Only allowed in DRAFT status.
void addLine(OrderLine line)
line
OrderLine
required
The order line to add
Throws:
  • IllegalStateException if order status doesn’t allow adding lines
  • IllegalArgumentException if line is null

removeLine()

Removes an order line by ID. Only allowed when status permits modifications.
void removeLine(OrderLineId lineId)
lineId
OrderLineId
required
ID of the line to remove
Throws:
  • IllegalStateException if status doesn’t allow modifications or line not found
  • IllegalStateException if removing would leave order with no lines

changeLineQuantity()

Changes the quantity of an existing order line.
void changeLineQuantity(OrderLineId lineId, Quantity newQuantity)
lineId
OrderLineId
required
ID of the line to modify
newQuantity
Quantity
required
New quantity value
Throws:
  • IllegalStateException if order status doesn’t allow modifications
  • IllegalStateException if line not found

priceLines()

Calculates and applies pricing to all order lines using the configured pricing service.
void priceLines()
For each line, creates a PricingContext with effective parties and delegates to OrderServices.pricing() to calculate the price.

confirm()

Confirms the order, triggering inventory allocation, payment capture, and fulfillment initiation.
void confirm()
Process:
  1. Validates order is in DRAFT status
  2. Allocates inventory for each line
  3. Authorizes and captures payment for total order amount
  4. Changes status to CONFIRMED
  5. Starts fulfillment process
Throws:
  • IllegalStateException if not in DRAFT status
  • IllegalStateException if inventory allocation fails
  • IllegalStateException if payment fails

cancel()

Cancels the order and cancels fulfillment if already started.
void cancel()
Throws:
  • IllegalStateException if order status doesn’t allow cancellation (CLOSED or already CANCELLED)

updateFulfillmentStatus()

Updates order status based on fulfillment progress.
void updateFulfillmentStatus(FulfillmentStatus fulfillmentStatus)
fulfillmentStatus
FulfillmentStatus
required
New fulfillment status (IN_PROGRESS, PARTIALLY_COMPLETED, COMPLETED)
Status mapping:
  • IN_PROGRESS or PARTIALLY_COMPLETED → Order status becomes PROCESSING
  • COMPLETED → Order status becomes FULFILLED

getEffectivePartiesFor()

Returns effective parties for a specific order line, merging line-level parties with order-level parties.
OrderParties getEffectivePartiesFor(OrderLine line)
line
OrderLine
required
The order line to get parties for
return
OrderParties
Merged parties with line-level overrides taking precedence

totalPrice()

Calculates total order price if all lines are priced.
Optional<Money> totalPrice()
return
Optional<Money>
Total price summed across all lines, or empty if any line is not priced

isFullyPriced()

Checks if all order lines have pricing applied.
boolean isFullyPriced()
return
boolean
True if all lines are priced

Builder Pattern

Orders are constructed using a fluent builder:
Order order = Order.builder(orderId, parties, services)
    .addLine(productId, quantity)
    .addLine(line -> line
        .productId(anotherProductId)
        .quantity(qty)
        .specification(spec -> spec
            .add("color", "blue")
            .component("battery", "BAT-001")
        )
        .parties(p -> p
            .receiver(receiverParty)
            .deliveryContact(contactParty)
        )
    )
    .build();

Builder Methods

builder()
static Builder
Creates a new order builderParameters:
  • id (OrderId): Order identifier
  • parties (OrderParties): Order-level parties
  • services (OrderServices): External services
addLine()
Builder
Adds an order line using a configuration function or direct parametersOverloads:
  • addLine(Function<LineBuilder, LineBuilder>) - Fluent configuration
  • addLine(ProductIdentifier, Quantity) - Simple line
  • addLine(ProductIdentifier, Quantity, OrderLineSpecification) - With specification
build()
Order
Constructs the order in DRAFT statusThrows: IllegalStateException if order has no lines

Usage Examples

Creating and Confirming an Order

// Create order
Order order = Order.builder(OrderId.generate(), parties, services)
    .addLine(ProductIdentifier.of("LAPTOP-001"), Quantity.of(1))
    .addLine(ProductIdentifier.of("MOUSE-001"), Quantity.of(2))
    .build();

// Price all lines
order.priceLines();

// Confirm order (allocates inventory, captures payment, starts fulfillment)
order.confirm();

Adding Line with Specifications

Order order = Order.builder(orderId, parties, services)
    .addLine(line -> line
        .productId(ProductIdentifier.of("LAPTOP-001"))
        .quantity(Quantity.of(1))
        .specification(spec -> spec
            .add("ram", "16GB")
            .add("storage", "512GB")
            .component("processor", "CPU-I7-12")
            .preference("color", "silver")
        )
    )
    .build();

Line-Level Party Overrides

Order order = Order.builder(orderId, orderParties, services)
    .addLine(line -> line
        .productId(giftCardProductId)
        .quantity(Quantity.of(1))
        .parties(p -> p
            .receiver(giftRecipient)  // Override order-level receiver
            .deliveryContact(recipientEmail)
        )
    )
    .build();

Modifying Order Lines

// Change quantity
order.changeLineQuantity(lineId, Quantity.of(5));

// Remove line
order.removeLine(lineId);

// Add new line
OrderLine newLine = new OrderLine(
    OrderLineId.generate(),
    productId,
    quantity,
    specification,
    null  // Use order-level parties
);
order.addLine(newLine);
  • OrderId: Value object for order identification
  • OrderLine: Individual line item in the order (see /api/ordering/order source code at line 10)
  • OrderStatus: Enum defining order lifecycle states (see /api/ordering/order source code at line 21)
  • OrderParties: Collection of parties involved in order and their roles
  • OrderServices: Service facade for pricing, inventory, payment, and fulfillment
  • OrderLineSpecification: Key-value attributes for product configuration

Lifecycle State Diagram

DRAFT → CONFIRMED → PROCESSING → FULFILLED → CLOSED
  ↓         ↓            ↓
CANCELLED  CANCELLED   CANCELLED
  • DRAFT: Initial state, lines can be added/modified
  • CONFIRMED: Order confirmed, inventory allocated, payment captured
  • PROCESSING: Fulfillment in progress
  • FULFILLED: All lines fulfilled
  • CLOSED: Order archived (final state)
  • CANCELLED: Order cancelled (terminal state)

Build docs developers (and LLMs) love