Structural patterns compose objects into larger structures while keeping them flexible. Adapter, Decorator, and Facade solve the everyday problems of integrating and simplifying code.
Why: you constantly integrate code whose interface does not match what your system expects — an adapter wraps it and translates, so a third-party or legacy component fits your interface without changing either side. When: use it to integrate external libraries, legacy code, or swap vendors behind a stable interface. Where: the adapter is what lets Dependency Inversion span systems you do not control.
// Your app expects this interface:
interface Logger { log(msg: string): void }
// A third-party logger has a different shape you can't change:
class VendorLogger { writeLine(level: string, text: string) {} }
// Adapter translates your interface to theirs:
class VendorLoggerAdapter implements Logger {
constructor(private vendor: VendorLogger) {}
log(msg: string) { this.vendor.writeLine('INFO', msg) }
}
// Your code depends on Logger; the vendor stays behind the adapter.Why: you often need to add responsibilities (caching, logging, retry, auth) to an object without changing it or exploding into subclasses — a decorator wraps the object with the same interface and adds behavior, and decorators stack. When: layer cross-cutting concerns around a core object. Where: this is composition in action — it embodies open/closed by extending behavior from the outside.
interface DataSource { read(): string }
class FileSource implements DataSource { read() { return 'raw data' } }
// A decorator wraps a DataSource and adds behavior, same interface:
class CachingSource implements DataSource {
private cache?: string
constructor(private inner: DataSource) {}
read() { return (this.cache ??= this.inner.read()) }
}
// Stack them: new LoggingSource(new CachingSource(new FileSource()))
// Each layer adds one concern — the core object never changes.Why: a subsystem with many moving parts is hard to use and couples callers to its internals — a facade offers one simple, high-level interface that orchestrates the pieces behind it. When: provide a facade over a complex library or a cluster of services so clients depend on one clean entry point. Where: it reduces coupling and is often the public API of a module or bounded context.
// Behind the scenes: inventory, payment, shipping, notifications...
class OrderFacade {
constructor(
private inventory: Inventory, private payments: Payments,
private shipping: Shipping, private email: Email,
) {}
placeOrder(order: Order) { // ONE simple call for clients
this.inventory.reserve(order)
this.payments.charge(order)
this.shipping.schedule(order)
this.email.confirm(order)
}
}
// Clients call placeOrder() — they don't touch the four subsystems.