AshEvents: Event Sourcing Made Simple For Ash

Hey folks!

AshEvents Release

We’ve just released the first version of AshEvents, an Event Sourcing tool for Ash Framework apps.

Check out the blog post for the announcement! Thanks to @Torkan for building this package and for writing a guest post for the Alembic blog to share!

AshEvents: Event Sourcing Made Simple For Ash — Alembic :tada:

Logo

30 Likes

The first thing I looked up in the docs, before reading anything, was “snapshot” – and the search returned nothing. Still looks interesting though, I’m going to read more :slight_smile:

1 Like

One of my first thought when I was reading the announcement was that it’s great, but also I don’t think it’s event sourcing. Ability to replay the state is great, but it seems that the state is still a primary concept and events are secondary.

Still great though. I would just avoid marketing it as event sourcing (as the readme does).

Which to me is the practical event sourcing. Going all the way I never found very productive. Getting the actual state of things as of right now became a chore. Just obstacles.

What you said is what would sell me to “event sourcing”.

1 Like

Event sourcing might be not productive for certain apps, especially if they are mostly CRUD. And it’s totally fine. I don’t see the reason to call something that is not an event sourcing an event sourcing. Why would one do that? To ride a hype wagon?

1 Like

To sell it better and achieve wider usage, I’d say.

In my case, I see a lot of value-add in “CRUD apps with extended history and potential state reconstruction if needed”. Would you not agree that that could be a compromise case for CQRS without having to resort to aggregates just to get an answer on “hey, what is in fact the current value of this shopping cart”?

1 Like

The term may not be as strictly defined as you think :slight_smile:

Lets cite some sources:

The fundamental idea of Event Sourcing is that of ensuring every change to the state of an application is captured in an event object, and that these event objects are themselves stored in the sequence they were applied for the same lifetime as the application state itself.

There are a lot of ways to interpret and implement the concept of “event sourcing”, but the most important property is that you can delete all of the projections from the event store, and then rebuild them all by playing forward an event log.

AshEvents has this property. What we’re doing, however, is persisting to the projections directly while persisting to the authoritative event log, which is just an implementation detail.

Could you perhaps restate this in terms of a concrete benefit that you get by having events as “primary” and state as “secondary”? Like a capability of an application that you have by doing that that you don’t have if you don’t do that? Regardless, I’d say that AshEvents is not putting one first or second, it is a middle-ground design pattern that gets you the best of both worlds (if you’re willing to adopt some constraints).

I think it’s very reasonable to invert the pattern that AshEvents uses, and would not take a significant amount of change to implement it. What we would do is add a piece of context that is set that tells us to actually run any hooks on an action, and default it to false. Then you can hook something up to your event resource (something like EventStore — EventStore v1.4.8) that runs it on a loop and reruns that event with that context set to true.

Ultimately, there is just no need for us to add that layer of indirection to have all of the same benefits. But if someone else wants it, then PRs welcome :slight_smile:

What we’re going for is not having to restructure the entire application like an event stream/handler system (often putting side effects far away from their cause, and making understanding a system without “just running it and finding out” really difficult) to get the benefits of event sourcing.

While I appreciate the concept, I definitely wouldn’t intentionally misuse a technical term just for marketing purposes :smiley:. If someone convinced me well enough that this really doesn’t qualify as event sourcing (i.e by no definition of the term does it fit), then I’ll happily ensure the blog post is updated.

6 Likes

I definitely commented a touch too quickly (a personal flaw that still gets the better of me occasionally; my apologies).

I meant this more like “As the CQRS / Event Sourcing is indeed not as clear-and-cut defined, why not ‘sell’ a subset of it better, especially since it solves real problems?”. Because like yourself, I am not convinced CQRS / Event Sourcing has super clear boundaries on its definition.

Could be my own ignorance, admittedly.

For sure, I read your good intentions/meaning I just wanted to clarify :heart:

We had a conversation before launching about this (i.e “some people will say this is not event sourcing”) and ultimately decided that it fits the need, and fits enough definitions for the term for us to say “this is one way to do event sourcing with Ash”.

