I. Facade — Structural Pattern
The Facade pattern is a structural design pattern that provides a simplified interface to a complex subsystem. It doesn't add new functionality — it hides complexity behind a clean, easy-to-use API.
The name comes from architecture: a building's facade is the front face that visitors see, hiding all the internal structure behind it. Similarly, a Facade class is what the client interacts with, while the complexity of the underlying subsystem is tucked away.
Key properties:
- Simplified interface: expose only what the client needs, hide what they don't
- Loose coupling: the client depends on the Facade, not on the individual subsystem classes
- Doesn't replace the subsystem — advanced users can still access it directly
- Single entry point: one place to orchestrate complex multi-step operations
Use Facade when:
- A subsystem is complex and clients only need a subset of its capabilities
- You want to layer your system with a clean boundary between tiers
- You want to reduce dependencies on internal implementation details
II. Real-world Example
A file storage system built on top of the Composite pattern:
- The underlying subsystem uses
composite.Folderandcomposite.File— powerful but verbose to use directly
StorageFacadewraps this complexity behind two simple methods:SaveFile()andListAll()
SaveFile("/docs", "readme.md")internally creates folders if they don't exist, normalizes paths, and appends the file to the right folder — all hidden from the client
ListAll()delegates toroot.Print()which uses Composite's recursive tree printing
- The client never touches
composite.Folderorcomposite.Filedirectly

III. Implementation
storage_facade.go — the facade
package facade
import (
"fmt"
"strings"
composite "github.com/structural-patterns/composite"
)
type StorageFacade struct {
root *composite.Folder
folders map[string]*composite.Folder
}
func NewStorageFacade(rootName string) *StorageFacade {
root := &composite.Folder{}
root.SetName(rootName)
return &StorageFacade{
root: root,
folders: map[string]*composite.Folder{"/": root},
}
}
func (s *StorageFacade) SaveFile(folderPath string, fileName string) {
folder := s.ensureFolder(folderPath)
file := &composite.File{}
file.SetName(fileName)
folder.Add(file)
}
func (s *StorageFacade) ListAll() {
s.root.Print()
}
func (s *StorageFacade) Summary() string {
return fmt.Sprintf("Storage root: %s", s.root.GetName())
}
func (s *StorageFacade) ensureFolder(folderPath string) *composite.Folder {
normalized := normalizePath(folderPath)
if folder, ok := s.folders[normalized]; ok {
return folder
}
segments := strings.Split(strings.Trim(normalized, "/"), "/")
currentPath := "/"
current := s.root
for _, segment := range segments {
currentPath = currentPath + segment + "/"
if folder, ok := s.folders[currentPath]; ok {
current = folder
continue
}
folder := &composite.Folder{}
folder.SetName(segment)
current.Add(folder)
s.folders[currentPath] = folder
current = folder
}
return current
}
func normalizePath(folderPath string) string {
if folderPath == "" || folderPath == "/" {
return "/"
}
path := folderPath
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
if !strings.HasSuffix(path, "/") {
path = path + "/"
}
return path
}IV. Explain the example above
Let's trace through a concrete usage:
s := NewStorageFacade("root")
s.SaveFile("/docs", "readme.md")
s.SaveFile("/docs", "changelog.md")
s.SaveFile("/src", "main.go")
s.ListAll()Output:
root
|--docs
|--readme.md
|--changelog.md
|--src
|--main.goStep by step:
NewStorageFacade("root")— creates the rootFolderand seeds thefoldersmap with"/": root
SaveFile("/docs", "readme.md")— callsensureFolder("/docs/")/docs/not in map → creates a newFolder{name: "docs"}, adds it toroot, caches it- Creates
File{name: "readme.md"}and callsfolder.Add(file)
SaveFile("/docs", "changelog.md")— callsensureFolder("/docs/")again/docs/already in map → returns cached folder directly, no new folder created- Adds
changelog.mdto the samedocsfolder
SaveFile("/src", "main.go")— same flow, createssrcfolder and addsmain.go
ListAll()— callsroot.Print()which uses the Composite pattern to recursively render the tree
The client wrote 4 lines. Behind the scenes: path normalization, map lookups, folder creation, Composite tree assembly, recursive printing — all handled by the Facade.
You can also use ensureFolder for deeply nested paths:
s.SaveFile("/src/handlers", "user.go")
// creates: root → src → handlers → user.go
// segments: ["src", "handlers"] — walks and creates each levelV. Conclusion
The Facade pattern is one of the most practical patterns in everyday software — you've almost certainly used it without naming it. Any time you create a service layer that wraps a complex repository, ORM, or third-party SDK behind clean methods, you're building a Facade.
Trade-offs:
- Good fit: complex subsystems with many moving parts; layered architecture where you want a clean API boundary; reducing client coupling to internal implementation
- Poor fit: when clients genuinely need fine-grained control over the subsystem — the Facade becomes a bottleneck; over-simplified Facades can hide information that callers actually need
One important note: Facade doesn't prevent direct access to the subsystem. It's a convenience, not a lock. Advanced clients can still use composite.Folder directly when they need to.
No pattern is universally superior. Use Facade when simplifying the interface genuinely reduces cognitive load for your callers.
VI. References
- Go Design Patterns — Mario Castro Contreras




