2Brute2Best
πŸ”₯0
A3

Notify Customer, Restaurant & Rider on Events

High-frequency interview topic Pro OnlyObserver std::function + lambdas
⚑ 60-Second Architecture SummaryA3 · Observer Pattern (Publish-Subscribe)
Naive Bottleneck→Observer Pattern (Publish-Subscribe) Cure
The Naive Bottleneck: Every new listener requires editing OrderService

Hardcoding direct calls to `CustomerNotifier`, `KitchenDisplay`, and `RiderDispatch` inside `OrderService` tightly couples order placement to every downstream consumer. Adding new listeners (like Kafka analytics or delay monitors) forces us to modify `OrderService` every sprint, and observer lifetime mismanagement risks dangling pointers.

Adding the Data Platform squad's Kafka tracker or Marketing's coupon notifier forces developers to open OrderService.cpp, add new #include headers, and insert more manual function calls β€” violating the Open-Closed Principle.

The Pattern Cure: Observer Pattern (Publish-Subscribe)

Define a one-to-many dependency between objects so that when the subject changes state, all its registered dependents are notified automatically.

We define an OrderObserver interface with an onOrderEvent(const OrderEvent&) method. OrderEventBus maintains a list of registered observers. When an order event occurs, OrderService publishes the event to the bus, which broadcasts it to all subscribers. OrderService has no knowledge of which classes are listening.

Sprint 3 of 9 Β· Real-Time Order Event Fan-Out
Story A β€” Food Delivery System β€” Progressive System Build
1. Where our codebase left off
← A2

In Sprint 2 (A2), we decomposed the monolithic OrderService into single-responsibility collaborators (OrderValidator, PriceCalculator, OrderRepository, ReceiptFormatter). OrderService can now persist placed orders cleanly.

2. What we are building now

When an order is placed or updated, multiple downstream systems must react immediately: customer push notifications, restaurant Kitchen Display System (KDS) prep tickets, and delivery rider fleet dispatch.

3. The pain point we hit

Hardcoding direct calls to CustomerNotifier, KitchenDisplay, and RiderDispatch inside OrderService tightly couples order placement to every downstream consumer. Adding new listeners (like Kafka analytics or delay monitors) forces us to modify OrderService every sprint, and observer lifetime mismanagement risks dangling pointers.

4. What this unlocks next
Next: A4 β†’

By routing events through an OrderEventBus to polymorphic OrderObserver subscribers (using std::shared_ptr and std::weak_ptr), any squad can subscribe without touching order logic. Next in Sprint 4 (A4 Β· State), orders cannot remain static β€” they must safely transition through a multi-stage lifecycle (Placed β†’ Accepted β†’ Cooking β†’ OutForDelivery β†’ Delivered) with stage-specific cancellation rules.

01

The product ticket

Ticket Β· A3FOOD-103 Β· Decouple Order Notifications via an Event Bus

When an order is placed or picked up, multiple independent systems need to react: Customer receives push notifications, the Restaurant kitchen prints a KDS prep ticket, and the Rider dispatch engine alerts nearby delivery drivers.

Requirements
  • β€’

    Notify the Customer whenever the order is placed, picked up, delivered, or cancelled.

  • β€’

    Send order details to the Restaurant Kitchen Display System (KDS) immediately upon order placement.

  • β€’

    Dispatch a fleet alert to nearby Delivery Riders when an order is ready for pickup.

The next requirement that breaks naive code

Next sprint, the Data Platform squad wants an Analytics Tracker to log all order lifecycle events to Kafka, and Customer Support wants delayed-order escalation alerts. We need to register new listeners without modifying or recompiling OrderService.

02

Step 1 β€” The naive code & why it breaks

OrderService directly instantiates concrete notification clients (CustomerPushNotifier, KitchenDisplaySystem, RiderDispatchSystem) and calls each one explicitly inside placeOrder().

Naive design (what not to ship) Β· naive.cpp
#include <print>
#include <string>

class CustomerPushNotifier {
public:
    void send(const std::string& orderId, const std::string& msg) {
        std::println("[CUSTOMER PUSH] Order {}: {}", orderId, msg);
    }
};

