Real World Example

Order Placed, Everyone Reacts

Kafka was created at LinkedIn and runs at Uber, Netflix, Flipkart, Swiggy, Zomato and Paytm for order, ride and payment events. In ShopEase one OrderPlaced event drives the whole post-checkout flow, and order-service never calls the other services directly:

order-service  -- OrderPlaced(1001) -->  [order-placed]
                                             |-> payment-service      charge UPI -> PaymentCompleted(1001)
                                             |-> product-service      reserve stock
                                             |-> notification-service SMS "Order received"
                                             '-> analytics            sales dashboard
 payment-service -- PaymentCompleted -->  [payment-events]
                                             |-> order-service        status = PAID
                                             '-> notification-service SMS "Payment successful"

Closing the Loop in order-service

public record PaymentCompletedEvent(String eventId, Long orderId, String paymentId, String status) {}

@Component
@RequiredArgsConstructor
@Slf4j
class PaymentEventsListener {

    private final OrderRepository orders;

    @KafkaListener(topics = "payment-events", groupId = "order-service")
    @Transactional
    void onPayment(PaymentCompletedEvent event) {
        Order order = orders.findById(event.orderId()).orElseThrow();
        if (order.getStatus() == OrderStatus.PAID) return;          // duplicate: already handled
        if ("SUCCESS".equals(event.status())) {
            order.markPaid(event.paymentId());
        } else {
            order.markPaymentFailed();
        }
        log.info("Order {} -> {}", order.getId(), order.getStatus());
    }
}

Example

The user gets 202 at once; the status changes a moment later:

curl -X POST localhost:8080/api/orders -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"userId": 42, "productId": 7, "quantity": 2, "paymentMode": "UPI"}'
sleep 2
curl localhost:8080/api/orders/1001 -H "Authorization: Bearer $TOKEN"

Example output

HTTP/1.1 202 Accepted
{"orderId":1001,"status":"PLACED"}

{"orderId":1001,"status":"PAID","paymentId":"pay_9XfK2LmQ","total":2998.00}

# logs
order-service        INFO OrderPlaced 1001 -> partition 2 offset 17
payment-service      INFO OrderPlaced 1001 (p2@17) amount 2998.00
notification-service INFO SMS to +91-98xxxxxx21: Order 1001 received
order-service        INFO Order 1001 -> PAID

Exercise: Add a loyalty-service that gives 1 point per ₹100 spent on every placed order. What do you need to change in order-service?

Show answer
// Nothing in order-service! Just subscribe with a new group id:
@KafkaListener(topics = "order-placed", groupId = "loyalty-service")
void onOrderPlaced(OrderPlacedEvent event) {
    if (processedEvents.existsById(event.eventId())) return;   // idempotent
    int points = event.totalAmount().divide(BigDecimal.valueOf(100), RoundingMode.DOWN).intValue();
    loyaltyAccounts.addPoints(event.userId(), points);
    processedEvents.save(new ProcessedEvent(event.eventId(), Instant.now()));
}
// With auto-offset-reset: earliest it can even process past orders still in retention.