Exercises — Software architecture — layered, MVC, event-driven, microservices, serverless
L1 — Recognition
Exercise 1.1 — Match the boundary
Each style below cuts the system along a different line. Match the style to what it isolates:
| Style | Cuts along… |
|---|---|
| Layered | ? |
| Microservices | ? |
| MVC | ? |
Options: (a) a business capability, (b) a technical concern that changes at its own rate, (c) three UI roles (data / display / input-handling).
The picture below shows the three different cutting lines at a glance — read it before answering.

Recall Solution
- Layered → (b) technical concern by change-rate (UI vs business vs data).
- Microservices → (a) a business capability (Orders, Payments…), each with its own database.
- MVC → (c) three roles: Model (data), View (display), Controller (input-handling).
WHY: every style answers "how do we cut so a change stays local?" but they pick different cutting lines. Look at the figure: layered cuts horizontally (the stacked bands), microservices cut vertically (the standing columns, each with its own drum = database), and MVC splits a single box into three labelled roles. See Coupling and Cohesion.
Exercise 1.2 — Which word fits?
Fill each blank with coupling or cohesion: "We want low ______ between modules and high ______ inside a module."
Recall Solution
Low coupling between modules; high cohesion inside a module. WHY: low coupling means changing module A rarely forces a change in B (they barely depend on each other). High cohesion means everything inside a module belongs together, so the module has one clear job.
Exercise 1.3 — Spot the serverless promise
True or false: "Serverless means there are literally no servers running your code."
Recall Solution
False. Servers still exist; the cloud provider manages them, spinning them up on demand and down to zero when idle. You just never provision or patch them. See Cloud Computing — IaaS PaaS FaaS.
L2 — Application
Exercise 2.1 — Trace an MVC click
A user submits an "Add to cart" form in an MVC web app. Put these four actions in order and say which role does each:
- renders updated cart HTML
- validates input and calls
cart.addItem() - decides which page to show next
- holds the cart data and the rule "max 20 items"
The flow diagram below traces one click through the three roles. Two of the four steps are done by the same Controller (they are drawn in amber): step 1 "validate input and call the Model" and step 3 "choose which View to show next." The Model step (apply the max-20 rule) and the View step (render HTML) are drawn in cyan.

Recall Solution
- Controller — validates input, calls
cart.addItem(). (Controller is the only piece touching raw input.) - Model — holds cart data + rule "max 20 items". (Business rules live in the Model.)
- Controller — decides which page to show next. (Controller chooses what to show.)
- View — renders updated cart HTML. (View decides how to show it.)
Order (with roles kept explicit, since two steps are the Controller): Controller (validate + update Model) → Model (apply rule) → Controller (choose which View) → View (render). Note the third step, "choose which View," is a Controller responsibility, not the View's — the View only knows how to render, never which page comes next. See Design Patterns — MVC, Observer.
Exercise 2.2 — Fan-out arithmetic
An e-commerce system has 4 event producers and 6 consumers. Compute the number of connections:
- (a) if every producer wires directly to every consumer it feeds (worst case: all-to-all),
- (b) if they all connect through one event broker.
The two wiring schemes are drawn side by side below — count the white lines in each half.

