Capstone: Designing the Architecture for a Real Application

Software Architecture Fundamentals

Chapter 10 · Capstone: Designing the Architecture for a Real Application

Every chapter in this course built one real, verified piece of the same underlying system. This capstone assembles all nine into a single, working whole — OrderRepository (Ch.1–2), PricingEngine behind ports (Ch.7), an EventBus (Ch.6), OrderService/UserService/EmailService (Ch.4–6), and a stateless quote API (Ch.8) — and runs one real order through the whole thing end to end.

Wiring the Full System

# Chapter 7 — the hexagonal core, and its adapters pricing_engine = PricingEngine(RealInventoryAdapter(stock_data), LoggingNotificationAdapter()) # Chapters 1-2, 4-7 — business layer, sharing the SAME PricingEngine as the API below order_service = OrderService(OrderRepository(), pricing_engine, EventBus()) # Chapters 5-6 — reacting services, subscribed via the event bus event_bus.subscribe('OrderPlaced', user_service.award_loyalty_points) event_bus.subscribe('OrderPlaced', email_service.send_confirmation) # Chapter 8 — a stateless quote endpoint, delegating to the SAME PricingEngine core def quote_price_via_api(pricing_engine, product_id, base_price, is_loyalty_member): return pricing_engine.calculate_price(product_id, base_price, is_loyalty_member)

Note the one deliberate design choice this capstone adds on top of the individual chapters: OrderService and the quote API both hold a reference to the identical PricingEngine instance — not two separately-constructed ones.

Running One Real Order End to End

Verified directly — a customer's quoted price matches the actual charged price exactly
A customer checks a price via quote_price_via_api() before buying — 103.5. They then place the order through order_service.place_order() — the actual charge is 103.5. Identical, matching a manual calculation (100 × 1.15 scarcity, stock 5 < 10, × 0.9 loyalty) exactly. This is Chapter 8's own thick-client lesson, applied a second time on the server side: had the quote endpoint and the order-placement flow used two separately-written pricing implementations instead of one shared PricingEngine, they could have silently diverged the same way the mobile and web clients did.
Verified directly — the shared core's own side effect fired twice, proving both paths genuinely ran the same logic
notification_adapter.notifications_sent shows ['PROD-1', 'PROD-1']two low-stock notifications, one from the quote call and one from the actual order. Both calls independently triggered PricingEngine's own scarcity-pricing branch, confirming the quote and the real order didn't just happen to agree — they ran through the exact same decision logic, twice.
Verified directly — every downstream reaction fired correctly, with the core business layer still fully decoupled from them
user_service.users correctly shows {'USER-1': {'points': 10}}; email_service.sent_emails correctly shows the confirmation message with the right total. OrderService's own source, re-checked here exactly as in Chapter 6, still contains False references to both UserService and EmailService by name. repository.get('ORD-1') correctly stored final_price: 103.5, matching the price actually charged.

A Real ADR for This Design

Title: ADR-007: One Shared PricingEngine for Both Quotes and Order Placement Status: Accepted Context: The system needs to let a customer see a price before committing to an order (a "quote"), and separately needs to calculate the actual charge when an order is placed. Building these as two separate pricing implementations was considered, since a quote endpoint and an order- placement flow are triggered by different parts of the system. This was rejected: Chapter 8 verified that two independently-implemented copies of the same business rule can silently diverge (a real $0.50 discrepancy between two thick clients was found and measured). Decision: Both the quote API (Ch.8) and OrderService's own order-placement flow (Ch.1-2, 4-6) will hold a reference to the same PricingEngine instance (Ch.7), constructed once and passed to both. Neither will implement its own copy of the pricing rules. Consequences: A quoted price is now guaranteed to match the eventual charged price, verified directly in this chapter's own capstone run. This does mean PricingEngine's own construction (which adapters it uses) becomes a shared dependency both the API layer and the business layer must agree on - a coordination cost, but a strictly smaller one than maintaining two implementations that could drift apart.
Chapter 9's own C4 diagram already documented this
Chapter 9's Level 2 diagram already drew PricingEngine as a single box, connected to both the order-processing flow and (indirectly, via the Pricing API) the client layer. This capstone's own verified code confirms that diagram wasn't aspirational — it's an accurate picture of what actually got built.

What This Course Doesn't Cover

This course deliberately stopped at a single application's own internal shape. It did not cover how OrderService and UserService would actually be deployed as separate, independently-scalable processes (Chapter 4 measured the network cost of that decision, but didn't build a real deployed system); how the event bus would behave under real concurrent load; or any of the resilience patterns (circuit breakers, retries, rate limiting) a genuinely production system would need once EventBus becomes a real message queue instead of an in-process Python object.

Where This Connects

This capstone's findingWhat it connects to
Every component from Chapters 1–9 assembled into one verified, running systemConfirms this course's own chapter-to-chapter continuity wasn't just narrative — the pieces genuinely compose
The scope note above (deployment, real message queues, resilience patterns)Distributed Systems & Scalability — this course's own direct sibling, picking up exactly where this scope note leaves off
A real ADR, tested against Chapter 9's own four-question standardThis capstone's own ADR intentionally follows Chapter 9's Context/Decision/Consequences shape, not a shortcut version

Hands-On Exercises

Exercise 1

Run this chapter's own capstone scenario a second time with stock_data = {'PROD-1': 50} (well-stocked, no scarcity pricing) for a non-loyalty customer. Verify the quoted price still matches the actual charged price, and that no low-stock notification fires this time.

📄 View solution
Exercise 2

Deliberately break this chapter's own guarantee: construct a second, separate PricingEngine instance for the quote API only, while OrderService keeps using the original. Using different stock data for each engine's own adapter, verify the quoted price and actual charged price now genuinely diverge — reproducing Chapter 8's own thick-client bug at the server level.

📄 View solution
Exercise 3

Using this chapter's own ADR and Chapter 9's own four-question test, verify how many of those four questions this chapter's ADR-007 can actually answer. Identify which section of ADR-007 answers each one.

📄 View solution

Chapter 10 Quick Reference — and Course Quick Reference

  • This capstone: every verified component from Chapters 1–9 assembled into one working system — a customer's quoted price (103.5) verified matching the actual charged price exactly, because both paths share one PricingEngine core
  • The one new design decision: sharing a single core instance between the quote API and the order-placement flow, documented in a real ADR that passes Chapter 9's own four-question test
  • Course arc: Ch.1 (measuring architecture's cost) → Ch.2 (layers) → Ch.3 (MVC variants) → Ch.4 (monolith vs. microservices, measured) → Ch.5 (finding boundaries) → Ch.6 (event-driven decoupling) → Ch.7 (hexagonal architecture, measured) → Ch.8 (stateless APIs, thin clients) → Ch.9 (documenting it all) → Ch.10 (assembling it into one system)
  • Where this leads: Distributed Systems & Scalability — this course's own sibling, covering what happens once this system needs to run across multiple machines under real load