After discussing some of my ideas around Event Sourcing at last night’s Elixir Happy Hour in Austin (highly recommend), I spent some time distilling things into a new package called Must.
Event Sourcing provides many technical and business benefits. However, the pitch often starts with technical details that overwhelm newcomers and lead to squabbles about consistency and complexity. I believe there is an opportunity to make Event Sourcing concepts more approachable for teams and leaders who are apprehensive about unfamiliar and intimidating aspects of this technique.
The Goal
Must should make it easier for engineering teams to build Event Sourced systems, with or without prior experience.
Motivation
As much as I feel comfortable using Event Sourcing & CQRS, much of the conversation and tooling feels overly academic. It’s a tough sell.
I also believe the concepts, which are often relegated to finance and highly regulated domains, could become more mainstream given a few modifications to the terminology and technical implementations.
While this package does not use the same verbiage and implementation, it takes inspiration from the first-principles design of the Verbs library for PHP.
The Ask
I am looking for feedback about this approach to ES from anyone who has interest. Please take a look at the documentation and source code. I have a short list of starter questions to get the conversation going:
Will the Must protocols & behaviour(s) make it easy/easier to spin up Event Sourcing systems?
Do the words used in the protocols make sense, particularly when they are composed together?
Which storage adapters are most desired?
Is this approach doomed to fail?
Do you have interest in contributing in general or to particular features?
It’s very early, and the picture may not be clear yet how these ideas will pan out. I hope this discussion will help validate the utility of this approach and lead to a strong set of tools for bringing event-sourced systems to life.
This is way too true. In my entire career I’ve never once heard anyone explain it an approachable manner and at one point I stopped looking. It felt like Haskell fanatics explaining how monads are the Universe’s hidden ether essence.
I am not going to go question by question, just going to give you a high-level take: I work in finance. Having audit logs for pretty much anything is not just table stakes, it’s mandatory or you can get in a huge trouble; auditors don’t much care if you can’t explain $1.37 or $98700.62.
However, all the BS terms (sorry not sorry) like “aggregates” really don’t help.
As a start, I believe the whole thing should be very dev-centric i.e. “aggregates are periodic snapshots of all the accumulated events”. Friggin plain and simple, every good dev will get it immediately. (And I might have gotten the term and the explanation wrong which would be both funny and also prove my point perfectly.)
Storage adapters: TigerBeetle (financial ledger, might not apply to everything) and PostgreSQL. Rest can wait.
Approach is doomed to fail if it continues being snobby hand-wavy cult requiring too much homework from people who came to be sold on an idea. NOT to get a new university degree.
I’ll bite
The short answer:
In a regular system every time something happens, a decision is made, we mutate the current state of the system - we update a table in a database.
In an eventsourced system we don’t mutate the state, instead we are going to store the decisions as events in an append only event store.
A bit more context - on my phone so I’ll keep it brief:
when we need to make a new decision we read back the history of events to build the current state and then we can make a new decision in the form of events.
events should preferably be domain events - not technical events. They have a meaning in the domain: Invoice marked as payed over invoice updated. Or user registered over user added.
eventsourcing are more useful in domains where the passage of time matters
To me GitHub - CargoSense/fable: Your events have a story to tell. · GitHub is still the most simple introduction one can have to event sourcing on elixir. It easily retrofits onto a regular ecto architecture, it’s 1000 loc you can read in like 15 minutes and most of it is simple boilerplate - if you can do phoenix with ecto you can understand the fable codebase. As with e.g. commanded you want to skip ProcessManager. Comparatively commanded feels like a kubernetes in terms of complexity in setup - useful, with lots of the mentioned sidequests.
I personally am a big fan of discovering concepts from first principle, so maybe that’s why I like the approach of fable so much. It shows you how to store decisions with events and updating an aggregate without needing to name those things. Once you got to see how such a system works then you can start giving the pieces names like the commands, aggregates, projections whatnot. Once the basic idea is explained you can start getting into all the additional ideas necessary to cover the sharp edges a more naive implementation might not care for or run into.
To turn this into the direction of Must. I like the idea of a more generalizable system, but it imo does suffer from e.g. expecting “Commands” where a user might not know what to make of the word command. At least for the context of teaching I’d personally stay with fable and maybe then update to Must if it eventually delivers a more featureful step beyond fable before needing to take the step to e.g. commanded.
First of all, aggregates is an overloaded term and I don’t use the definition both of you are pointing to - but that’s a different concern - I gave a full talk on this: https://www.youtube.com/watch?v=m7SMk8VA7Bg&t=3s
But even what you’re saying in the more traditional use of what an aggregate is, I don’t agree with what you’re saying.
In most event sourcing context, an aggregate just refers to the history of events for 1 specific stream. A stream might be a “bank account” or “the lifecycle of 1 invoice” or anything else that you decide to give an id.
That’s it. The periodic snapshots is purely an implementation detail - one that I would not recommend reaching for from the beginning.
So the aggregate is the history of events for a stream - so a list of events - the snapshot is something you can but don’t have to keep.
I was not trolling so I don’t think you are biting a bait. I am simply disillusioned by CQRS / Event sourcing is all, and I was honest enough to admit that at one point I stopped looking for good material.
Which means that material might very well exist today – and your material might be that, even – but I simply lost interest with the years.
Everything has an impl detail behind it. Ignoring those means exactly falling in the overly academic and theoretical trap I pointed at above. I want to know how is it doneandwhy is this useful. Both are important. Helps a pragmatic doer like myself grok it.
I get the stated benefits but until somebody writes a library (thanks @LostKobrakai for the reference; fable still managed to confuse me almost at the beginning with “routers” and “oh, we need access to private functions so we do this thing” but still appreciate the pointer; /CC @benwilson512 as he’s the author) whose benefits are blindingly obvious at the literal first minute of reading, for now I’ll skip and will continue doing my own half-baked CQRS.
The main idea is Clickhouse’s materialized views play the role of a snapshot, so basically the latest actual state is maintained by Clickhouse itself, the app just pushes new records there.
Good feedback. For Must, I am willing to use a different vocabulary for ES/CQRS concepts. The jargon was a big hurdle at first, so I’ll consider a different term for the command concept. Perhaps “change” is a more ubiquitous word.
I can see Fable’s value in brownfield CRUD applications and for anyone new to Event Sourcing. It looks like a nice way to layer in events without prior experience. As a contributor to that repo, it makes sense you have a story to tell about events.
Must is also an opportunity to try out newer concepts like the dynamic consistency boundary. This discussion from some of the ES influencers/thought leaders makes me think there is promise to this approach, and I hope to avoid aggregates entirely if this truly works.
This is how I currently expect commands/changes to flow into the system, using Must.process_command/2:
So far, there are two protocols and a planned behaviour for event storage. I expect the package to include adapter modules for the most common dependencies, perhaps Postgres & TigerBeetle as @dimitarvp mentioned.
@mudasobwa I was introduced to ClickHouse in the last year, and it dose sound like a good fit for an event store. Thanks for sharing!
I agree, and it’s something that is overlooked. But I do like to start at the why first. If we understand the why and can agree when it’s a good idea to use it, then we can have a look at how - which of course should be factored in the decision as well.
If the how is complex then even a good why might not be enough.
I think it’s also important to reflect on the fact that in essence eventsourcing is a different concept than state based systems. We often compare complexity against those systems that we already know and forget it took us (me at least) some time to learn those systems as well. So that means we should give it a fair chance (if the why matters to us).
I’m not going to explain the how here - Idon’t have time to do a decent explanation currently.
I’m also ignoring CQRS completely, I know, it just not that important for the essence of eventsourcing. I have some good diagrams on both of these things actually from courses that I gave in the past but I don’t have good written text. Maybe someday I’ll share that more broadly
The intent for this thread is to focus on the ideas presented in Must to distill event sourcing concepts into a small extensible package. The discussion about aggregates, CQRS, and the like are valuable, but a bit out of the intended scope. A discussion about aggregates, using that talk as a starting point, deserves its own thread.
To reorient the conversation, I am curious which framework(s) and tools you use to implement Event Sourcing. Commanded, Fable, something bespoke?
What bugs me most in all the implementations of ES so far, is the necessity to explicitly issue an event. All these MyEventStore.append_to_stream/3 in Commanded, or ChangeStore.save_change!/2 in Must, or anything else.
In persistomata I mentioned above I abstracted data and made each and every data change an event, that’s it. I strongly believe that ES had been invented to diminish a hassle about persistence, ideally to zero. I can live with @side_effect annotation, or @intermediate_store [target: :result], but explicit calls to functions exported from the store implementation ditch the purpose of ES completely IMHO. If I need to write a code keeping in mind that I have to store it here and there, I am good with Postgres, ain’t I?
I’m probably going offtopic to the original topic again, so @LostKobrakai feel free to split this up.
I can agree with the storing part, but not with the issuing an event.
I don’t think that was the original goal, but it might have been an intended side-effect.
But I don’t agree with some of the other things - or I’m misunderstanding stuff.
Emitting domain events allows you to very clearly and explicitly capture decisions made in a system / in a process and base your next decisions on the past ones.
# this is based on a real domain I worked in but hugely simplified
DocumentSubmittedForAproval
FourEyesApprovalRequested
DocumentReceivedApproval(person1)
DocumentReceivedApproval(person2)
DocumentApproved
# Now something changes and the rules is that after a change we need 1 of the
# initial pair of 4 eyes to take another look - in reality it depends on the type of change.
DocumentChanged
NewApprovalRequestedFrom(person1 or person2)
DocumentRecievedApproval(person 1)
DocumentApproved
In reality a document consists of multiple lines and each line has specific approval rules on changing things sometimes only the one line needs to be re-approved, sometimes mutliple.
Modelling this on top of a regular postgres state based system is possible, but with a stream of events it really tells the story of what happened to this document and what is allowed to happen right now.
So the choice of emitting an event is really valuable - again - it explicitly captures the domain/business decision.
But if you think about this in a more abstract way, how often do you want to capture the domain/business decision without amending any data? What kind of a business decision is not mirrored by any data change, leaving the whole system in the state which cannot be distinguished from the state before this decision?
This is to be handled by an FSM instance, it has nothing to do with the ES per se. Add listeners to data changes and make one of these listeners writing to ES—and you are all set. You code the business logic, and ES happens to appear automagically. Alongside with materialized views in ClickHouse—you get the latest snapshot, the full events history, and whatnot. The consistency of business processes is guaranteed by an FSM. Voilà.