Documentation Index
Fetch the complete documentation index at: https://mintlify.com/microservices-patterns/ftgo-application/llms.txt
Use this file to discover all available pages before exploring further.
The Order History Service is the FTGO application’s CQRS read-side view for order history. Rather than querying the Order Service’s write-side MySQL database, it consumes domain events from the Order Service and materializes a denormalized, consumer-queryable view in Amazon DynamoDB. This makes it possible to efficiently list all orders for a given consumer without putting load on the transactional order database. This page covers the service’s purpose, domain model, REST API, event subscriptions, and DynamoDB schema.
Responsibilities
- Subscribe to
OrderCreatedEvent, OrderAuthorized, OrderCancelled, and OrderRejected events from the Order Service
- Maintain a DynamoDB-backed view of each order including consumer ID, status, restaurant info, line items, and order total
- Support efficient per-consumer order history queries using DynamoDB’s partition key model
- Provide a REST API for the API Gateway to retrieve a consumer’s order history or look up a single order
- Track event source metadata (
SourceEvent) to ensure idempotent event processing
The Order History Service is a read-only CQRS projection — it never writes to the Order Service’s database and does not expose any mutation endpoints. All state derives from domain events.
Domain Model
Order (DynamoDB projection)
The Order class represents a denormalized row in DynamoDB — not a JPA entity. It is populated from event payloads and stored in DynamoDB by OrderHistoryDaoDynamoDb.
| Field | Type | Source |
|---|
orderId | String | DomainEventEnvelope.getAggregateId() |
consumerId | String | OrderCreatedEvent.orderDetails.consumerId |
status | OrderState | Updated on each state-change event |
lineItems | List<OrderLineItem> | OrderCreatedEvent.orderDetails.lineItems |
orderTotal | Money | OrderCreatedEvent.orderDetails.orderTotal |
restaurantId | long | OrderCreatedEvent.orderDetails.restaurantId |
restaurantName | String | OrderCreatedEvent.restaurantName |
creationDate | DateTime | Set at projection time (Joda-Time DateTime.now()) |
OrderHistory
OrderHistory is a simple wrapper returned by OrderHistoryDao.findOrderHistory():
| Field | Type | Description |
|---|
orders | List<Order> | The matched orders for the consumer |
startKey | Optional<String> | DynamoDB pagination key for the next page of results |
OrderHistoryDao
OrderHistoryDao is the data access interface with the following operations:
| Method | Description |
|---|
addOrder(Order, Optional<SourceEvent>) | Insert a new order projection; idempotent via SourceEvent check |
findOrderHistory(consumerId, filter) | Query orders for a consumer with optional filters |
findOrder(orderId) | Look up a single order by ID |
updateOrderState(orderId, newState, eventSource) | Update the order’s status field |
noteTicketPreparationStarted(orderId) | Mark that kitchen preparation has begun |
noteTicketPreparationCompleted(orderId) | Mark that the order is ready for pickup |
notePickedUp(orderId, eventSource) | Mark that the courier has collected the order |
updateLocation(orderId, location) | Update the courier’s current location for the delivery |
noteDelivered(orderId) | Mark the order as delivered |
SourceEvent carries the source aggregate type, aggregate ID, and event ID. It is used by the DynamoDB implementation to prevent duplicate event processing via a conditional write expression.
REST API
The service listens on port 8086. All endpoints are under the /orders path.
| Method | Path | Query params | Description |
|---|
GET | /orders | consumerId (required) | List all orders for the given consumer. Returns order ID, status, restaurant ID, and restaurant name for each. |
GET | /orders/{orderId} | — | Retrieve a single order projection. Returns 404 if not found. |
List orders response body
{
"orders": [
{
"orderId": "1",
"status": "APPROVED",
"restaurantId": 2,
"restaurantName": "Ajisen Ramen"
}
],
"startKey": null
}
The startKey field is populated when DynamoDB returns a pagination token. Pass it as a filter in the next request to retrieve subsequent pages.
Get order response body
{
"orderId": "1",
"status": "APPROVED",
"restaurantId": 2,
"restaurantName": "Ajisen Ramen"
}
Messaging
Events consumed
The service subscribes to the net.chrisrichardson.ftgo.orderservice.domain.Order aggregate channel via DomainEventHandlersBuilder:
| Event | Handler | Effect |
|---|
OrderCreatedEvent | handleOrderCreated() | Calls orderHistoryDao.addOrder() with the full projection |
OrderAuthorized | handleOrderAuthorized() | Calls updateOrderState(..., APPROVED, ...) |
OrderCancelled | handleOrderCancelled() | Calls updateOrderState(..., CANCELLED, ...) |
OrderRejected | handleOrderRejected() | Calls updateOrderState(..., REJECTED, ...) |
A DeliveryPickedUp handler exists in the source (handleDeliveryPickedUp) but is currently commented out, pending a common event abstraction.
Commands handled / published
The Order History Service does not handle saga commands and does not publish any domain events. It is purely a consumer.
Database
The Order History Service uses Amazon DynamoDB instead of MySQL. This is a deliberate architectural choice: DynamoDB’s partition-key model enables efficient per-consumer queries without requiring a full table scan.
| DynamoDB attribute | Description |
|---|
| Partition key | consumerId — enables fast findOrderHistory queries |
| Sort key / secondary index | orderId — enables findOrder single-item lookups |
status | Current OrderState string |
lineItems | Serialized list of order line items |
orderTotal | Serialized Money value |
restaurantId / restaurantName | Denormalized from OrderCreatedEvent |
creationDate | ISO-formatted timestamp |
The SourceEvent metadata (aggregate type, aggregate ID, event ID) is stored alongside each item to support idempotent writes via DynamoDB conditional expressions.
When running the FTGO application locally with docker-compose, DynamoDB is emulated by the dynamodblocal container (port 8000). Tables are initialized automatically by the dynamodblocal-init container at startup.