π§± Design Patterns β
Classic and modern patterns for solving recurring software problems.
Creational patterns β
Factory β
Create objects without specifying the exact class.
typescript
function createNotification(type: 'email' | 'sms' | 'push'): Notification {
switch (type) {
case 'email': return new EmailNotification()
case 'sms': return new SmsNotification()
case 'push': return new PushNotification()
}
}Builder β
Construct complex objects step by step.
typescript
const query = new QueryBuilder()
.select('name', 'email')
.from('users')
.where('active', true)
.orderBy('name')
.build()Singleton β
Single shared instance (use sparingly β prefer dependency injection).
Structural patterns β
Adapter β
Wrap an interface to match another expected interface. Useful for integrating third-party libraries.
Decorator β
Add behavior to objects dynamically.
typescript
const logger = withRetry(withTimeout(httpClient, 5000), 3)Facade β
Provide a simple interface to a complex subsystem.
Behavioral patterns β
Strategy β
Swap algorithms at runtime.
typescript
interface PricingStrategy {
calculate(order: Order): number
}
// StandardPricing, PremiumPricing, DiscountPricingObserver / Event Emitter β
Notify subscribers when state changes.
Chain of Responsibility β
Pass request through a chain of handlers (middleware pattern).
Modern patterns in practice β
| Pattern | Real-world example |
|---|---|
| Factory | API client creation based on provider |
| Builder | Query builders, form builders |
| Strategy | Payment processors, auth strategies |
| Observer | Event systems, webhooks |
| Middleware | Express/Koa middleware chain |
| Repository | Data access abstraction |
| Decorator | Logging, caching, retry wrappers |
When to use patterns β
- When you recognize a recurring problem
- When the pattern simplifies the code
- NOT when it adds unnecessary abstraction
- NOT to impress β use the simplest solution that works