“Structural design patterns organize relationships between entities. The Adapter pattern converts the interface of a class into another interface clients expect. The Decorator pattern attaches additional responsibilities to an object dynamically without subclassing. The Facade pattern provides a unified, high-level interface to a complex subsystem.”
Composing objects into larger structures using Adapters for incompatible interfaces, Decorators for dynamic behavior wrapping, and Facades for simplified APIs.
// Decorator Pattern: Adding Logging & Caching to Data Service
export interface IDataService {
fetchData(id: string): Promise<string>;
}
export class CoreDataService implements IDataService {
async fetchData(id: string): Promise<string> {
return `DataPayload for ${id}`;
}
}
// Decorator adding Logging & Telemetry dynamically
export class LoggingDataDecorator implements IDataService {
constructor(private readonly inner: IDataService) {}
async fetchData(id: string): Promise<string> {
console.log(`[LOG] Fetching data for id: ${id}`);
const start = Date.now();
const result = await this.inner.fetchData(id);
console.log(`[LOG] Completed in ${Date.now() - start}ms`);
return result;
}
}Adapter: Wrap legacy or 3rd-party class inside an adapter class implementing your target domain interface
Decorator: Wrap component class inside a decorator implementing the same interface, delegating calls while adding pre/post behavior
Facade: Bundle complex multi-class orchestrations into a single streamlined gateway method
Decorators follow the Open/Closed Principle by adding caching, logging, or metrics wrappers without altering core domain business logic.