{} The Go Reference

Structural pattern · Gang of Four · Beginner

Facade

Provide a single, simplified interface over a complex subsystem, so clients don't have to orchestrate its parts.

Structural Beginner ⏱ 3 min read Complete

🛎️ Analogy

At a hotel, you call the concierge for “dinner reservation, taxi at 7, and theatre tickets.” You don’t phone the restaurant, the cab company and the box office yourself. The concierge is a facade — one friendly interface in front of many services working together.

The problem

Placing an order really means: check inventory, then charge the customer, then (maybe) reserve shipping. If every caller wires those steps together, the orchestration logic is duplicated and clients are coupled to every subsystem. A Facade puts that workflow behind a single PlaceOrder method.

Structure

classDiagram
class OrderService {
  -pay Payment
  -stock Inventory
  +PlaceOrder(user, product, qty)
}
class Payment {
  <<interface>>
  +Charge(user, amount)
}
class Inventory {
  <<interface>>
  +InStock(product, qty)
}
OrderService --> Payment
OrderService --> Inventory
note for OrderService "Clients call one method; the facade runs the multi-step dance."

How it works

sequenceDiagram
participant C as Client
participant F as OrderService (facade)
participant I as Inventory
participant P as Payment
C->>F: PlaceOrder(user, product, qty)
F->>I: InStock(product, qty)?
I-->>F: true
F->>P: Charge(user, total)
P-->>F: ok
F-->>C: order placed

Idiomatic Go

The facade is a struct holding its subsystems as small interfaces — which keeps it decoupled and trivially testable with fakes. Edit and Run:

facade.go — editable & runnable
package main

import "fmt"

// Subsystem contracts (small interfaces).
type Payment interface{ Charge(user string, amount float64) error }
type Inventory interface{ InStock(product string, qty int) bool }

// Concrete (fake) subsystems.
type stripe struct{}
func (stripe) Charge(user string, amount float64) error {
fmt.Printf("charged %s $%.2f\n", user, amount)
return nil
}

type warehouse struct{}
func (warehouse) InStock(product string, qty int) bool { return qty <= 10 }

// Facade: one method hides the orchestration.
type OrderService struct {
pay   Payment
stock Inventory
}

func (o OrderService) PlaceOrder(user, product string, qty int, price float64) error {
if !o.stock.InStock(product, qty) {
	return fmt.Errorf("%s: not enough stock", product)
}
if err := o.pay.Charge(user, price*float64(qty)); err != nil {
	return err
}
fmt.Printf("order placed: %d x %s\n", qty, product)
return nil
}

func main() {
svc := OrderService{pay: stripe{}, stock: warehouse{}}
if err := svc.PlaceOrder("ada", "keyboard", 2, 49.99); err != nil {
	fmt.Println("error:", err)
}
}

🐹 The facade is just dependency injection

Notice there’s nothing exotic here — a struct with interface fields and a high-level method. Because the subsystems are interfaces, your tests inject fakes and assert on the orchestration. “Facade” is mostly a name for good, coarse-grained API design over a subsystem.

Design principle: Least Knowledge (Law of Demeter)

The Facade is the clearest expression of the Principle of Least Knowledgetalk only to your immediate friends. Without it, a caller chains into the guts of every subsystem (order.Inventory().Warehouse(3).Reserve(...)), coupling itself to structures it shouldn’t know. The facade gives the client one friend to call, so the subsystem’s shape can change freely behind it.

That’s also the line between Facade and its neighbours: a Mediator coordinates peers that talk back through it (two-way), while a facade is a one-way simplification whose subsystems don’t even know it exists; an Adapter changes one type’s interface rather than simplifying many.

In the standard library

  • http.Get / http.Post are facades over Client, Transport, and Request construction.
  • log.Println and friends are a facade over the default Logger.
  • os.ReadFile hides open + read + close behind one call.

Pitfalls

⚠️ Beware the god-facade

A facade should expose the common path, not every knob. When it grows a method for every possible operation, it becomes a bottleneck and a god-object. Keep it focused; let advanced callers reach the subsystems directly when they truly need to.

When to use it — and when not

✅ Reach for it when

  • A common task touches several subsystems (inventory, payment, shipping) and you want one entry point.
  • You want to decouple clients from the internals so the subsystem can change behind a stable surface.
  • You're wrapping a messy or legacy API and want to expose just the parts people actually need.

⛔ Think twice when

  • Clients genuinely need fine-grained control of the subsystem — a facade would hide what they require.
  • The 'facade' keeps growing until it's a god-object that does everything.

Check your understanding

Score: 0 / 5

1. What is a Facade's job?

A facade orchestrates several subsystem calls behind one method. It simplifies (many → one); it doesn't translate interfaces (that's Adapter) or add layers (Decorator).

2. In Go, what does a Facade usually look like?

Idiomatically it's a struct whose fields are the subsystem dependencies (as small interfaces, which also makes it testable), exposing coarse methods like PlaceOrder.

3. How does Facade differ from Adapter?

Facade is about reducing complexity across a subsystem; Adapter is about interface compatibility for a single object.

4. Which design principle does a Facade most directly serve?

The Law of Demeter says 'talk only to your immediate friends.' A facade gives clients exactly one friend to talk to, so they don't chain calls into the guts of inventory, payment, and shipping — cutting coupling to the subsystem's internals.

5. Facade vs Mediator — how do they differ?

A facade points one way: client → facade → subsystem, and the subsystem doesn't know the facade exists. A mediator sits between colleagues that send messages back through it, decoupling a many-to-many mesh into a hub.

Comments

Sign in with GitHub to join the discussion.