06 / DESIGN PATTERNS

Design Patterns

All twenty-three Gang of Four patterns as runnable JavaScript reference cards. Each card states the intent, the problem it solves, the sentence to say, where you have already used it in Node or React, and the pattern it is confused with. Every file ends with an ok: line so you know the examples really run.

Start here 23 pages · 3 groups

Creational

5 pages

How objects get made: factories, builders, prototypes and the one-instance rule.

Creational — how objects get made — intent and sightings
PatternIntentReal-world JS sightingFile
Abstract FactoryOne factory object yields a whole family of compatible productsKnex dialect clients; providers[name] kit (charge + refund + verify)creational/abstract-factory.js
BuilderAssemble a complex object step by step, then build()Knex/Kysely query builders; Zod schemas; superagent chainscreational/builder.js
Factory MethodDefer the new to a hook so a subclass/key picks the classdocument.createElement, http.createServer, React.createElementcreational/factory-method.js
PrototypeCreate new objects by cloning a configured instanceObject.create, structuredClone, spread copies in reducerscreational/prototype.js
SingletonExactly one instance, globally reachableNode module cache (module.exports = new Logger()), Redux store, processcreational/singleton.js
  1. Abstract Factory2 sections
  2. Builder2 sections
  3. Factory Method2 sections
  4. Prototype2 sections
  5. Singleton2 sections

Structural

7 pages

How objects are composed: wrappers, bridges, trees, proxies and shared state.

Structural — how objects are composed — intent and sightings
PatternIntentReal-world JS sightingFile
AdapterConvert an interface into the one clients already expectutil.promisify, axios adapters, Readable.toWeb, SDK wrappersstructural/adapter.js
BridgeSplit abstraction from implementation so both vary independentlyWinston logger × transports; react-reconciler × react-dom/nativestructural/bridge.js
CompositeTree of parts and wholes, all answering the same callsDOM, React element trees, ASTs, Express Router mountingstructural/composite.js
DecoratorWrap an object to add behavior; interface unchanged; stackablememoize/withRetry HOFs, React HOCs & React.memo, util.deprecatestructural/decorator.js
FacadeOne simple entry point in front of a messy subsystemfs.readFile, fetch/axios, express(), service-layer placeOrder()structural/facade.js
FlyweightShare immutable intrinsic state across huge numbers of objectsSymbol.for, cached Intl.*Format, string interningstructural/flyweight.js
ProxyStand-in that controls access: lazy, guard, cache, remoteES Proxy (Vue 3, MobX, Immer), ORM lazy relations, Promisestructural/proxy.js
  1. Adapter2 sections
  2. Bridge2 sections
  3. Composite2 sections
  4. Decorator2 sections
  5. Facade2 sections
  6. Flyweight2 sections
  7. Proxy2 sections

Behavioral

11 pages

How objects talk and divide responsibility: observers, strategies, commands, iterators and state machines.

Behavioral — how objects talk and divide responsibility — intent and sightings
PatternIntentReal-world JS sightingFile
Chain of ResponsibilityPass a request along a chain until one link handles itExpress/Koa middleware next(), axios interceptors, Redux middlewarebehavioral/chain-of-responsibility.js
CommandTurn a request into an object: queue, log, undo, replayRedux actions, BullMQ jobs, editor undo stacks, migrations up/downbehavioral/command.js
InterpreterGrammar as node classes; evaluate sentences by walking the treeRegExp, Mongo/Prisma where objects, JSON Logic, template enginesbehavioral/interpreter.js
IteratorWalk a collection sequentially without exposing its representationSymbol.iterator, generators, for await over streams, DB cursorsbehavioral/iterator.js
MediatorPeers talk through one coordinator instead of to each otherReact lifted state, Flux dispatcher, Socket.io rooms, form controllersbehavioral/mediator.js
MementoSnapshot state opaquely; restore it laterRedux DevTools time travel, history.state, DB savepoints, git stashbehavioral/memento.js
ObserverSubject pushes changes to subscribed listenersEventEmitter, addEventListener, RxJS, store.subscribebehavioral/observer.js
StateBehavior changes with internal state; states drive transitionsXState, Promise states, readyState, order/upload status machinesbehavioral/state.js
StrategyInterchangeable algorithms behind one interface, picked at runtimesort(comparator), Passport strategies, payment/auth provider mapsbehavioral/strategy.js
Template MethodFixed algorithm skeleton; subclasses/options fill in the stepsNode stream _transform, React lifecycle, toJSON(), Jest hooksbehavioral/template-method.js
VisitorAdd operations over a node tree without touching node classesBabel/ESLint traverse, GraphQL visit, PostCSS walkers, JSON reviverbehavioral/visitor.js
  1. Chain of Responsibility2 sections
  2. Command2 sections
  3. Interpreter3 sections
  4. Iterator2 sections
  5. Mediator2 sections
  6. Memento2 sections
  7. Observer2 sections
  8. State2 sections
  9. Strategy2 sections
  10. Template Method2 sections
  11. Visitor3 sections

