skip to content

In the GRASP set of object-oriented design principles, what does the Pure Fabrication principle say, and when would you apply it?

level: juniorimportance: must knowfreq 55%

answer

  1. invented, behavior-only class
  2. no domain counterpart
  3. protects cohesion + coupling
  4. repository / mapper / calculator
  5. exception to Information Expert

basics

~20 s

Pure Fabrication says: when no real-world domain concept can hold a responsibility without spoiling the design, invent a made-up, behavior-only class for it — such as a repository, mapper, or validator — to keep cohesion high and coupling low.

solid answer

~50 s

Pure Fabrication is one of the GRASP (General Responsibility Assignment Software Patterns) principles. Most responsibilities are assigned to classes that mirror a real concept in the problem domain (Order, Invoice, Customer). But some responsibilities — saving objects to a database, converting between formats, running a scoring algorithm, dispatching notifications — do not belong to any domain concept. Forcing them onto a domain class inflates it with unrelated logic (low cohesion) and ties it to infrastructure such as SQL, HTTP, or file formats (high coupling). Pure Fabrication answers this by inventing a class that has no counterpart in the domain vocabulary: it exists purely to hold behavior. Classic examples are OrderRepository, OrderToJsonMapper, PasswordHasher, PricingCalculator. The invented class is usually stateless or holds only collaborators, is highly cohesive (one job), and is easy to test and reuse. The trade-off is more classes and more indirection, so fabricate only when a real motivation exists.

code

pseudocode · 14 lines
pseudocode
// Domain-driven assignment: Order now knows about databases
class Order {
  items; total()
  save() { db.connect(); db.execute("INSERT INTO orders ...") }  // low cohesion, high coupling
}

// Pure Fabrication: invent a class with no domain counterpart
class Order { items; total() }              // pure business behavior

class OrderRepository {                      // fabricated: behavior only
  constructor(db) { this.db = db }
  save(order)  { this.db.execute("INSERT INTO orders ...") }
  findById(id) { /* rebuild an Order from rows */ }
}

go deeper

for a junior

Define it: invent a non-domain, behavior-only class (repository, mapper) when no real-world concept fits, so classes stay focused and loosely coupled. Give one example.

for a middle

Frame it as the deliberate exception to Information Expert, name the two forces (high cohesion, low coupling) plus reuse and testability, and show the persistence example.

for a senior

Discuss the decision procedure, the anemic-domain risk, naming discipline, and how fabrication interacts with Indirection and Protected Variations.

for a principal

Position fabrication as an architectural convention (ports/adapters, application services), talk about where the domain/infrastructure boundary is drawn, the cost of indirection, and how to keep fabrications from proliferating across teams.

