15 minlesson

Real-World Adapter Patterns

Real-World Adapter Patterns

API Version Adapter

Adapt between API versions:

typescript
1interface ShippingServiceV2 {
2 createShipment(request: ShipmentRequestV2): ShipmentResponseV2;
3}
4
5// Adapt old V1 API to new V2 interface
6class V1toV2Adapter implements ShippingServiceV2 {
7 constructor(private v1Service: ShippingServiceV1) {}
8
9 createShipment(request: ShipmentRequestV2): ShipmentResponseV2 {
10 const v1Request = this.downgradeRequest(request);
11 const v1Response = this.v1Service.createShipment(v1Request);
12 return this.upgradeResponse(v1Response);
13 }
14
15 private downgradeRequest(v2: ShipmentRequestV2): ShipmentRequestV1 {
16 return {
17 from: v2.origin,
18 to: v2.destination,
19 weight: v2.packages[0]?.weight ?? 0
20 };
21 }
22}

Data Format Adapter

Convert between data formats:

typescript
1interface AddressNormalizer {
2 normalize(input: unknown): NormalizedAddress;
3}
4
5class XMLAddressAdapter implements AddressNormalizer {
6 normalize(xml: string): NormalizedAddress {
7 const parsed = parseXML(xml);
8 return {
9 street: parsed.querySelector('street')?.textContent ?? '',
10 city: parsed.querySelector('city')?.textContent ?? '',
11 // ...
12 };
13 }
14}
15
16class JSONAddressAdapter implements AddressNormalizer {
17 normalize(json: string): NormalizedAddress {
18 const data = JSON.parse(json);
19 return {
20 street: data.address_line_1,
21 city: data.city_name,
22 // Map different field names
23 };
24 }
25}

Event System Adapter

Adapt between event systems:

typescript
1interface EventEmitter {
2 on(event: string, handler: Function): void;
3 emit(event: string, data: unknown): void;
4}
5
6// Adapt DOM events to custom event system
7class DOMEventAdapter implements EventEmitter {
8 constructor(private element: HTMLElement) {}
9
10 on(event: string, handler: Function): void {
11 this.element.addEventListener(event, (e) => handler(e.detail));
12 }
13
14 emit(event: string, data: unknown): void {
15 this.element.dispatchEvent(new CustomEvent(event, { detail: data }));
16 }
17}

Testing with Adapters

Create mock adapters for testing:

typescript
1class MockShippingAdapter implements ShippingService {
2 public calls: Array<{ from: Address; to: Address }> = [];
3
4 getRate(from: Address, to: Address): Rate {
5 this.calls.push({ from, to });
6 return { amount: 10.00, currency: 'USD' };
7 }
8
9 createLabel(): Label {
10 return { trackingNumber: 'MOCK123', url: 'http://mock' };
11 }
12}
13
14// In tests
15const mockAdapter = new MockShippingAdapter();
16const service = new OrderService(mockAdapter);
17await service.processOrder(order);
18expect(mockAdapter.calls).toHaveLength(1);

Summary

Adapters are essential for:

  • Third-party API integration
  • Legacy system migration
  • Data format conversion
  • Testing isolation