ALL-DESI1001 · primitive_obsession
Classify whether generic values hide repeated domain rules.
This is a contextual rule for all languages. Read its implementation.
Definition
Section titled “Definition”A primitive is a generic value such as str, int, or dict. Using one is normal. The Fowler
smell appears only when repeated validation, units, legal states, or operations give that value
stable domain meaning. One value with repeated rules suggests a value object. Several values
that change state together suggest a domain model. An existing wrapper is useful only when it
centralizes those rules. The final category is selected by the explicit decision table, not by
the judgment backend.
Evidence
Section titled “Evidence”Each finding names one independently assessed predicate and the exact retained claims behind it, so a reader can check the deterministic result against evidence rather than against the model. Those claims cite repeated validation sites, operations, parameter groups, state transitions, call boundaries, and any existing wrapper. A proposed class is not evidence that an existing abstraction adds knowledge. The repair is always a choice here, because a judgment nobody can reproduce is not an edit.
Exceptions
Section titled “Exceptions”Local counters, transient parsing values, stable wire formats, and framework-required scalar
fields can remain generic when their domain rules are not duplicated. A required generic form
is read before any existing wrapper is weighed, so building a domain type out of the raw values
a boundary hands over is appropriate rather than one model too many.
Examples
Section titled “Examples”.. rubric:: Bad example
Validation for the same monetary concept is repeated at several boundaries.
.. code-block:: python
def charge(amount_minor. int, currency. str) -> None. if amount_minor < 0 or currency not in SUPPORTED_CURRENCIES. raise ValueError(“invalid money”)
.. rubric:: Good example
One immutable value owns the invariant and reusable operation.
.. code-block:: python
class Money(FrozenModel): amount_minor. int currency. Currency
def add(self, other. Money) -> Money. if self.currency is not other.currency. raise ValueError("currency mismatch") return Money( amount_minor=self.amount_minor + other.amount_minor, currency=self.currency, )A loop counter that never crosses its local algorithm remains an int.
References
Section titled “References”- Cites “Refactoring”, Primitive Obsession
- Cites “Domain-Driven Design”, Value Objects
- Cites “Refactoring Guru”, primitive obsession smell