Recall Solution
Using and with :
- (a) direct connections.
- (b) broker connections (each side plugs once into the broker).
WHY it matters: direct wiring grows like — quadratic — while the broker grows like — linear. Add a 7th consumer: direct jumps by 4 wires, broker by 1.
Edge / degenerate cases: if there are no producers () or no consumers (), then both formulas give 0 connections — with nobody to wire, there is nothing to connect either way. The broker only starts winning once both sides have more than one participant; at direct needs wire and the broker needs (the broker adds one extra connection), so for tiny systems direct is actually cheaper. See Message Queues and Brokers.
Exercise 2.3 — Serverless bill
A function is invoked times/month. Each run uses 0.5 GB memory for 0.4 s. Price is $0.00001667 per GB-second plus a $0.20 per 1M requests fee. Estimate the monthly cost using
Recall Solution
GB-seconds per run (memory × time, from the definition above). Compute cost = N \times 0.2 \times 0.00001667 = 2{,}000{,}000 \times 0.2 \times 0.00001667 = \6.668= 2{,}000{,}000 \times \dfrac{0.20}{1{,}000{,}000} = $0.40\approx $6.668 + $0.40 = \boxed{$7.07}$ per month.
WHY so cheap: you pay only for the ~400{,}000 GB-seconds actually consumed (). Idle time costs nothing — that is the serverless win when is bursty.
L3 — Analysis
Exercise 3.1 — Why "layered ≠ scalable"?
A team argues: "Our app has 4 clean layers, so it's already scalable." Explain in 2–3 sentences why this reasoning is flawed, and what layers actually buy you.
Recall Solution
Layers separate concerns for maintainability, but the whole stack still usually deploys as one process (a monolith). To handle more load you must replicate the entire stack, not one layer — organizational separation is not deployment separation. Layers buy you reasoning locally and swappable implementations (change the DB without touching business logic), not raw horizontal scale. See Monolith vs Microservices and Scalability — Horizontal vs Vertical.
Exercise 3.2 — Spatial vs temporal coupling
Team A switches from direct HTTP calls to an event broker and celebrates "we removed all coupling!" What kind of coupling did they remove, and what new class of problems did they buy?
Recall Solution
They removed spatial coupling — a producer no longer needs to know the consumer's address or wait for its response. They bought temporal complexity: Eventual Consistency (readers may see stale data briefly), no cross-service stack trace (harder debugging), and ordering / duplicate-delivery problems. Net: events trade "who-knows-whom" coupling for "when-does-it-actually-happen" uncertainty. Great for many independent reactions; painful for simple request→response.
Exercise 3.3 — Read the trade-off table
Given the Monolith vs Microservices table (deploy unit, database, failure mode, where complexity lives), a startup has 5 engineers and one shared database. Which row makes microservices most costly for them right now, and why?
Recall Solution
The database row (shared → per-service) plus complexity in the network/ops. Splitting one shared DB into per-service databases forces distributed transactions and eventual consistency, and 5 engineers must now deploy/monitor N services over the network. For that team the code-level complexity of a monolith is far cheaper than the operational complexity of microservices.
L4 — Synthesis
Exercise 4.1 — Design the "OrderPlaced" flow
A monolithic shop must now: (1) charge the card, (2) email a receipt, (3) decrement inventory, (4) log analytics — all triggered when an order is placed. Emails and analytics can lag; charging must succeed before confirming the order.
Design the architecture: what stays synchronous, what goes event-driven, and why.
Recall Solution
- Synchronous (request path): charge the card. The customer must see success/failure now, so the Controller calls the payment step directly and waits.
- Publish
OrderPlacedto a broker once payment succeeds. - Event consumers (asynchronous): email receipt, decrement inventory, analytics — each subscribes to
OrderPlaced. They can lag (eventual consistency is fine here).
WHY split this way: the fan-out is producer → consumers, so event-driven wiring is legs (one leg per participant, from the definition above) vs direct calls. Watch the overhead: here the broker path actually needs one more connection (4 legs vs 3 direct wires) — the extra leg is the producer→broker hop. In such a small case the broker is not cheaper on wire count; you choose it anyway because adding a 5th reaction (SMS) means adding one consumer and the order flow never changes. So the payoff is future changeability, not today's connection count. Correctness-critical steps stay synchronous; "fire-and-forget" reactions go async. See Message Queues and Brokers and Eventual Consistency.
Exercise 4.2 — Conway-aware split
An org has three autonomous teams: Payments, Search, Catalog. Search gets 10× the traffic of the others at peak. Propose a deployment architecture and justify each boundary.
Recall Solution
Three microservices, one per team/capability (Payments, Search, Catalog), each owning its own database.
- Conway's Law: architecture mirrors the org, so mapping one service per autonomous team lets each ship independently. See Conway's Law.
- Independent scaling: only Search needs 10× replicas; a microservice split lets you scale it horizontally without paying to replicate Payments/Catalog. See Scalability — Horizontal vs Vertical.
- Cross-service events (e.g.
ProductUpdated) keep Search's index fresh via a broker, accepting eventual consistency.
WHY not stay monolith: here the three drivers (independent deploy, independent scale of one hot capability, team autonomy) are all genuinely present — the exact conditions that justify the extra network/ops cost.
L5 — Mastery
Exercise 5.1 — Where the cost curves cross
Compare serverless vs a reserved server. Serverless costs $0.00001667 per GB-second (function uses 0.5 GB, 0.4 s per call) plus $0.20 per 1M requests. A reserved server costs a flat $150/month and handles the load with no per-request fee. Find, roughly, the monthly invocation count (the "break-even" count) where the two costs are equal.
The cost curves are plotted below — a sloped line (serverless) meets a flat line (server) at exactly one point.

Recall Solution
Serverless cost per invocation: Set the two costs equal — serverless must match the flat $150: Solve for by dividing both sides by (that isolates , and the units check: \frac{\text{\}}{\text{$/call}} = \text{calls}): $$N^{*} = \frac{150}{c} = \frac{150\ \text{\}}{3.534\times10^{-6}\ \text{$/call}} \approx \mathbf{4.24 \times 10^{7}}\ \text{calls} \approx 42.4\ \text{million invocations/month}.$$
Reading it: below ~42M calls/month, serverless is cheaper (you pay near-zero when idle). Above it, the steady flat server wins. WHY: serverless cost is a straight line through the origin (); the server is a flat horizontal line (\150N^{*}$.
Exercise 5.2 — The degenerate case:
For one month a serverless function is never invoked (). What does each cost model give, and what does this reveal about the deepest reason serverless exists?
Recall Solution
- Serverless: \text{Cost} = 0 \times c = \0$. Idle cost is exactly zero.
- Reserved server: still $150 — you pay 24/7 whether or not anything runs.
WHY this is the soul of serverless: most servers sit idle most of the time yet bill continuously. Serverless makes idle cost vanish, so it wins hardest for bursty / rarely-triggered workloads. The flip side: when load is huge and steady, that per-request fee accumulates past the flat server (Exercise 5.1), so serverless loses.
Exercise 5.3 — Steel-man the monolith at scale
A senior engineer at a 200-person company says: "We should keep our modular monolith, not go microservices." Give the strongest correct case for their position, and the single condition that would flip your recommendation.
Recall Solution
Strongest case for the monolith:
- One deploy unit and one database means no distributed transactions, no eventual-consistency bugs, and a real stack trace across the whole request — debugging stays cheap.
- Modular internal boundaries already give low coupling / high cohesion, so most maintainability benefits are had without the network cost.
- Fewer moving parts = lower operational cost (no N-service deploy/monitor pipeline).
The condition that flips it: when one module genuinely needs independent deployment or independent (horizontal) scaling that the shared process cannot provide — e.g. a hot Search path needing 10× replicas, or a team blocked because every release couples to everyone else's. Then, and only then, split that boundary out — start with the smallest split that relieves the real pressure. See Monolith vs Microservices.
Recall Quick self-check clozes
A style that cuts along a business capability with its own database is ::: microservices. Direct wiring of producers to consumers scales like ::: . Through a broker it scales like ::: . Serverless idle cost when is ::: exactly zero. Layers primarily improve ::: maintainability, not raw scalability.