We use analytics cookies.We use cookies to analyze site traffic and improve your experience.
Loading...
System Design Masterclass · Microservices Architecture
Free preview: the first 2 parts of this 8-part lesson.
Already bought the course? Sign in to read the whole lesson.
this lesson is part of
System Design Masterclass
Unlock this lesson and the 751 other paid lessons
$25 once, by PayPal
See the System Design courseread the rest of this lesson in
System Design Masterclass
Unlock this lesson and the 751 other paid lessons
$25 once, by PayPal
See the System Design coursein Microservices Architecture
This lesson is part of the full System Design Masterclass. Unlock it along with 769 other lessons for a one-time payment.
One-time payment by card or PayPal
Get Lifetime AccessOne-time payment: UPI, card or netbanking
Get Lifetime Access18 Foundation lessons are free, no payment needed. Try them now
It is easy and good
“It is really nice and good Please join the course”
If you have written Express.js middleware, you already know the Decorator pattern. You just did not know it had a name.
app.use(cors()); // Decorator 1: adds CORS headers
app.use(rateLimit()); // Decorator 2: adds rate limiting
app.use(authenticate()); // Decorator 3: adds auth check
app.use(logger()); // Decorator 4: adds request logging
app.get('/api/users', handler); // The actual handler
Each middleware wraps the next one. A request flows through CORS, then , then authentication, then logging, then finally reaches your handler. Each layer adds behavior without modifying the handler itself. You can add, remove, or reorder middleware without changing any of the other layers.
That is the Decorator pattern. You take an object, wrap it in another object that adds some behavior, and the outside world cannot tell the difference because the wrapper has the same interface as the original.
Java's I/O library is built entirely on decorators. BufferedInputStream(GZIPInputStream(FileInputStream("data.gz"))) stacks buffering on top of decompression on top of file reading. Each layer implements InputStream. Swap FileInputStream for SocketInputStream and the buffering and decompression decorators work identically. That is composition over inheritance in action.
The Decorator pattern attaches additional responsibilities to an object dynamically. It provides a flexible alternative to subclassing for extending functionality.
Instead of creating LoggingAuthenticatedRateLimitedCachedUserService (a class that does everything), you create small, focused wrappers:
| Layer | Adds | Cost |
|---|---|---|
UserService | Core business logic | Baseline |
LoggingDecorator(UserService) | Logs every method call | ~0.1 ms per call |
RetryDecorator(LoggingDecorator) | Retries on transient failure | 0 ms on success, 200-1600 ms on retry |
CacheDecorator(RetryDecorator) | Returns cached results when available | Sub-ms on hit, bypasses all inner layers |
Each decorator implements the same interface and wraps the next layer. You compose behavior by stacking decorators, not by creating massive subclasses.
With 4 optional behaviors (logging, auth, , metrics), subclassing produces 16 combinations (2^4). Decorators produce 4 simple wrappers that you mix and match at runtime. That math alone killed deep inheritance hierarchies in most modern codebases.