Backend Architecture
Middleware and DI: wire a testable service
Reading about the two axes is not the same as wiring them. Build a small service where the middleware pipeline is deliberately ordered, the object graph is assembled in one composition root, every scope choice is justified, and the testing seam is real — then prove each property with a test or a measurement.
Turn the unit’s mental model into a working service: a correct, ordered middleware pipeline; a composition root with no hidden new in business logic; deliberate singleton/request/transient choices; a seam you exercise with both a fake and a boundary mock; and evidence that the ordering and scopes behave as intended under load.
Build a small HTTP service (Express, Koa, Fastify, or NestJS) with one real domain use-case (e.g. place an order or create a user) whose middleware pipeline and dependency graph are both correct by design — then prove correctness with tests and a short load run, not assertion.
- A request flows end-to-end through the ordered pipeline; an unauthenticated or rate-limited request is rejected before the expensive body-parsing step, demonstrated by a test or a log/trace.
- No business-logic class constructs its own collaborators — the entire graph is assembled in the composition root, verifiable by grepping the domain code for new on injected types.
- The state-based test survives a behavior-preserving refactor (e.g. splitting one save into two) while the boundary mock still verifies the external call — showing you mocked the boundary and faked what you own.
- Every provider's scope is justified, and a short note explains why no singleton holds per-request state and why any request-scoped provider was unavoidable.
- Introduce a deliberate circular dependency between two services, observe the container's resolution error, then remove the cycle by extracting a shared third class — and write up why forwardRef would only have hidden it.
- Add a request-scoped provider deep in the graph, load-test before and after, and measure the allocation/latency cost of scope bubbling versus passing the value as an argument.
- Switch the eager/lazy startup mode (or simulate a missing dependency) and show that eager instantiation turns a misconfiguration into a loud boot-time crash instead of a quiet first-request failure.
- Port the same service to a second framework (e.g. Express to Fastify) and compare how the middleware model and encapsulation differ for the same pipeline.
This is the loop you run on every real backend: order the pipeline so cheap rejections come first and one terminal error handler catches the rest; assemble the graph in one composition root so no logic class constructs its own collaborators; choose each scope deliberately and keep singletons stateless; and use the seam to fake what you own and mock the boundaries you don’t. Doing it once on a small service makes the two axes — request and wiring — automatic when you read a production codebase.