Skip to main content

Adapter

What This Concept Is

Adapter is a structural pattern that reconciles two interfaces that should be collaborators but were not designed together. It wraps an existing object (the adaptee) and exposes a different interface (the target) that your client code already expects.

Two flavors:

  • Object adapter (almost always correct): the adapter holds the adaptee as a field and delegates.
  • Class adapter: the adapter inherits from both target and adaptee; uses multiple inheritance; language-dependent and fragile.

The problem it absorbs: you cannot change the adaptee (third-party library, legacy service, hardware SDK, vendor stub) and you do not want every caller to speak its dialect.

Why It Matters Here

Adapter is the most practical pattern in this module. Real systems live surrounded by other people's code: HTTP clients, payment providers, ORMs, logging frameworks, OS APIs. Wrapping each external boundary in an Adapter keeps the adaptee's types, exceptions, and quirks from leaking into your domain.

Adapter is also the textbook first layer of an anti-corruption layer: the boundary between your model and a model you did not choose.

Concrete Example

// Target interface your domain already speaks
interface PaymentGateway {
charge(amountCents: number, token: string): Promise<{ id: string }>;
}

// Adaptee: a third-party SDK with a different shape
class StripeClient {
async createCharge(opts: {amount: number; source: string; currency: 'usd'}) {
// ... returns { charge_id: string, ... }
}
}

// Object adapter: your interface in, adaptee out
class StripePaymentGateway implements PaymentGateway {
constructor(private readonly stripe: StripeClient) {}
async charge(amountCents: number, token: string) {
const r = await this.stripe.createCharge({
amount: amountCents, source: token, currency: 'usd'
});
return { id: r.charge_id }; // normalize result
}
}

Structural sketch:

  Client --> Target (PaymentGateway)
^
|
|
+--------------+ +----------+
| Adapter |------->| Adaptee |
| (Stripe...) | | (Stripe) |
+--------------+ +----------+

Your domain talks only to PaymentGateway. If you move to a different provider, you swap the Adapter, not the domain.

Common Confusion / Misconception

Adapter vs Facade is the sharpest confusion in this module:

  • Adapter reshapes one interface into a different interface of roughly the same level. Intent: compatibility.
  • Facade simplifies a subsystem's many interfaces into a single smaller one. Intent: convenience.

A Stripe wrapper that exposes charge() is an Adapter. A wrapper that hides an entire home-theater subsystem behind watchMovie() is a Facade.

Adapter is also not a Proxy (same interface, controls access) and not a Decorator (same interface, adds behavior). Adapter's defining property is that the interfaces differ.

How To Use It

  1. Identify the boundary. Circle every import from a third-party package in your domain layer -- those are your candidates.
  2. Define the target interface from your domain's point of view, in your domain's vocabulary.
  3. Write the Adapter: take the adaptee by constructor, translate arguments and results, map exceptions.
  4. Keep the Adapter thin. It should not contain business logic; if it does, that logic wants to be in the domain.
  5. Inject the target interface into consumers; do not let the adaptee type escape.

Check Yourself

  1. What is the single test that tells you whether a class is really an Adapter or whether it is becoming a Facade?
  2. Why does letting adaptee types leak into your domain undo the point of the pattern?
  3. When is it worth using a class adapter instead of an object adapter?

Mini Drill or Application

Pick a third-party SDK you have used in any language (HTTP, logging, queue, payment). Do three things:

  1. Write the target interface in the vocabulary of your domain, not the SDK's.
  2. Implement an Adapter that translates in both directions, including one exception mapping.
  3. Swap the underlying SDK for a fake or second provider and prove your domain code did not change.

Read This Only If Stuck