Also, I notice this approach is imperative / trigger style, meaning we have to spread out the code for a computed value across all the dependencies where it's computed from. Is there a more declarative / materialized view / Functional Reactive Programming style approach?
Greg's example updates state in an imperative style, but it's trivial to get a more FRP-style with these systems by using the reducer pattern (as seen in Elm, Redux, etc). For any stateful entity in your system (denormalized by virtue of simply not being an event), all you need to do is put together a stream of all the events from your journal which could effect the entity's value, and then write the logic for each event type, saying what it means for that particular piece of state. It's a profoundly simple separation of concerns in theory and practice. Having state-only FRP like Derivable would be a relatively thin layer of icing on top of the enormous and delicious cake that is event sourcing :)
isn't that the fundamental problem of writing clunky code which my post talks about?
having to manually reason that it has to be either "newMessage" or "seenRoom" or "markAsRead" or "deleteRoom" or "deleteMessage", etc
I'm not sure I follow. In your blog post you say
A good codebase is one that makes you, the programmer, nimble. Such a codebase minimizes your workload in the face of ever-changing (1) behavioral requirements and (2) schema field definitions.
The normalized approach, for all its performance flaws, at least lets us take our beast of a query and wrap it in a function — something like getNumUnreadRooms(userId). That function can be referenced from anywhere in our codebase, so any change to the schema field definition of User.numUnreadRooms requires us to change our code in only one place. With the normalized approach, we are nimble.
But one gets exactly the same benefits with event sourcing. Domain changes don't cascade exponentially. New features and changes to existing features are often stupefyingly easy to accomplish because you get this same separation of concerns. The main difference is that you write pure reducers instead of pure query functions.
by your logic, redux is already great so we don't really need derivable/mobx/vueJS :)
Haha 🙈 - I think that Derivables are extremely useful for computing derived state and using it to trigger side effects. I'm using Redux without Derivable right now in a React Native app and I often feel crippled without Derivable and frustrated by the boilerplate and indirection that Redux requires to effectively deal with derived state.