Skip to content

Real-World Example: Order Processing

E-commerce store purchase order workflow demonstrating event-driven architecture with Kafka and CQRS patterns.

Use Case: E-Commerce Order Flow

This workshop simulates a complete order processing system where customers browse products, add items to a cart, and submit orders asynchronously.

User Flow

1. Browse Products (REST → QueryService)
   GET /api/products → List all products

2. Add to Cart (Frontend state)
   Cart maintained in browser session

3. Submit Order (Kafka Producer)
   POST /orders/purchase → Kafka message to 'purchase-orders' topic

4. Backend Consumes (Kafka Consumer)
   @KafkaListener receives message

5. Order Processing (CommandService)
   Save order to MySQL database

6. Check Status (REST → QueryService)
   GET /api/orders/{id} → Get current order status

State Transitions

CREATED ─→ PENDING ─→ COMPLETED
                        │
                        ├─→ SHIPPED ─→ DELIVERED
                        │
                        └─→ CANCELLED

Each state transition represents an event in the system. The order status progresses through these states as it moves through the fulfillment pipeline.

Code Walkthrough

Frontend: PurchaseOrderProducer

@Component
public class PurchaseOrderProducer {
    private final KafkaTemplate<String, PurchaseOrderDTO> kafkaTemplate;

    public void sendOrder(PurchaseOrderDTO order) {
        kafkaTemplate.send("purchase-orders", order.getOrderId(), order);
        log.info("Order sent to Kafka: {}", order.getOrderId());
    }
}

Backend: PurchaseOrderConsumer

@Component
public class PurchaseOrderConsumer {
    private final OrderCommandService commandService;

    @KafkaListener(topics = "purchase-orders", groupId = "order-processing-group")
    public void consumeOrder(PurchaseOrderDTO order) {
        log.info("Received order: {}", order.getOrderId());
        commandService.createOrder(order);
    }
}

Backend: OrderCommandService

@Service
public class OrderCommandService {
    private final OrderRepository orderRepository;

    @Transactional
    public void createOrder(PurchaseOrderDTO orderDTO) {
        Order order = new Order();
        order.setOrderId(orderDTO.getOrderId());
        order.setCustomerId(orderDTO.getCustomerId());
        order.setTotal(orderDTO.getTotal());
        order.setStatus(PurchaseOrderStatus.CREATED);

        orderRepository.save(order);
    }
}

Database Schema

Order Table:
- order_id (VARCHAR, PK)
- customer_id (VARCHAR)
- total (DECIMAL)
- order_date (TIMESTAMP)
- status (VARCHAR)

OrderItem Table:
- id (BIGINT, PK, AUTO_INCREMENT)
- order_id (VARCHAR, FK → Order)
- product_id (VARCHAR)
- quantity (INT)
- price (DECIMAL)
- total (DECIMAL)

Key Takeaways

  • Order submission is asynchronous via Kafka
  • Order queries are synchronous via REST
  • CQRS separates command and query paths
  • State machine for order lifecycle management