Skip to content

Application State

The Big Ratom

re-frame puts all application state into one place, which is called app-db.

Now, this advice is not the slightest bit controversial for 'real' databases, right? You'd happily put all your well-formed data into PostgreSQL.

But within a running application (in memory), there can be hesitation. If you have a background in OO, this data-in-one-place business is a really, really hard one to swallow. You've spent your life breaking systems into pieces, organised around behaviour and trying to hide state. I still wake up in a sweat some nights thinking about all that Clojure data lying around exposed and passive.

But, as Fogus reminds us above, data at rest is quite perfect.

In re-frame, app-db is one of these:

(def app-db  (reagent/atom {}))    ;; a Reagent atom, containing a map

Although it is a Reagent atom (hereafter ratom), I'd encourage you to think of it as an in-memory database. It will contain structured data. You will need to query that data. You will perform CRUD and other transformations on it. You'll often want to transact on this database atomically, etc. So "in-memory database" seems a more useful paradigm than a map in an atom.

Further Notes:

  1. app-state would probably be a more accurate name, but I choose app-db instead because I wanted to convey the in-memory database notion as strongly as possible.
  2. In the documentation and code, I make a distinction between app-db (the ratom) and db which is the value (A map) currently stored inside this ratom. Be aware of that naming as you read code.
  3. re-frame creates and manages an app-db for you, so you don't need to declare one yourself (see the first FAQ if you want to inspect the value it holds).
  4. app-db doesn't actually have to be a ratom containing a map. It could, for example, be a datascript database. In fact, any database which can signal you when it changes would do. We'd love! to be using datascript database - so damn cool - but we had too much data in our apps. If you were to use it, you'd have to tweak re-frame a bit and use re-posh.

The Benefits

There are benefits to having data in the one place:

  1. Here's the big one: because there is a single source of truth, we write no code to synchronise state between many different stateful components. I cannot stress enough how significant this is. You end up writing less code and an entire class of bugs is eliminated. (This mindset is very different to OO which involves distributing state across objects, and then ensuring that state is synchronised, all the while trying to hide it, which is, when you think about it, quite crazy ... and I did it for years).

  2. Because all app state is coalesced into one atom, it can be updated with a single reset!, which acts like a transactional commit. There is an instant in which the app goes from one state to the next, never a series of incremental steps which can leave the app in a temporarily inconsistent, intermediate state. Again, this simplicity causes a certain class of bugs or design problems to evaporate.

  3. The data in app-db can be given a strong schema so that, at any moment, we can validate all the data in the application. All of it! We do this check after every single "event handler" runs (event handlers compute new state). And this enables us to catch errors early (and accurately). It increases confidence in the way that Types can increase confidence, only a good schema can potentially provide more leverage than types.

  4. Undo/Redo becomes straight forward to implement. It is easy to snapshot and restore one central value. Immutable data structures have a feature called structural sharing which means it doesn't cost much RAM to keep the last, say, 200 snapshots. All very efficient. For certain categories of applications (eg: drawing applications), this feature is borderline magic. Instead of undo/redo being hard, disruptive and error-prone, it becomes trivial. But, many web applications are not self-contained data-wise and, instead, are dominated by data sourced from an authoritative, remote database. For these applications, re-frame's app-db is mostly a local caching point, and being able to undo/redo its state is meaningless because the authoritative source of data is elsewhere.

  5. The ability to genuinely model control via FSMs (discussed later).

  6. The ability to do time travel debugging, even in a production setting. More soon.

Create A Leveragable Schema

You will want to provide a spec for all this data-in-the-one-place. You'll want the leverage it brings.

Of course, that means you'll have to learn spec and there's some overhead in that, so maybe, just maybe, in your initial experiments, you can get away without one. But not for long. Promise me you'll write a spec. Promise me. Okay, good.

Specs are potentially more leveragable than types. This is a big interesting idea which is not yet sufficiently mainstream. Watch how. Also, watch Rich Hickey roar (bummer, poor audio)