## Where the principle comes from **GRASP** stands for *General Responsibility Assignment Software Patterns*. It is a set of nine principles (Information Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection, Protected Variations) that answer one recurring question in object-oriented design: **which class should be responsible for this piece of behavior?** They are not code patterns you instantiate; they are naming and reasoning tools. ## The default rule, and where it breaks The default GRASP rule is **Information Expert**: assign a responsibility to the class that holds the information needed to fulfil it. This tends to produce a design whose classes mirror the *domain* — the real-world concepts users talk about: `Order`, `Invoice`, `Customer`, `Reservation`. This is called **domain-driven** or *representational decomposition*, and it is a good default because the code vocabulary matches the business vocabulary. But some responsibilities have no natural owner: - **Persistence** — "store this `Order` in a relational database." The `Order` holds the data, so Information Expert *seems* to say `Order.save()`. But then `Order` must know SQL, connection pools, transactions, and mapping rules. - **Format translation** — "turn this `Order` into JSON / a CSV row / a wire DTO." - **Algorithms** — "compute a fraud score," "hash this password," "find the shortest route." - **Cross-object coordination** — "apply this pricing policy across a basket." ## The definition > **Pure Fabrication:** assign a highly cohesive set of responsibilities to an *invented* class that does **not** represent a concept in the problem domain, in order to support high cohesion, low coupling, and reuse when the domain-driven assignment would violate them. "Fabrication" = *made up*. "Pure" = *not derived from the domain at all*. The invented class is a **behavior-only** class: it typically has no meaningful identity or business state, only its collaborators (dependencies), and its methods do work rather than represent a thing. ### Two key quality terms it protects - **Cohesion** — how focused a class is. High cohesion = every member serves one clearly related purpose. An `Order` that also opens JDBC connections and formats XML is *low* cohesion. - **Coupling** — how much a class depends on other things. Low coupling = few, stable, narrow dependencies. An `Order` that imports a database driver is coupled to infrastructure that changes for reasons unrelated to the business. ## Canonical examples | Responsibility | Fabricated class | Why not a domain class | |---|---|---| | Load/store aggregates | `OrderRepository` | keeps SQL/ORM out of `Order` | | Domain object ⇄ wire format | `OrderJsonMapper`, `OrderDtoAssembler` | serialization format changes independently | | Password hashing | `PasswordHasher` | crypto is not a `User` concern | | Multi-object business rule | `PricingService`, `TaxCalculator` | rule spans several entities; no single owner | | Sending mail/SMS | `NotificationSender` | I/O and templates are not domain data | | Cross-cutting logging/metrics wrappers | `AuditingDecorator` | keeps entities free of infrastructure | Note that DTOs, value objects, and framework-generated classes are *not* Pure Fabrications in the strict sense: Pure Fabrication is about **behavior with no domain owner**, not about any non-domain class. ## How to recognise the moment to fabricate Ask, in order: 1. Does a domain class already hold all the information? If yes, and the behavior is *about that data as a business concept*, keep it there (Information Expert wins). `order.total()` belongs on `Order`. 2. Would putting it there force the class to import infrastructure, know about formats/protocols/frameworks, or grow a second unrelated theme? If yes → fabricate. 3. Does the behavior span several equal peers with no obvious owner? If yes → fabricate a coordinator/calculator. 4. Would extracting it let you reuse or swap the behavior independently, or test it without building a whole entity? If yes → fabricate. ## Trade-offs **Gains:** entities stay small and pure; infrastructure can change without touching the domain; the fabricated class is unit-testable in isolation and reusable; different implementations can be swapped behind the same interface. **Costs:** more classes, more names, more indirection to follow when reading code; risk of drifting into an **anemic domain model** where entities become bags of getters/setters and *all* behavior lives in `…Service` classes; risk of vague names (`OrderManager`, `Helper`, `Utils`) that hide low cohesion rather than fixing it. Rule of thumb: fabricate for a *reason you can state in one sentence* ("to keep persistence out of the entity"), give the class a name describing what it *does*, and keep genuinely domain behavior on domain classes. ## Edge cases - A fabrication can later be *promoted* into the domain if the business starts talking about it (a `PricingService` becomes a `PricingPolicy` the business names and configures). Conversely, a domain-sounding class that only shuffles data may really be a fabrication in disguise. - Static utility classes are the degenerate form of fabrication: acceptable for pure functions, but they cannot be substituted or injected, so prefer instances when the behavior may vary. - Layered/hexagonal architectures institutionalise fabrication: repositories, gateways, adapters, and application services are all pre-agreed Pure Fabrications.

  • Isn't Pure Fabrication just a licence to create *Service and *Manager classes for everything?
    No. It applies when a responsibility has no sensible domain owner. If a domain class already holds the data and the behavior is genuinely about that concept, Information Expert wins. Overusing fabrication produces an anemic domain model where entities are data bags.
  • Is a DTO a Pure Fabrication?
    Not really. Pure Fabrication is about assigning *behavior* that no domain class should own. A DTO is a data carrier. The mapper/assembler that converts between the entity and the DTO is the fabrication.
  • Which GRASP principles does Pure Fabrication serve?
    High Cohesion and Low Coupling primarily, plus reuse. It also usually produces Indirection (a mediating object) and can enable Protected Variations by hiding an unstable dependency behind a stable interface.

A restaurant's domain concepts are Chef, Dish, and Guest. Nobody wants the Chef also washing dishes, doing the books, and driving deliveries. So the restaurant invents roles nobody eats or orders — dishwasher, accountant, courier. They are not part of the meal; they exist purely so each real role stays focused.

saying these in an interview costs you the question

  • Saying Pure Fabrication means "any class not in the domain model" — DTOs and config objects are not what the principle is about.
  • Claiming it contradicts Information Expert; it is the deliberate exception applied when Expert would hurt cohesion or coupling.
  • Fabricating a class with no stated motivation, producing Manager/Helper/Utils grab bags.
  • Moving all behavior out of entities into services and calling it good design (anemic domain model).
  • Believing a fabricated class must be stateless or static — it commonly holds collaborators and can be injected/substituted.

context