class KitchenDisplaySystem {
public:
    void printTicket(const std::string& orderId, const std::string& items) {
        std::println("[KITCHEN KDS] Printed ticket for {}: {}", orderId, items);
    }
};

class RiderDispatchSystem {
public:
    void alertFleet(const std::string& orderId, const std::string& pickupArea) {
        std::println("[RIDER DISPATCH] Nearby riders alerted for {} at {}", orderId, pickupArea);
    }
};

// Naive OrderService: directly instantiates and calls each concrete notifier.
// Adding an Analytics listener next week means modifying OrderService again!
class NaiveOrderService {
    CustomerPushNotifier customerNotifier_;
    KitchenDisplaySystem kitchenDisplay_;
    RiderDispatchSystem riderDispatch_;
public:
    void placeOrder(const std::string& orderId, const std::string& items, const std::string& area) {
        std::println("--- Processing Order {} ---", orderId);

        // Hardcoded notification calls:
        customerNotifier_.send(orderId, "Your order has been placed successfully!");
        kitchenDisplay_.printTicket(orderId, items);
        riderDispatch_.alertFleet(orderId, area);
        // Next week: analytics.track(...) will be added here!
    }

    void markOrderPickedUp(const std::string& orderId) {
        std::println("--- Order {} Picked Up ---", orderId);
        customerNotifier_.send(orderId, "Rider picked up your food and is on the way!");
    }
};

int main() {
    NaiveOrderService service;
    service.placeOrder("ORD-301", "2x Butter Naan, 1x Paneer Tikka", "Koramangala");
    service.markOrderPickedUp("ORD-301");
}
Every new listener requires editing OrderService

Adding the Data Platform squad's Kafka tracker or Marketing's coupon notifier forces developers to open OrderService.cpp, add new #include headers, and insert more manual function calls β€” violating the Open-Closed Principle.

High coupling to concrete subsystem APIs

OrderService is tightly coupled to every listener's method signature (sendPush, printTicket, alertFleet). If the kitchen display system changes its ticket format, order processing code breaks.

Inflexible runtime subscriptions

You cannot dynamically disable the kitchen printer during downtime or add a test spy observer during automated integration runs without changing the production class.

03

Step 2 β€” The fix: Observer Pattern (Publish-Subscribe)

Core idea in one sentence

Define a one-to-many dependency between objects so that when the subject changes state, all its registered dependents are notified automatically.

We define an OrderObserver interface with an onOrderEvent(const OrderEvent&) method. OrderEventBus maintains a list of registered observers. When an order event occurs, OrderService publishes the event to the bus, which broadcasts it to all subscribers. OrderService has no knowledge of which classes are listening.

Pattern roles in our story
Subject / Event Bus

OrderEventBus β€” manages the collection of OrderObserver instances and broadcasts events via publish().

Observer Interface

OrderObserver β€” declares virtual void onOrderEvent(const OrderEvent& event) = 0; and a virtual destructor.

Concrete Observers

CustomerNotifier, KitchenDisplay, RiderDispatch, AnalyticsTracker β€” implement onOrderEvent to execute subsystem-specific reactions.

Context / Publisher

OrderService β€” triggers domain actions and tells OrderEventBus that an order event occurred.

Brute2Best Pro Content

Unlock Full UML Diagram & C++23 Solution

The interactive UML architecture diagrams, refactored C++23 code, real-world extensions, and interview Q&A are part of Brute2Best Pro. All 7 foundation chapters are 100% free.

All 440 visual dry runs & algorithms
Interactive step-by-step playback with pointer & array state transitions
52 comprehensive LLD chapters
Interactive UML diagrams, real-world stories (Food Delivery, Trading, RPG), and practice solutions
40 C++ basics labs & hardware memory models
Vtables, byte layouts, assembly insights, and multi-threading race conditions
Production-ready C++23 source code
Modern idioms, clean contracts, time/space complexity derivations, and interview takeaways

First anchor question per pattern, Part 1 LLD & Module 1 C++ basics are 100% Free forever.

Systems Integration

Related C++ Systems & Algorithmic Foundations

Deep dive into underlying layers