Confusables#

  • Strategy vs State — identical shape (context delegates to a swappable object). Strategy: the client picks the algorithm; strategies don't know each other. State: the state object decides the next state; behavior changes over time as transitions fire.
  • Decorator vs Proxy vs Adapter — Decorator: same interface, adds behavior, stacks freely. Proxy: same interface, controls access (lazy, guard, cache, remote), usually owns the subject's lifecycle. Adapter: changes the interface so an incompatible thing fits what you already have.
  • Factory Method vs Abstract Factory vs Builder — Factory Method: one product, one overridable creation hook. Abstract Factory: a family of related products from one factory object (so you can't mix Stripe charge with PayPal refund). Builder: one complex product assembled over many steps, then build().
  • Observer vs Mediator vs Pub/Sub — Observer: the subject holds its listener list and notifies them directly. Mediator: colleagues never reference each other; a hub holds the coordination logic and talks back to them. Pub/Sub: a broker/channel sits between publisher and subscribers so neither knows the other (often async, cross-process).
  • Composite vs Decorator — both are recursive wrappers with the component's interface. Composite has many children and aggregates (size of a folder). Decorator has exactly one wrappee and adds behavior.
  • Facade vs Adapter — Facade invents a new, simpler API over several objects (one-way; subsystems unaware). Adapter makes one object fit an existing API you don't want to change.
  • Command vs Strategy — Command is what to do: a request with its arguments, storable, queueable, undoable. Strategy is how to compute something: an interchangeable algorithm with no history.
  • Template Method vs Strategy — Template Method keeps the skeleton and varies steps via inheritance (or an options object of callbacks). Strategy varies the whole algorithm via composition — you pass a different object.

How to answer "what design patterns have you used?"#

Lead with the ones you actually ship, name them, and point at the code they live in:

  1. "Most Node services I build are a Chain of Responsibility at the edge — Express/Koa middleware with next() for auth, rate limiting, validation — and the app itself is wired as Observers on EventEmitters and streams."
  2. "Shared clients (Prisma, Redis, loggers) are module-scope Singletons; I keep them behind a factory so tests can inject a fake instead of fighting global state."
  3. "Payment and auth providers are Strategy objects — a plain map of { charge } / { verify } functions keyed by provider — and when a provider needs a full kit (charge + refund + webhook verify) that's an Abstract Factory."
  4. "Query and config objects use a Builder (Knex/Kysely style fluent chain ending in build()); third-party SDKs sit behind an Adapter so swapping Stripe for Braintree touches one file."
  5. "Cross-cutting behavior — retry, caching, logging — is Decorator via higher-order functions (withRetry(withCache(fn))), the same idea as React HOCs."
  6. "Anything paginated or streamed is an Iterator: Symbol.iterator and async generators so callers just for await."
  7. "Order and UI status flows are explicit State machines (transition tables / XState) so illegal transitions are impossible rather than untested; undo/redo is Command objects with execute/undo."
  8. Close with the trade-off: "I reach for the pattern when the duplication or the bug class is real, not up front — a switch with two cases doesn't need a Strategy yet."