The Problem
An e-commerce platform needs to send different types of notifications (order confirmations, shipping updates) through different channels (email, SMS, push). Without the Bridge pattern, you would need a separate class for every combination — OrderConfirmationEmail, OrderConfirmationSms, ShippingUpdatePush, and so on — leading to a combinatorial explosion.
The Solution
The Bridge pattern splits the problem into two independent hierarchies: the abstraction (notification types) and the implementation (delivery channels). The abstraction holds a reference to an implementation object and delegates the actual sending to it. New notification types or channels can be added independently.
Structure
- Implementor Interface (
NotificationSender) — Defines thesend(to, subject, body)method. - Concrete Implementors (
EmailSender,SmsSender,PushSender) — Each implementsNotificationSenderfor a specific delivery channel. - Abstraction (
Notification) — Abstract class that holds a reference to aNotificationSenderand defines anotify(to)template method. - Refined Abstractions (
OrderConfirmationNotification,ShippingUpdateNotification) — Concrete subclasses that provide specific subject and body content. - Client (
BridgeController) — Resolves the appropriate sender and notification type at runtime.
Implementation
OrderConfirmationNotification builds a subject like “Order Confirmation - #ORD-123” with a thank-you body. ShippingUpdateNotification builds a subject with tracking information. These are independent from the delivery mechanism.
export interface NotificationResult {
channel: string;
recipient: string;
status: string;
}
export interface NotificationSender {
send(to: string, subject: string, body: string): NotificationResult;
} NestJS Integration
This implementation creates sender and notification instances on-demand within the controller based on request parameters, demonstrating that the Bridge pattern does not require all participants to be NestJS-managed singletons — the key structural relationship is established at request time. In a production system, the senders could be registered as NestJS providers.
When to Use
- You want to avoid a combinatorial explosion of classes when you have two or more independent dimensions of variation.
- You need to switch implementations at runtime (e.g., choosing email vs. SMS based on user preferences).
- Both the abstraction and its implementation should be extensible independently.
When NOT to Use
- There is only one dimension of variation — a simple interface and implementations will suffice.
- The abstraction and implementation are unlikely to change independently.
- The relationship is so tightly coupled that separating them would require constant coordination.
Related Patterns
Adapter
Convert the interface of a class into another interface clients expect.
Strategy
Define a family of algorithms, encapsulate each one, and make them interchangeable.
Abstract Factory
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.