How does GRASP's Pure Fabrication relate to two other GRASP principles — Indirection and Protected Variations — and can you have one without the others?
answer
- fabrication = who owns it
- indirection = middleman
- protected variations = stable interface over predicted change
- repository = all three
- leaky wrapper = indirection without protection
basics
~20 sPure Fabrication invents a non-domain class to keep cohesion high. Indirection puts a middleman between two things so they don't touch. Protected Variations hides something unstable behind a stable interface. They overlap often, but each can occur alone.
solid answer
~50 s**Pure Fabrication** is about *where* a responsibility goes: into an invented, non-domain class, motivated by cohesion, coupling, and reuse. **Indirection** is about *shape*: insert an intermediate object so two elements do not depend on each other directly — the goal is decoupling. **Protected Variations** is about *change*: identify a predicted point of instability and wrap it in a stable interface so variation does not ripple. In practice a repository is all three at once: it is invented (fabrication), it sits between the domain and the database (indirection), and behind an interface it shields the domain from storage technology changes (protected variations). But they separate cleanly. A `PricingCalculator` extracted purely because the rule spans several entities is fabrication without any mediation goal. A domain class that mediates between two others — an `Order` linking `Customer` and `Product` — is indirection with no fabrication. And using polymorphic subclasses of a domain concept protects variation with no invented class at all.
code
pseudocode · 12 lines// All three at once
interface PaymentGateway { charge(amount, token): Result } // Protected Variations: stable interface
class StripeGateway implements PaymentGateway { ... } // unstable detail behind it
class CheckoutService { // Pure Fabrication: invented class
constructor(gateway: PaymentGateway) { ... } // Indirection: domain never calls Stripe
}
// Fabrication only (no indirection, no variation protection)
class PriceCalculator { price(order, customer, promos) { ...spans 3 aggregates... } }
// Indirection only, by a DOMAIN class (no fabrication)
class Enrollment { student; course; grade } // decouples Student from Course, is a business conceptgo deeper
Distinguish them plainly: fabrication invents a class, indirection adds a middleman, protected variations hides likely change behind an interface — and note a repository is often all three.
Give the repository example showing all three, plus one example of each occurring alone.
Separate motivation from mechanism: cohesion-only fabrications, domain classes as mediators, and leaky indirection that protects nothing. Discuss when an interface is speculative generality.
Frame it as budgeting decoupling: protect variation where change is evidenced and the blast radius is large; elsewhere prefer directness. Connect to Open/Closed, information hiding, and the navigability cost across a large codebase.
## The three principles, defined All three are GRASP (*General Responsibility Assignment Software Patterns*) principles — heuristics for deciding which object should be responsible for what. ### Pure Fabrication Assign a cohesive set of responsibilities to a class **invented by the designer**, with no counterpart in the problem domain, when domain-driven assignment would hurt cohesion, coupling, or reuse. *Question answered:* **who** should own this behavior? *Examples:* `OrderRepository`, `PasswordHasher`, `XmlMapper`. ### Indirection Assign the responsibility of **mediating** between two (or more) elements to an intermediate object, so that they are not directly coupled. *Question answered:* how do I stop A and B from knowing each other? *Examples:* an adapter between an application and a third-party library; a controller between UI and domain; an event bus between publisher and subscriber; a mediator coordinating a set of widgets. ### Protected Variations Identify points of **predicted variation or instability** and assign responsibilities to create a **stable interface** around them. *Question answered:* how do I stop foreseeable change from rippling? *Examples:* a `PaymentGateway` interface with Stripe/PayPal implementations; a plugin API; a configuration abstraction; data-driven rules instead of hard-coded ones. This is the GRASP name for the idea also expressed as the Open/Closed Principle, information hiding, encapsulation, and "program to an interface." ## How they stack They form a natural layering of intent: 1. **Fabrication** decides a new class exists and what it is responsible for. 2. **Indirection** is very often the *structural consequence*: because the new class sits between the domain and something else, callers now go through it. 3. **Protected Variations** is achieved when that middleman exposes a **stable, abstract** interface and the unstable thing lives strictly behind it. The repository example carries all three: - Fabrication: `OrderRepository` is invented, not a business concept. - Indirection: the domain no longer touches the database; the repository mediates. - Protected Variations: with `interface OrderRepository` + `SqlOrderRepository` / `InMemoryOrderRepository`, changing storage technology does not ripple into the domain. A `PaymentGateway` port in hexagonal architecture is the same trio. ## Where they come apart **Fabrication without indirection.** Extract `PriceCalculator` because the pricing rule spans `Order`, `Customer`, and `PromotionCatalog` and no one of them may own the others. There is no unstable dependency to hide and nothing being shielded from anything — the motive is purely cohesion. Similarly `PasswordHasher` as a concrete class with no interface. **Indirection without fabrication.** A domain class can be the mediator. In a many-to-many association, an `Enrollment` entity sits between `Student` and `Course`; it is a real business concept *and* it decouples the two. Likewise a domain `Order` mediating `Customer` and `Product`. **Protected Variations without fabrication.** Use polymorphism over a domain hierarchy: `Shape.area()` with `Circle`/`Square` implementations protects callers from shape-type variation, and every class involved is a domain concept. Or protect variation with data: a rules table instead of branching code. **Indirection without protected variations.** A middleman that simply forwards calls and re-exposes the same unstable types protects nothing — it is a *leaky* indirection. Wrapping a library while passing its exception types and value objects straight through is the classic example: you added a hop and gained no insulation. This is the most common mistake in this area. **Protected variations without indirection.** Encapsulating fields behind methods on the *same* class hides a representation choice with no intermediate object at all. ## Why interviewers ask The question checks whether you can separate **motivation** from **mechanism**. Candidates often collapse all three into "add an interface." The discriminating answers: - Adding an interface with exactly one implementation, no predicted variation, and no unstable dependency behind it gives you indirection cost with no protected-variations benefit — speculative generality. - Conversely, protecting variation matters most where change is *predicted with evidence* (a vendor likely to be replaced, a format known to evolve, a regulated rule that changes yearly), not everywhere. - Fabrication can be justified with zero change-protection motive, purely on cohesion grounds. ## Cost side Every one of them buys decoupling with **navigability**: more classes, longer call chains, harder "where does this actually happen" reading, and sometimes runtime cost. Applying all three by reflex produces architectures where a single feature touches eight files. The mature position: fabricate on cohesion evidence, add indirection where two things genuinely must not know each other, and pay for protected variations where the variation is predicted and the cost of an unprotected change is high.
- Should every fabricated class get an interface?No. Add an interface when there is a predicted variation, a real substitution need, or a boundary you want to test across. A single-implementation interface with no such driver is speculative generality — it adds navigation cost and buys nothing.
- Give an example of indirection that fails to protect variations.A wrapper around an HTTP client that returns the library's own response and exception types. Callers still depend on the vendor's types, so swapping the library still ripples. Real protection requires translating to your own types at the boundary.
- How does Protected Variations relate to the Open/Closed Principle?They express the same idea from different angles: OCP says a module should be open to extension and closed to modification; Protected Variations says wrap predicted instability in a stable interface. Protected Variations is broader — it also covers data-driven configuration and service lookup, not just polymorphism.
saying these in an interview costs you the question
- Treating the three as synonyms, or saying "they all mean add an interface."
- Claiming Pure Fabrication always implies an interface and multiple implementations.
- Assuming indirection alone guarantees decoupling, when leaking the wrapped library's types keeps callers coupled.
- Applying Protected Variations to every dependency, producing speculative generality and eight-file features.
- Believing only invented classes can act as mediators; domain classes frequently do.