News for hackers. Stuff that matters.
Re:Combining event sourcing and stateful systems by withinboredom on Tuesday April 21, 2020 @08:01AM (#22934124)
You should take a look at Microsoft's Durable Functions which pairs event sourcing + (optional) actor model + serverless. It's some pretty neat tech.

I tried doing something similar to this several years ago, and here's a few issues I ran into:

1. Pub/sub in Event Sourcing is a bad idea. It's really hard to get right. (what to do if sub happens after pub due to scaling issues/infrastructure, etc?) Instead it's better to push commands deliberately to a process manager that handles the inter-domain communication and orchestration.

2. Concurrency. Ensuring aggregates are essentially single-threaded entities is a must. Having the same aggregate id running in multiple places can cause some really fun bugs. This usually requires a distributed lock of some sort.

3. Error handling. I ended up never sending a command to a domain directly, instead I sent it to a process manager that could handle all the potential failure cases.

Re:Combining event sourcing and stateful systems by slashdotdash on Tuesday April 21, 2020 @09:44AM (#22934905)
For anyone interested in event sourcing with the actor model I've built an open source Elixir library called Commanded (https://github.com/commanded/commanded) which takes advantage of Erlang's BEAM VM to host aggregate processes. There's also an event store implemented in Elixir which uses Postgres for storage (https://github.com/commanded/eventstore).

The actor model provides the guarantee that requests to a single instance are processed serially, while requests to different instances can be processed concurrently. Distributed Erlang allows these instances to be scaled out amongst a cluster of nodes with transparent routing of commands to the instance, regardless of which connected node it is running on.

In Elixir and Erlang, the OTP platform provides the building blocks to host an aggregate instance as a process (as a `GenServer`). Following the "functional core, imperative shell" style I model the domain code as pure functions with the host process taking care of any IO, such as reading and appending the aggregate's events.

Re:Combining event sourcing and stateful systems by sbellware on Tuesday April 21, 2020 @02:54PM (#22938124)
> Pub/sub in Event Sourcing is a bad idea

I find this point surprising. I would say the exact opposite. I would say that pub/sub and event sourcing are two sides of the same coin: events.

> what to do if sub happens after pub

That should only ever be a problem with a non-durable transport that doesn't have serialized writes per topic. Which, admittedly, can be pretty common. But it's not so much an event sourcing or pub/sub issue as much as a choice of message transport issue.

> Concurrency. Ensuring aggregates are essentially single-threaded entities is a must. Having the same aggregate id running in multiple places can cause some really fun bugs. This usually requires a distributed lock of some sort.

Or it requires partitioning the queues and using an optimistic lock when writing (just to be on the safe side).

Re:Combining event sourcing and stateful systems by withinboredom on Wednesday April 22, 2020 @06:29AM (#22944105)
> I find this point surprising. I would say the exact opposite. I would say that pub/sub and event sourcing are two sides of the same coin: events.

I meant in the context of getting it right. I didn't experiment with all the pub/sub systems at the time, but most I experimented with would lose data in a catastrophic event and cause inconsistencies. This was several years ago though.

Re:Combining event sourcing and stateful systems by sbellware on Wednesday April 22, 2020 @02:17PM (#22948540)
> most I experimented with would lose data in a catastrophic event and cause inconsistencies

Fair enough. Those are probably message buses or message queues that are ephemeral transports. Since event sourcing is predicated upon permanent storage of events, there's no way to lose events that have already been committed (unless someone actually physically deletes the events).

Re:Combining event sourcing and stateful systems by vorpalhex on Tuesday April 21, 2020 @10:39AM (#22935307)
On [1] - You're correct that Pub/Sub is difficult to get right, but it can confer a bunch of benefits which make it worthwhile to struggle with. RabbitMQ + Replay-ability (the details of which will differ based on your design) + good data model design is usually a safe bet here.
Re:Combining event sourcing and stateful systems by alextheparrot on Tuesday April 21, 2020 @11:41AM (#22935936)
Is (2) motivated by using aggregates which do not commute or by trying to do distributed modifications on a single value?

One common technique if you have commutative aggregates is to have each writer just write to their own spot and then do a range query on read to re-join. If your aggregates don’t commute this, of course, doesn’t work and you’re stuck in “single threaded” land. I do remember reading a paper that avoided this, but i can’t remember the implementation / trade offs (If I can find the paper I’ll post it here)

Re:Combining event sourcing and stateful systems by Twisol on Tuesday April 21, 2020 @03:28PM (#22938463)
What do you mean by commutative aggregates? My understanding is that an aggregate is state, whereas commutativity is a property between two operations on said state. Do you mean something like CRDTs?
Re:Combining event sourcing and stateful systems by dllthomas on Tuesday April 21, 2020 @05:44PM (#22939647)
I think what's meant is that the event stream can be reordered and give the same result.

This isn't necessarily an abuse of terminology - I believe if we look at an operator that takes sequences of events and appends them, what we're talking about is in fact commutativity of that operator (in terms of the impact on the system).

Re:Combining event sourcing and stateful systems by Twisol on Tuesday April 21, 2020 @10:31PM (#22941451)
You're right, you can line the two concepts up to some extent. (I mentioned CRDTs because they're one way to do that in a particularly nice way.) But given the context of the question, it seemed like teasing out the difference would be helpful.
Re:Combining event sourcing and stateful systems by alextheparrot on Tuesday April 21, 2020 @05:54PM (#22939741)
I'm not familiar with CRDTs, though I'll look into them -- thanks for the pointer. To clarify my original idea/question, the following example:

Yeah sure, imagine you're trying to compute count of how many times you've seen something. This is a SUM aggregate.

I assume the reason the author said "Ensuring aggregates are essentially single-threaded entities is a must" was because they are mutating a single key, if two different processes try to change the same value, you get inconsistent state.

An example of this would be:

Current K/V Pair: "A" -> 2

Modifier 1: Reads "A", gets 2

Modifier 2: Reads "A", gets 2

Modifier 1: Writes "A" -> 3

Modifier 2: Writes "A" -> 3

This means our sum is now inconsistent, as we would expect the value to be 4 in a single-threaded system.

However, because we are doing a sum, we can use commutativity to remove this conflict. Instead of each writer trying to write to a single key, you might make a compound key (Id, <Unique Writer Identifier>)

Current Value Pairs: [("A", "Modifier 1") -> 1, ("A", "Modifier 2") -> 1]

Modifier 1: Reads ("A", "Modifier 1"), gets 1

Modifier 2: Reads ("A", "Modifier 2"), gets 1

Modifier 1: Writes ("A", "Modifier 1") -> 2

Modifier 2: Writes ("A", "Modifier 2") -> 2

Then, a reader could just ask for all the keys with the prefix "A" (This is the range query). So a reader gets back (2, 2), which then can now merge into 4 as SUM is a commutative operation. Because the reader is taking care of the final aggregation step, there's no concurrency conflict so you can get away with having any number of writers as long as the read and read-side computation is cheap enough.

Some aggregates are commutative only in certain forms, like AVERAGE. To explain further, if one writer says the AVERAGE is 5 and another says it is 7, I can't combine those to say that the global average is 6.

However, if each writer stores both SUM and COUNT, I can use (COUNT1 + COUNT2)/(SUM1 + SUM2). This is because division doesn't commute, so I have to delay the non-commutative operation until the reader if I want to be able to merge two data sources.

Edited: Formatting

Re:Combining event sourcing and stateful systems by Twisol on Tuesday April 21, 2020 @10:28PM (#22941425)
I see. I think you may be using a different definition of "aggregate" than the article and OP are referring to. Your "aggregate" is that from database systems like SQL and Excel, while the OP's "aggregate" is a different concept taken from the lexicon of Domain-Driven Design.

In DDD, an "aggregate" is a collection of state that can only be changed atomically, as a whole, by the outside world. It helps to think concretely of an Actor, i.e. some entity that owns a collection of state, such that the only way to mutate that state is to send a message to the actor. No matter how many messages come in from the outside world, only one is being handled at a time by the entity, and there are no concerns of concurrent access. Necessarily though, all messages are handled in a linear order.

From Martin Fowler's article on aggregates [0]:

> Aggregates are the basic element of transfer of data storage - you request to load or save whole aggregates. Transactions should not cross aggregate boundaries.

I think you'll be interested in CRDTs. They're like DDD aggregates where mutation operations are essentially always commutative, so there's a bit more alignment between the senses of "aggregate" being confused here.

[0] https://www.martinfowler.com/bliki/DDD_Aggregate.html