Adapter converts an interface a client already depends on; Facade hides a whole subsystem's complexity behind one simple entry point. Built through a third-party PaymentGateway wrapper.
Published September 22, 2026
Both patterns hide something behind an interface — but they solve opposite-shaped problems. Adapter makes one existing interface look like another; Facade makes many things look like one.
The canonical trigger: your code depends on interface A, but you need to use a third-party (or legacy) class that implements interface B — and you can't change either one.
// Your internal interface, used everywhere in your codebase
interface PaymentProcessor {
void charge(String userId, double amount);
}
// Third-party SDK — a completely different shape, you don't own this code
class StripeSdkClient {
void createCharge(int amountInCents, String customerToken) { /* Stripe's own API */ }
}
// Adapter: makes StripeSdkClient satisfy PaymentProcessor
class StripeAdapter implements PaymentProcessor {
private final StripeSdkClient stripeClient;
StripeAdapter(StripeSdkClient stripeClient) { this.stripeClient = stripeClient; }
@Override
public void charge(String userId, double amount) {
int cents = (int) Math.round(amount * 100); // unit conversion the two APIs disagree on
String token = lookupStripeToken(userId); // concept translation: userId -> Stripe's customer token
stripeClient.createCharge(cents, token);
}
private String lookupStripeToken(String userId) { /* ... */ return "tok_..."; }
}
Every other class in the codebase depends only on PaymentProcessor — swapping payment providers later means writing a new adapter, not touching any calling code. This is Dependency Inversion in action (see the Dependency Inversion lesson): the adapter is exactly the piece that lets a high-level PaymentProcessor-consuming class stay decoupled from a specific low-level SDK.
Where Adapter translates between two interfaces, Facade doesn't translate anything — it just hides a subsystem's internal complexity (multiple classes, a specific call order, error handling) behind one simplified method.
// The complex subsystem — each class has real responsibilities, correctly separated (SRP)
class InventoryService { boolean reserve(String sku, int qty) { /* ... */ return true; } }
class PaymentService { String charge(String userId, double amount) { /* ... */ return "txn_1"; } }
class ShippingService { void scheduleDelivery(String orderId) { /* ... */ } }
class NotificationService { void sendConfirmation(String userId) { /* ... */ } }
// Facade: one call for the common case, hiding the orchestration
class CheckoutFacade {
private final InventoryService inventory = new InventoryService();
private final PaymentService payment = new PaymentService();
private final ShippingService shipping = new ShippingService();
private final NotificationService notifications = new NotificationService();
void placeOrder(String userId, String sku, int qty, double amount) {
if (!inventory.reserve(sku, qty)) throw new IllegalStateException("Out of stock");
String txnId = payment.charge(userId, amount);
shipping.scheduleDelivery(txnId);
notifications.sendConfirmation(userId);
}
}
Callers of CheckoutFacade.placeOrder(...) don't need to know the correct call order, or that four separate services are involved at all. Critically, the Facade doesn't replace the subsystem's own well-factored classes (each still has a single responsibility) — it just adds a simplified entry point on top for the common case, while advanced callers can still reach the individual services directly if they need finer control.
Adapter's job is interface translation — same underlying behavior, different shape, because you're bridging two things that were never designed to work together. Facade's job is complexity hiding — the subsystem behind it was designed by you, cooperatively, and the facade is just an ergonomic simplification, not a translation between incompatible contracts.
Q: Can Adapter wrap more than method-signature differences — e.g. different error-handling conventions?
A: Yes — a common real case is adapting a third-party API that returns error codes into one that throws your codebase's own exception types, or adapting a callback-based API into a CompletableFuture-returning one. The translation isn't limited to parameter shapes.
Q: Does a Facade violate the Single Responsibility Principle by touching four services? A: No — SRP is about a class having one reason to change; the Facade's one reason to change is "the checkout orchestration sequence changes," which is a single, cohesive responsibility (see Single Responsibility & Open/Closed), distinct from each underlying service's own responsibility.
Q: When would you need two Adapters for the same third-party class? A: When two different parts of your codebase depend on two different internal interfaces that the same third-party class needs to satisfy — each adapter translates to a different target shape, since Adapter is defined by the pair of interfaces it bridges, not just the source class.
Q: Is a Facade the same as a Service Layer in a typical backend architecture? A: Conceptually very similar — a service-layer method that coordinates several repositories/lower-level services behind one method signature is Facade applied at the application-architecture level, not just within a single class.