Backend Architecture
Tracing one request: every unit on a single code path
The cleanest way to see seven mechanisms as one system is to stop talking about them abstractly and follow a single request all the way down. Take a concrete one: POST /payments, charge a card, write the result. That request does not visit one unit — it touches all of them, in order, on one code path. It arrives on a socket the lifecycle unit taught you to accept and parse. It passes through the middleware chain that authenticates it and starts a timeout, with its dependencies handed in by the DI container. The handler runs on an event loop that must not block, so the database call is async; that call borrows a connection from a pool; the pool’s connection talks to a payment provider through a circuit breaker; the write it performs is guarded by an idempotency key so a retry cannot double-charge. And underneath the whole thing, a graceful-shutdown handler is waiting — if a SIGTERM lands while this request is in flight, every layer it touched has to unwind in the right order. One request, seven units, one continuous path. Trace it once and the “system, not a stack” claim from the last lesson stops being a slogan and becomes a thing you can point at.
The happy path, layer by layer
Follow POST /payments from socket to response and the units appear as a stack of layers, each handing off to the next:
- Accept and parse (lifecycle). The server accepts the connection, reads the request off the socket, and parses headers and body. Backpressure and body-size limits live here — a malformed or oversized body is rejected before it costs anything downstream.
- Middleware and DI. The request passes through the chain: logging, auth, rate-limit, and crucially the place where the timeout budget is set for the whole request. The DI container supplies the handler its dependencies — the payment client, the repository — already constructed and scoped to this request.
- Handler on the event loop (async I/O). The handler runs without blocking the loop. Every I/O call is awaited, so while this request waits on the database or the provider, the same loop serves thousands of other requests.
- Borrow a connection (pool). The database write needs a connection, so the handler acquires one from the pool — with an acquisition timeout, so a saturated pool fails fast instead of hanging forever.
- Call the provider through a breaker. The charge goes to an external payment provider, wrapped in a circuit breaker and its own timeout. If the provider is failing, the breaker short-circuits and the handler takes its fallback rather than piling on.
- Write idempotently. The result is persisted under an idempotency key, so if the client retries the same
POSTthe server returns the original result instead of charging twice. - Answer, then shutdown waits. The response is written back. If a SIGTERM arrived mid-charge, the shutdown handler has been holding the door: it stopped accepting new requests, kept this one alive, and will only tear down the pool and the loop once this request has answered.
The timeout budget threads through all of it
The single thread that ties these layers together is the deadline. The timeout set in middleware (step 2) is not just for the handler — it has to be divided among everything downstream. The pool acquisition timeout, the provider call timeout, and the database write timeout all have to fit inside the request’s total budget, with margin. If the request budget is 3 seconds, you cannot give the provider call a 3-second timeout, because then a slow acquire plus a slow write blows past the deadline with no time to answer. This is the lifecycle unit’s tail-latency lesson and the pooling unit’s acquisition-timeout lesson and the breaker unit’s call-timeout lesson, all reconciled on one request: the budgets are nested, not independent, and they must sum to less than the whole.
One path, one ordering
Notice the ordering is forced, not arbitrary. You parse before you route, acquire a connection before you use it, set the breaker around the call it guards, and write idempotently before you answer. And shutdown unwinds this same graph in reverse — stop accepting, drain in-flight, then close the pool last — which is exactly the reverse-dependency-order rule from the graceful-shutdown unit. The request path and the shutdown path are the same dependency graph read in opposite directions.
Why this works
Why does the timeout budget have to be divided down the stack instead of just setting one generous timeout on the whole request and letting each call take what it needs? Because a single outer timeout tells you that the request was too slow but gives every inner layer permission to consume the entire budget alone, which makes the failure both later and less diagnosable. If the request has 3 seconds and the provider call has no timeout of its own, a hung provider holds the connection, the loop slot, and the request itself for the full 3 seconds before the outer deadline fires — and during those 3 seconds that pooled connection is unavailable to everyone else, so one slow dependency quietly drains the pool. Nested budgets fix both problems: give pool acquisition a few hundred milliseconds, the provider call a second or so, the write its own slice, each with margin, so the innermost slow thing fails first, fast, and specifically, freeing its resources back to the system while there is still time to take a fallback and answer within the outer deadline. This is why the lifecycle, pooling, and breaker units each insisted on a timeout from their own angle — they were all describing one shared budget seen from different layers. The deeper idea is that a timeout is not a safety net you set once at the top; it is a resource-release contract enforced at every layer, and the contract only holds if the inner deadlines are strictly smaller than the outer one. Get the nesting wrong and every other mechanism degrades: the pool can’t protect itself, the breaker can’t see a clean timeout to count, and shutdown can’t predict how long a drain will take.
| Step | Layer | Unit | Guard on this step |
|---|---|---|---|
| Accept and parse | Lifecycle | 01 | Body-size limit, backpressure |
| Auth, set deadline | Middleware / DI | 02 | Request timeout budget |
| Run handler | Event loop | 03 | Never block the loop |
| Borrow connection | Pool | 04 | Acquisition timeout |
| Call provider | Breaker | 06 | Breaker + call timeout |
| Persist result | Idempotency | 05 | Idempotency key |
| Answer / unwind | Shutdown | 07 | Drain in-flight, close pool last |
A POST /payments request has a 3-second total budget set in middleware. The provider call is given its own 3-second timeout with no smaller inner deadlines. What goes wrong?
How does the request path relate to the graceful-shutdown teardown path?
Order one POST /payments request as it descends the stack:
- 1 Accept the connection and parse the body (lifecycle), enforcing size limits
- 2 Run middleware: auth, then set the request's total timeout budget; DI supplies dependencies
- 3 Acquire a pooled connection with an acquisition timeout inside the budget
- 4 Call the provider through a circuit breaker, then persist the result under an idempotency key
- 01Walk POST /payments down the stack and name the guard at each layer.
- 02Why must the timeout budget be nested down the stack, and how does that connect the lifecycle, pooling, and breaker units?
To see the system instead of the stack, you follow one request — POST /payments — from socket to response, and the seven units line up as one ordered path: accept and parse, middleware and DI set the deadline and wire dependencies, the handler runs async on the loop, it borrows a pooled connection, calls the provider through a breaker, writes under an idempotency key, answers, and a shutdown handler waits underneath the whole time. The deadline is the thread that stitches the layers together: the request budget set in middleware must be divided so every inner timeout — acquisition, provider call, write — fits inside it with margin, nested rather than independent, so the innermost slow thing fails first and frees its resources in time to answer. The ordering is forced, and shutdown unwinds the exact same dependency graph in reverse — stop accepting, drain in-flight, close the pool last — so the request path and the teardown path are one graph read both ways. We have now seen all seven cooperate on the happy path; the next lesson turns the pressure up and watches them fail together, where one mechanism’s correct behaviour becomes the next one’s bad input.