I've been struggling with using Datascript as the "exclusive" datastore in my frontend app. The challenge is related to the hierarchical nature of UI vs the flat structure of Datomic schema.
As an example, I want to open a modal in my app. The modal may take an :modal/arguments
vector. One of these arguments could be a map representing a TODO entity which is already transacted into the DataScript DB.
{ :modal/type :edit-todo
:modal/arguments [... {entity}] }
Challenges:
A) If the map representing the TODO is transacted as part of the modal's arguments, it will upsert on :entity/id
. At best it's an unnecessary & identical upsertion, at worst it may overwrite newer data in the app-db
B) Datomic (and thus DataScript) schema doesn't support a variable-length vector of heterogenous values. Thus using a generic schema for :modal/arguments
isn't possible.
To solve A, I've taken to casting the :entity/id
key to :modal/entity-id
, preventing upsertion. Unfortunately this complicates the Clojure specs.
To solve B, I'm not sure. I could use a unique :arguments key for each :modal/type
-- such as :modal/edit-todo-arguments
A different solution to B would be accepting the loss of schema parity between Datomic <-> DataScript. Could just transact the :modal/arguments
as a variable-length heterogenous vector -- it works just fine in DataScript.
An alternative solution to both of these would be accepting two data-stores in my frontend app. 1 app-db atom for UI datastore + 1 datascript DB for business entities. This would work but creates extra hassle (testing, undo/redo FNality, etc.)We don't do very many frontend unit tests. All our tests are integration tests, testing typical user paths. Only unit/gen tests are on the more complex biz logic functions. Those are almost always functional and so we don't need to deal with state.
Understood, & thank you for the explanation! I've been struggling with this & it's good to hear that there's working solutions out there π
Feels like some sort of impedance mismatch going on here. If anyone has seen these problems or could speak to how they handled datastore in their own app, please do.
(FYI when I use :entity/id
, this refers to a system-wide unique UUID, not a Datomic ID.)
Our app takes the two data store approach. We donβt have undo requirements. Pretty much everything stored in DataScript is then transacted to Datomic.
Anything that is frontend specific (e.g., modal opened, special modal state, etc), is stored in the app-db atom.
Hmm, good to know that's viable. For testing, do you just set the state of both DBs in setup?