Real-World Facade Examples
API Gateway as Facade
typescript1// Multiple microservices2class OrderService { }3class InventoryService { }4class PaymentService { }5class ShippingService { }6class NotificationService { }78// API Gateway facade9class OrderGateway {10 async placeOrder(request: OrderRequest): Promise<OrderResponse> {11 // Coordinate multiple services12 const inventory = await this.inventoryService.reserve(request.items);13 const payment = await this.paymentService.charge(request.payment);14 const order = await this.orderService.create(request, inventory, payment);15 const shipment = await this.shippingService.schedule(order);16 await this.notificationService.sendConfirmation(order, shipment);1718 return { orderId: order.id, trackingNumber: shipment.tracking };19 }20}
Third-Party SDK Facade
typescript1// Complex third-party shipping SDK2import { FedExShipping, UPSWorldShip, DHLExpress } from 'carrier-sdks';34class CarrierFacade {5 async getQuote(shipment: ShipmentRequest): Promise<Quote[]> {6 const quotes: Quote[] = [];78 // FedEx - complex API9 const fedex = new FedExShipping(this.credentials.fedex);10 const fedexResponse = await fedex.rateRequest({11 shipper: this.toFedExAddress(shipment.from),12 recipient: this.toFedExAddress(shipment.to),13 packages: shipment.packages.map(this.toFedExPackage)14 });15 quotes.push(...this.normalizeFedExRates(fedexResponse));1617 // UPS - different API structure18 const ups = new UPSWorldShip(this.credentials.ups);19 const upsResponse = await ups.Rate.request({20 Shipment: this.toUPSShipment(shipment)21 });22 quotes.push(...this.normalizeUPSRates(upsResponse));2324 return quotes.sort((a, b) => a.price - b.price);25 }2627 private toFedExAddress(addr: Address): FedExAddress { /* ... */ }28 private toFedExPackage(pkg: Package): FedExPackage { /* ... */ }29 private toUPSShipment(shipment: ShipmentRequest): UPSShipment { /* ... */ }30}
Database Facade
typescript1// Raw database operations are complex2class DatabaseFacade {3 private pool: ConnectionPool;4 private cache: RedisCache;56 async findOrderWithDetails(orderId: string): Promise<OrderDetails> {7 // Check cache first8 const cached = await this.cache.get(`order:${orderId}`);9 if (cached) return cached;1011 // Complex query across multiple tables12 const order = await this.pool.query(`13 SELECT o.*, c.*, s.*14 FROM orders o15 JOIN customers c ON o.customer_id = c.id16 LEFT JOIN shipments s ON o.id = s.order_id17 WHERE o.id = $118 `, [orderId]);1920 const items = await this.pool.query(`21 SELECT oi.*, p.*22 FROM order_items oi23 JOIN products p ON oi.product_id = p.id24 WHERE oi.order_id = $125 `, [orderId]);2627 const result = this.assembleOrderDetails(order.rows[0], items.rows);2829 // Cache for 5 minutes30 await this.cache.set(`order:${orderId}`, result, 300);3132 return result;33 }34}
Event Handling Facade
typescript1// Multiple event systems2class EventFacade {3 constructor(4 private kafka: KafkaProducer,5 private sns: SNSClient,6 private websocket: WebSocketServer7 ) {}89 async publishOrderEvent(event: OrderEvent): Promise<void> {10 // Publish to Kafka for async processing11 await this.kafka.send({12 topic: 'orders',13 messages: [{ value: JSON.stringify(event) }]14 });1516 // Publish to SNS for mobile push17 await this.sns.publish({18 TopicArn: process.env.ORDER_EVENTS_TOPIC,19 Message: JSON.stringify(event)20 });2122 // Push to connected websocket clients23 const connectedClients = this.websocket.getClientsByCustomer(event.customerId);24 for (const client of connectedClients) {25 client.send(JSON.stringify(event));26 }27 }28}
Testing with Facade
typescript1// Easy to test - mock the whole subsystem2describe('ShippingFacade', () => {3 it('creates shipment successfully', async () => {4 const mockDeps = {5 addressValidator: {6 validate: vi.fn().mockResolvedValue({ isValid: true, address: validAddress })7 },8 carrierService: {9 getRates: vi.fn().mockResolvedValue([{ carrier: 'FedEx', price: 10 }]),10 createLabel: vi.fn().mockResolvedValue({ trackingNumber: 'TRACK123' })11 }12 };1314 const facade = new ShippingFacade(mockDeps);15 const result = await facade.ship(testOrder);1617 expect(result.trackingNumber).toBe('TRACK123');18 expect(mockDeps.addressValidator.validate).toHaveBeenCalledWith(testOrder.address);19 });20});
Anti-Patterns
typescript1// DON'T: Facade that does too much2class GodFacade {3 async doEverything(input: any): Promise<any> {4 // Hundreds of lines of logic5 // This is a service, not a facade6 }7}89// DON'T: Facade that exposes internals10class LeakyFacade {11 getConnection(): DatabaseConnection {12 return this.pool.getConnection(); // Leaking implementation13 }14}1516// DO: Focused facade with clear purpose17class ShippingFacade {18 async ship(order: Order): Promise<ShipmentResult> { }19 async track(trackingNumber: string): Promise<TrackingInfo> { }20 async cancel(trackingNumber: string): Promise<CancelResult> { }21}
Summary
Facades appear everywhere in modern applications:
- API gateways
- SDK wrappers
- Database access layers
- Event publishers
- External service integrations
The key is to hide complexity while providing a simple, focused interface.