Patterns and Distribution
Purpose
Stop a local design's guarantees from being assumed across a network. GoF patterns primarily
describe collaborating objects in one address space. Local calls can still partially mutate then
throw, block on I/O, or race; a process boundary additionally introduces an independent
failure/ambiguity domain, serialization and operational ownership. The patterns
whose names survive the crossing are the ones most likely to hide that it stopped holding.
What a boundary removes
This classification is conceptual; Java API references use Java 17. Inspect the target runtime,
codec, broker and persistence versions and effective configuration before applying their guarantees.
Inside one process Across a boundary
────────────────────────────────── ───────────────────────────────────
A call has one process failure domain It may commit remotely while the reply is lost
Latency follows local work/I/O Adds transport queues and independent tail latency
A reference is the object A copy; identity does not travel
Uniqueness is per class loader Uniqueness requires coordination
Order follows synchronization/API Broker/protocol/topology defines its scope
State can share memory State may be remote/replicated; consistency is a contract
Monotonic intervals are local Clock offset and rate assumptions need explicit treatment
One invocation; effects may partial Delivery may be at-most/at-least/effectively-once
Every transformation below follows from that table.
Classification
PROCESS-LOCAL — the guarantee stops at the JVM
Singleton uniqueness is per class loader, never per cluster
Flyweight references are shared; nothing crosses the wire
Iterator the cursor is in this process
Memento opacity/lifecycle is local unless a durable snapshot contract is added
BOUNDARY — the pattern manages a seam, and the seam may be a network
Adapter where a foreign model, vocabulary and failure stop
Proxy the pattern most able to hide that a call is remote
Facade coarse granularity is how round trips are saved
Bridge supported backends must satisfy the chosen contract honestly
INTERACTION — the pattern shapes who talks to whom
Command may become a message: schema, delivery and effect policies needed
Observer becomes pub/sub with broker-specific delivery and ordering
Mediator becomes an orchestrator, with its own availability
Chain becomes a workflow, failing at every step
ALGORITHM — largely unaffected; the choice may not be
Strategy the choice of partitioner, serialiser or retry policy
has system-wide effects
State needs durability if progress must survive restart
Template coordinate overall budget with each remote step's transport limits
Visitor the element set becomes a versioned contract
The transformations
| Local pattern |
Distributed form |
What must be added |
| Singleton |
Leader election / a lease |
Fencing tokens, or idempotency so overlap is harmless |
| Flyweight |
Distributed cache/content addressing—different mechanisms |
Invalidation, staleness, serialization and remote/local tiers |
| Iterator |
Pagination/cursor |
Strategy, bound, deadline, cancellation, mid-walk consistency |
| Memento |
Durable snapshot/checkpoint |
Schema identity, compatibility, consistency and corruption recovery |
| Observer |
Publish/subscribe |
Declared delivery/ordering; transactional bridge when committed changes must publish reliably |
| Command |
A message |
Schema identity, delivery/effect policy, deduplication where needed, terminal outcome |
| Mediator |
An orchestrator |
Required durable progress, deadlines, applicable compensation and availability budget |
| Chain |
A workflow |
Per-step failure and retry, redelivery semantics, partial-effect handling |
| Facade |
Remote facade, gateway or BFF when appropriate |
Contract, deployment, authentication, scaling and outage surface |
| Proxy |
A service client |
Deadlines, a failure vocabulary, bulk operations |
| State |
State machine within a distributed workflow |
Required persistence, timeout outcomes and duplicate policy; not automatically a saga |
| Composite |
Fan-out |
Concurrency, an overall deadline, a defined partial-failure result |
Decision rules
IF a requirement says "there must be only one"
THEN ask "one per what?" A static field gives one per class loader.
Cluster-wide singularity needs leader election, a lease, or a
design where multiplicity does not matter (gof-singleton,
leader-election).
IF a process-local limit is configured—a pool, limiter or cache
THEN model the aggregate across minimum/maximum dynamic replica count, rollout
overlap and sidecars. Simple multiplication is a scenario, not a stable invariant.
IF an interface designed against a local implementation is about to be
implemented remotely
THEN review the contract: suitable granularity, propagated deadline/context, cancellation
and named failure/unknown-outcome semantics may require change.
Otherwise a loop becomes N network calls (gof-proxy).
IF an in-process listener is being moved to a broker
THEN it is a redesign: six properties change at once — thread,
transaction, ordering, delivery, failure visibility, schema
(gof-observer).
IF an object is sent across a boundary
THEN a representation is serialized and reconstructed; reference identity does not
travel. Constructor/invariant behavior is codec-specific, and the representation
becomes a compatibility contract (rpc-and-api-contracts).
IF a pattern name is applied to a deployed component — "the gateway is
our facade", "the orchestrator is a mediator"
THEN the name is a metaphor, not a design. The component has
availability, authentication, scaling and an outage surface that
no class has.
IF the design question is really "where should this boundary be"
THEN it is not an object-design question at all
(distribution-boundaries).
IF duplicate effects are harmful
THEN choose among naturally idempotent operations, deduplication, fencing/coordination
and transactional authority. Dedup stores also fail/expire; no mechanism is universal.
The level confusion
Design pattern objects and classes inside one component
Component design modules, packages, release units
Architectural pattern how a system is organised: hexagonal, CQRS,
event-driven, layered
Distributed pattern what crosses a network: saga, outbox, circuit
breaker, bulkhead, gateway, service mesh
A GoF pattern is not a substitute for any of the lower three rows. Proxy is not an API gateway;
Observer is not event-driven architecture; Mediator is not orchestration; Facade is not a
backend-for-frontend; Memento is not event sourcing; Flyweight is not a distributed cache. In each
pair the second has an operational existence — deployment, availability, scaling, failure — that
the first does not, and using one word for both is how a network hop becomes invisible in a design
discussion.
Patterns do participate in architectures: an adapter implements a port in hexagonal architecture, a
command is a CQRS write, a state machine is a saga's core. That is composition across levels, not
equivalence.
Review checklist
References
Deliver the boundary, assumptions that changed, required guarantees and owners, then the smallest
contract changes and failure checks. Keep unsupported guarantees conditional and route detailed
protocol design to the specialist skills below.
- Boundary classification — all twenty-three placed in the
four classes, with what survives a boundary crossing, what silently stops holding, and the
specific additions each distributed form requires. Read when distributing an existing design.
- Design patterns against architectural patterns — the four
levels with what belongs at each, the pairs most often conflated (Proxy/gateway, Observer/EDA,
Mediator/orchestration, Memento/event sourcing, Flyweight/distributed cache), how patterns
legitimately participate in architectures, and the escalation ladder from a class to a service.
Read when a pattern is being proposed as an architecture, or vice versa.
1---2name: gof-patterns-and-distribution3description: What happens to a Gang-of-Four pattern when the collaboration crosses a process boundary, and which additional architectural contracts it may require. Covers process-local, boundary, interaction and algorithm patterns; assumptions that need rechecking at a boundary — shared state, clocks, atomicity, ordering and delivery; the transformations (Singleton to leader election, Observer to pub/sub, Iterator to pagination, Mediator to an orchestrator); and the level confusion that treats a design pattern as a substitute for an architectural one. Use when a local design is being distributed, when a pattern name is applied to a network component, when a "singleton" or a cache is expected to hold across replicas, or when a getter turns out to make a call. Does not cover the individual patterns (the gof-\* skills), saga and outbox mechanics (distributed-transactions-and-sagas, event-driven-architecture), service boundary decisions (distribution-boundaries), or failure taxonomy (failure-models).4---56# Patterns and Distribution78## Purpose910Stop a local design's guarantees from being assumed across a network. GoF patterns primarily11describe collaborating objects in one address space. Local calls can still partially mutate then12throw, block on I/O, or race; a process boundary additionally introduces an independent13failure/ambiguity domain, serialization and operational ownership. The patterns14whose names survive the crossing are the ones most likely to hide that it stopped holding.1516## What a boundary removes1718This classification is conceptual; Java API references use Java 17. Inspect the target runtime,19codec, broker and persistence versions and effective configuration before applying their guarantees.2021```text22Inside one process Across a boundary23────────────────────────────────── ───────────────────────────────────24A call has one process failure domain It may commit remotely while the reply is lost25Latency follows local work/I/O Adds transport queues and independent tail latency26A reference is the object A copy; identity does not travel27Uniqueness is per class loader Uniqueness requires coordination28Order follows synchronization/API Broker/protocol/topology defines its scope29State can share memory State may be remote/replicated; consistency is a contract30Monotonic intervals are local Clock offset and rate assumptions need explicit treatment31One invocation; effects may partial Delivery may be at-most/at-least/effectively-once32```3334Every transformation below follows from that table.3536## Classification3738```text39PROCESS-LOCAL — the guarantee stops at the JVM40 Singleton uniqueness is per class loader, never per cluster41 Flyweight references are shared; nothing crosses the wire42 Iterator the cursor is in this process43 Memento opacity/lifecycle is local unless a durable snapshot contract is added4445BOUNDARY — the pattern manages a seam, and the seam may be a network46 Adapter where a foreign model, vocabulary and failure stop47 Proxy the pattern most able to hide that a call is remote48 Facade coarse granularity is how round trips are saved49 Bridge supported backends must satisfy the chosen contract honestly5051INTERACTION — the pattern shapes who talks to whom52 Command may become a message: schema, delivery and effect policies needed53 Observer becomes pub/sub with broker-specific delivery and ordering54 Mediator becomes an orchestrator, with its own availability55 Chain becomes a workflow, failing at every step5657ALGORITHM — largely unaffected; the choice may not be58 Strategy the choice of partitioner, serialiser or retry policy59 has system-wide effects60 State needs durability if progress must survive restart61 Template coordinate overall budget with each remote step's transport limits62 Visitor the element set becomes a versioned contract63```6465## The transformations6667| Local pattern | Distributed form | What must be added |68| ------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------- |69| Singleton | Leader election / a lease | Fencing tokens, or idempotency so overlap is harmless |70| Flyweight | Distributed cache/content addressing—different mechanisms | Invalidation, staleness, serialization and remote/local tiers |71| Iterator | Pagination/cursor | Strategy, bound, deadline, cancellation, mid-walk consistency |72| Memento | Durable snapshot/checkpoint | Schema identity, compatibility, consistency and corruption recovery |73| Observer | Publish/subscribe | Declared delivery/ordering; transactional bridge when committed changes must publish reliably |74| Command | A message | Schema identity, delivery/effect policy, deduplication where needed, terminal outcome |75| Mediator | An orchestrator | Required durable progress, deadlines, applicable compensation and availability budget |76| Chain | A workflow | Per-step failure and retry, redelivery semantics, partial-effect handling |77| Facade | Remote facade, gateway or BFF when appropriate | Contract, deployment, authentication, scaling and outage surface |78| Proxy | A service client | Deadlines, a failure vocabulary, bulk operations |79| State | State machine within a distributed workflow | Required persistence, timeout outcomes and duplicate policy; not automatically a saga |80| Composite | Fan-out | Concurrency, an overall deadline, a defined partial-failure result |8182## Decision rules8384```text85IF a requirement says "there must be only one"86THEN ask "one per what?" A static field gives one per class loader.87 Cluster-wide singularity needs leader election, a lease, or a88 design where multiplicity does not matter (gof-singleton,89 leader-election).9091IF a process-local limit is configured—a pool, limiter or cache92THEN model the aggregate across minimum/maximum dynamic replica count, rollout93 overlap and sidecars. Simple multiplication is a scenario, not a stable invariant.9495IF an interface designed against a local implementation is about to be96implemented remotely97THEN review the contract: suitable granularity, propagated deadline/context, cancellation98 and named failure/unknown-outcome semantics may require change.99 Otherwise a loop becomes N network calls (gof-proxy).100101IF an in-process listener is being moved to a broker102THEN it is a redesign: six properties change at once — thread,103 transaction, ordering, delivery, failure visibility, schema104 (gof-observer).105106IF an object is sent across a boundary107THEN a representation is serialized and reconstructed; reference identity does not108 travel. Constructor/invariant behavior is codec-specific, and the representation109 becomes a compatibility contract (rpc-and-api-contracts).110111IF a pattern name is applied to a deployed component — "the gateway is112our facade", "the orchestrator is a mediator"113THEN the name is a metaphor, not a design. The component has114 availability, authentication, scaling and an outage surface that115 no class has.116117IF the design question is really "where should this boundary be"118THEN it is not an object-design question at all119 (distribution-boundaries).120121IF duplicate effects are harmful122THEN choose among naturally idempotent operations, deduplication, fencing/coordination123 and transactional authority. Dedup stores also fail/expire; no mechanism is universal.124```125126## The level confusion127128```text129Design pattern objects and classes inside one component130Component design modules, packages, release units131Architectural pattern how a system is organised: hexagonal, CQRS,132 event-driven, layered133Distributed pattern what crosses a network: saga, outbox, circuit134 breaker, bulkhead, gateway, service mesh135```136137A GoF pattern is not a substitute for any of the lower three rows. Proxy is not an API gateway;138Observer is not event-driven architecture; Mediator is not orchestration; Facade is not a139backend-for-frontend; Memento is not event sourcing; Flyweight is not a distributed cache. In each140pair the second has an operational existence — deployment, availability, scaling, failure — that141the first does not, and using one word for both is how a network hop becomes invisible in a design142discussion.143144Patterns do participate in architectures: an adapter implements a port in hexagonal architecture, a145command is a CQRS write, a state machine is a saga's core. That is composition across levels, not146equivalence.147148## Review checklist149150- [ ] Every "only one" requirement names its scope, and the mechanism matches151- [ ] Process-local limits are modeled across autoscaling and rollout replica ranges152- [ ] No interface hides remoteness: deadlines, failure types and granularity are in the contract153- [ ] Any getter/per-item remote call is explicit, bounded and protected from accidental fan-out154- [ ] Published representations have explicit schema identity and compatibility/unknown-value policy155- [ ] Delivery semantics drive idempotency/deduplication and atomicity requirements156- [ ] Fan-out has an overall deadline and a defined partial-failure result157- [ ] Durable workflows persist their state and treat timeouts as real events158- [ ] Pattern names are not used for deployed components without saying so159160## References161162Deliver the boundary, assumptions that changed, required guarantees and owners, then the smallest163contract changes and failure checks. Keep unsupported guarantees conditional and route detailed164protocol design to the specialist skills below.165166- [Boundary classification](references/boundary-classification.md) — all twenty-three placed in the167 four classes, with what survives a boundary crossing, what silently stops holding, and the168 specific additions each distributed form requires. Read when distributing an existing design.169- [Design patterns against architectural patterns](references/design-vs-architecture.md) — the four170 levels with what belongs at each, the pairs most often conflated (Proxy/gateway, Observer/EDA,171 Mediator/orchestration, Memento/event sourcing, Flyweight/distributed cache), how patterns172 legitimately participate in architectures, and the escalation ladder from a class to a service.173 Read when a pattern is being proposed as an architecture, or vice versa.