1 Like

That might be true. However it still has “sourcing” in the name, meaning that the events are actually the source (what I meant by primary vs secondary). Since you brought up Fowler’s article, let me quote two things from it:

Introducing Event Sourcing adds a step to this process. Now the service creates an event object to record the change and processes it to update the ship.

What I understand here is that a record of a change (an event) is then processed to update the state. Not the state is updated and then an event is stored. This also echoes later in the article, when he writes that just having a log of changes is a relatively small gain, which can be achieved otherwise, without event sourcing.

The key to Event Sourcing is that we guarantee that all changes to the domain objects are initiated by the event objects.

Here, again, the change (of state) is initiated by an event.

This is how I understand it, which might be a wrong understanding of course. I have no credentials to gatekeep what is really ES or not. It was just my reaction when I read the announcement. No need to change it just because of that :wink:

This is a bit tricky. For me the biggest benefit is that the application has just one track to do things: always start with an event. I have seen systems that bragged that they can replay, but they actually could not, because nobody used that feature for a long time and it stopped working, because direct changes to the state made it incompatible.

I would guess there might be some cases with race conditions, where writing an event and then replaying state is more reliable than concurrent updates of the state, but I have no proof here.

Just to be clear, I am completely fine with that. I don’t think ES is some kind of a silver bullet for every application. Most of them probably don’t need it. But having “actionable” events (i.e. a structure in the storage, not just text logs) is always better than not having them.

1 Like

Right, so with AshEvents, an event is always committed first, transactionally, representing exactly what changes are about to occur in the projection: ash_events/lib/events/create_action_wrapper.ex at v0.1.1 · ash-project/ash_events · GitHub

I think there is definitely a way that you can sort of “accident” your way out of the benefits (just like folks could accidentally change the source code of an event handler in a more standard implementation of event sourcing), and likely as the package is used more widely or internally we’ll find improvements to be made to the pattern itself (i.e checking automatically for backwards incompatible changes/the need to add event version adapters).

2 Likes

I’m inclined to agree with @zachdaniel here: if the interface and observable behaviour are the same—and in this case, not strictly defined to begin with—the implementation detail of the persistence model shouldn’t disqualify.

Admittedly, when I first learned about the pattern I myself was nerd sniped by the persistence mechanism turning how I’d thought about state management to that point on its head. But this implementation seems like a pragmatic compromise that works elegantly with Ash fundamentals and avoids some of the expensive projection work laid out in the OG recipe, so I’m more than fine with it.

3 Likes

What would happen if event persisting succeeds, but the original action itself fails?

They would roll back together.

If I am able to construct a given piece of information from the events alone, and the state of my system is derived by it, call it whatever you want.

It is totally fine to have some event sourced systems that guarantees transaction with some piece of State and Event Log; some shortcuts for optimization sake, still, I would personally avoid it, but meh, “It Depends :trade_mark:

That also means that “state” is second class citizen and I should be able to delete it at any time.


From Greg Young incoming Book (unedited):

Event Sourcing says that all of our current state is derived off of the events that we store. That is it.

It only states that every piece of state that we have is derived off of this series of events which are structured into (a) log/logs.

It does not matter if we are discussing say a piece of state such as a domain object in memory or whether we are discussing a piece of state such as a table off in a database. ALL of that state is directly derived off of the log of events. Beyond this, any of that state can be thrown away at any moment and rebuilt from scratch by replaying those same events as they are by definition derived off of the log of events. The state is in fact a direct “interpretation”.


Have been over half a decade in exclusive event sourcing systems in Elixir so I am happy to see a bit more movement around the topic. Hopefully Ash understand of event sourcing wouldn’t cause problems. Definitely having fun over the weekend with it.

My immediate comment is that I do not see a way to have multiple events, or how to control the event types. Without it, it would be more CDC (Change Data Capture) than Event Sourcing, the event type dimension and control its data is extremely important to understand intent.