~ / track C / clojure ecosystem
Data-oriented programming
IntermediateIn an object-oriented design, behavior lives with the data: each class defines its own methods, and changing the shape ripples through dozens of files. In data-oriented programming the data is a plain, transparent value — a map or a vector — and functions operate on it from the outside. Anyone can read it, anyone can transform it; nothing is hidden, nothing is private.
The slogan: generic data + generic functions > specialized objects.
Minimal example
Represent a domain value as a plain map. There is no constructor, no class, no DSL — just keys and values:
The "methods" are ordinary functions. They take the data as a first argument and return a new value:
Practical example
Because the data is just data, many small functions compose freely. Here a discount pipeline applies independent transforms without any object lifecycle to manage:
Notice the pipeline: a chain of pure functions, each producing the next
version of the order. The original order is untouched.
Where it pays off
- Logging, serialization, debugging come for free: a value is just a
printable map. There is no
toString/__repr__to keep updated. - Tests are trivial: build a map literal, call the function, compare to a map literal. No mocks, no fixtures.
- Tools compose: every function that works on maps in general (
assoc,merge,select-keys,walk,spec) works on yours.
The trade-off: there is no compiler-enforced schema. Tools like clojure.spec
or Malli cover that gap when you want it.
Check yourself
? quiz
What is the practical implication of representing the `order` as a plain map instead of an `Order` class with methods?
Exercise
Add a :notes field to an order without modifying any existing function. Then
write add-note, a pure function that appends a string to :notes (creating
the vector if it doesn't exist).