Skip to content

🧱 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, DiscountPricing

Observer / Event Emitter ​

Notify subscribers when state changes.

Chain of Responsibility ​

Pass request through a chain of handlers (middleware pattern).

Modern patterns in practice ​

PatternReal-world example
FactoryAPI client creation based on provider
BuilderQuery builders, form builders
StrategyPayment processors, auth strategies
ObserverEvent systems, webhooks
MiddlewareExpress/Koa middleware chain
RepositoryData access abstraction
DecoratorLogging, 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

Pergame Knowledge Base