Composition and DI Clinic
Retrieval Prompts
- Describe Composite in one sentence. What property does it guarantee to clients?
- Describe Decorator in one sentence. What does it not change about the wrapped component?
- Describe Proxy in one sentence. Name two kinds and their intents.
- Describe Dependency Injection in one sentence. What is its opposite, and why is that usually worse?
- Describe a composition root in one sentence. Where does it live in a typical HTTP service?
Compare and Distinguish
Separate these pairs clearly:
- Decorator vs Proxy
- Composite vs Decorator
- DI vs service locator
- DI vs DI container
- Constructor injection vs setter injection
- Composition root vs "bean configuration"
Common Mistake Check
For each statement, identify the error:
- "We use a Decorator to translate our HTTP response into domain objects."
- "This class is a Proxy because it just forwards calls to another object."
- "Our
OrderServicelooks up itsPaymentGatewayfrom a service locator so we can swap it in tests." - "Constructor injection is too verbose, so we made all dependencies public fields and set them after construction."
- "Our composition root is a class that every service imports."
Mini Application
Build a small service with all three patterns and a composition root.
Step 1 -- Domain service
Write a NotificationService with one method, notifyUser(userId, template, data), that depends on:
- a
UserRepository(interface) - a
TemplateEngine(interface) - a
Transport(interface) - a
Clock(interface)
Use constructor injection. No new for any of these inside the class.
Step 2 -- Composite
Represent a Channel as Composite:
Channelinterface withsend(user, message)EmailChannel,SmsChannel(leaves)MultiChannel(composite) that delegates to a list of children
Step 3 -- Decorator pipeline
Wrap Transport in decorators:
LoggingTransport-- logs every sendRetryingTransport-- retries N times on failureRateLimitingTransport-- allows at most K/sec
Compose them in a specific order at the composition root and write one unit test that pins the ordering.
Step 4 -- Proxy
Add an AuditProxy that wraps the NotificationService and records each call. Use it in an integration test to assert outgoing calls; keep the real service untouched.
Step 5 -- Composition root
Write a single buildApp(env) function that:
- loads config once at the boundary
- constructs concrete infrastructure (in-memory repo, fake SMTP, fake SMS)
- wires decorators in a clear top-to-bottom order
- returns the domain service
Write a second buildTestApp() that swaps the fakes for even simpler test doubles.
Reflection
- Count the
newkeywords in your domain layer. The answer should be zero (except for value objects). - Delete the composition root temporarily and observe which call sites break. The fewer, the better.
- Name one decorator in your pipeline that you would remove if this were a prototype and justify keeping it.
Evidence Check
This page is complete only if you have:
- a service that uses only injected interfaces
- a working Composite tree
- a Decorator pipeline with at least three layers and an ordering test
- a Proxy exercising an access-control or auditing concern
- one composition root per runtime (prod and test) with zero cross-imports from domain code