100 lines
5.4 KiB
Markdown
100 lines
5.4 KiB
Markdown
|
## Application State
|
||
|
|
||
|
In our first high level code walk through, there was talk of
|
||
|
"application state". It was supplied to event handlers and there was talk about
|
||
|
query functions computing materialised views from it, etc.
|
||
|
|
||
|
Let's now look at it in more depth.
|
||
|
|
||
|
### On Data
|
||
|
|
||
|
<blockquote class="twitter-tweet" lang="en"><p>Well-formed Data at rest is as close to perfection in programming as it gets. All the crap that had to happen to put it there however...</p>— Fogus (@fogus) <a href="https://twitter.com/fogus/status/454582953067438080">April 11, 2014</a></blockquote>
|
||
|
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||
|
|
||
|
### The Big Ratom
|
||
|
|
||
|
re-frame puts all your application state into one place, which is
|
||
|
called `app-db`.
|
||
|
|
||
|
Ideally, you will provide a spec for this data in the one place,
|
||
|
[using a powerful and leveragable schema](http://clojure.org/about/spec).
|
||
|
|
||
|
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 is 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 reminded us, data at rest is perfect.
|
||
|
|
||
|
In re-frame's reference implementation, `app-db` is one of these:
|
||
|
```clj
|
||
|
(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 plain old map-in-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 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 (map) `value` currently stored **inside** this `ratom`.
|
||
|
3. the reference implementation creates and manages an `app-db` for you, so
|
||
|
you don't need to declare one yourself (see the 1st 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](https://github.com/tonsky/datascript database). In fact, any database which
|
||
|
can signal you when it changes would do. We'd love! to be using [datascript](https://github.com/tonsky/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 the
|
||
|
reference implementation a bit, [perhaps using this inspiration](https://gist.github.com/allgress/11348685).
|
||
|
|
||
|
|
||
|
### The Benefits Of Data-In-The-One-Place
|
||
|
|
||
|
1. Here's the big one: because there is a single source of truth, we write no
|
||
|
code to synchronize state between many different stateful components. I
|
||
|
cannot stress too much how significant this is. You end up writing less code
|
||
|
and an entire class of bugs is eliminated.
|
||
|
(This mindset very different to OO which involves
|
||
|
distributing state across objects, and then ensuring that state is synchronized, 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 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 provide more
|
||
|
**leverage** than types](https://www.youtube.com/watch?v=nqY4nUMfus8).
|
||
|
|
||
|
4. Undo/Redo [becomes straight forward to implement](https://github.com/Day8/re-frame-undo).
|
||
|
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 virtually 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 do 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.
|
||
|
|