15 minlesson

Real-World Facade Examples

Real-World Facade Examples

API Gateway as Facade

typescript
1// Multiple microservices
2class OrderService { }
3class InventoryService { }
4class PaymentService { }
5class ShippingService { }
6class NotificationService { }
7
8// API Gateway facade
9class OrderGateway {
10 async placeOrder(request: OrderRequest): Promise<OrderResponse> {
11 // Coordinate multiple services
12 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);
17
18 return { orderId: order.id, trackingNumber: shipment.tracking };
19 }
20}

Third-Party SDK Facade

typescript
1// Complex third-party shipping SDK
2import { FedExShipping, UPSWorldShip, DHLExpress } from 'carrier-sdks';
3
4class CarrierFacade {
5 async getQuote(shipment: ShipmentRequest): Promise<Quote[]> {
6 const quotes: Quote[] = [];
7
8 // FedEx - complex API
9 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));
16
17 // UPS - different API structure
18 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));
23
24 return quotes.sort((a, b) => a.price - b.price);
25 }
26
27 private toFedExAddress(addr: Address): FedExAddress { /* ... */ }
28 private toFedExPackage(pkg: Package): FedExPackage { /* ... */ }
29 private toUPSShipment(shipment: ShipmentRequest): UPSShipment { /* ... */ }
30}

Database Facade

typescript
1// Raw database operations are complex
2class DatabaseFacade {
3 private pool: ConnectionPool;
4 private cache: RedisCache;
5
6 async findOrderWithDetails(orderId: string): Promise<OrderDetails> {
7 // Check cache first
8 const cached = await this.cache.get(`order:${orderId}`);
9 if (cached) return cached;
10
11 // Complex query across multiple tables
12 const order = await this.pool.query(`
13 SELECT o.*, c.*, s.*
14 FROM orders o
15 JOIN customers c ON o.customer_id = c.id
16 LEFT JOIN shipments s ON o.id = s.order_id
17 WHERE o.id = $1
18 `, [orderId]);
19
20 const items = await this.pool.query(`
21 SELECT oi.*, p.*
22 FROM order_items oi
23 JOIN products p ON oi.product_id = p.id
24 WHERE oi.order_id = $1
25 `, [orderId]);
26
27 const result = this.assembleOrderDetails(order.rows[0], items.rows);
28
29 // Cache for 5 minutes
30 await this.cache.set(`order:${orderId}`, result, 300);
31
32 return result;
33 }
34}

Event Handling Facade

typescript
1// Multiple event systems
2class EventFacade {
3 constructor(
4 private kafka: KafkaProducer,
5 private sns: SNSClient,
6 private websocket: WebSocketServer
7 ) {}
8
9 async publishOrderEvent(event: OrderEvent): Promise<void> {
10 // Publish to Kafka for async processing
11 await this.kafka.send({
12 topic: 'orders',
13 messages: [{ value: JSON.stringify(event) }]
14 });
15
16 // Publish to SNS for mobile push
17 await this.sns.publish({
18 TopicArn: process.env.ORDER_EVENTS_TOPIC,
19 Message: JSON.stringify(event)
20 });
21
22 // Push to connected websocket clients
23 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

typescript
1// Easy to test - mock the whole subsystem
2describe('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 };
13
14 const facade = new ShippingFacade(mockDeps);
15 const result = await facade.ship(testOrder);
16
17 expect(result.trackingNumber).toBe('TRACK123');
18 expect(mockDeps.addressValidator.validate).toHaveBeenCalledWith(testOrder.address);
19 });
20});

Anti-Patterns

typescript
1// DON'T: Facade that does too much
2class GodFacade {
3 async doEverything(input: any): Promise<any> {
4 // Hundreds of lines of logic
5 // This is a service, not a facade
6 }
7}
8
9// DON'T: Facade that exposes internals
10class LeakyFacade {
11 getConnection(): DatabaseConnection {
12 return this.pool.getConnection(); // Leaking implementation
13 }
14}
15
16// DO: Focused facade with clear purpose
17class 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.