2

I am looking into Event Sourcing.

One of the key points is about saving the differences to the event store rather than objects themselves.

This is fine in simple examples. However, I am struggling with the correct way to do this in the, more complex, real world (i.e. you could have an object with 20 properties on being updated, or the incoming updates could be a list).

The EventStore example uses a Json file. Marten seems to take an event object.

To my mind, this means one needs to either:

Option 1 (see what's changed)

  • Use reflection to get the differences in state between 2 objects
  • Serialize that information to Json (in the case of EventStore), or use another object for Marten.
  • Have a means of deserializing that information and rebuilding state if needed. .

The downsides are:

  • Add extra work to the system (millions of reflections and serializations on potentially complex objects)
  • Require you to have a strict way of message handling to rebuild the model. This could also be brittle when the model changes

Option 2 (just assume there's changes)

If a list comes in, rather than comparing, just assume the data coming in are changes and use one Event to store all that info.

Where the downside is:

  • Some changes might not happen (e.g. an "update" is the same as the original), so it's not a true reflection of changes (though it is a true reflection of events).

Option 3 (manual comparisons, lots of events)

  • Manually compare ClassA.PropA with ClassB.PropA.
  • If it's changed, create a PropAChanged event

The downsides appear to be:

  • If there are 20 properties on a class, we have a lot of tedious typing.
  • Doesn't seem very DRY.
  • I guess you may also have to pay more attention to the order of events too.

I'm thinking option 2 is the way to go.

Is this correct? If not, what is the correct way to do this please?

  • 2
    A few points. When using event sourcing, you don't have to (and probably shouldn't) do it for the entire system, but for a select number of domain processes that have a temporal component - find things that are better expressed as a series of events. Second, you shouldn't really be changing the object then doing a diff, you are not writing changes after-the-fact. Instead, your behavior (use cases) should be centered around emitting events, which in turn result in jointly updating the target object and in publishing an entry to the event store (various approaches there). – Filip Milovanović Oct 7 '19 at 17:16
  • 1
    (continued...) Also don't think of the event store as of a private database, as that's not what it really is; the events have to be carefully thought out as they represent the communication interface for the interacting components that write to and read from the stream. P.S. To learn more, find anything you can from Greg Young (articles, videos, etc.). – Filip Milovanović Oct 7 '19 at 17:16
  • @Filip_Milovanović . Great input. Thanks :) – HockeyJ Oct 7 '19 at 17:31
5

I think you misunderstand the concept of event sourcing a bit. You still have the base concept of an "object" with "properties", and you conceptualize event sourcing as another weird way of encoding "updates" to those "properties", which might otherwise be handled by a relational database (as columns in a table), or by a NoSQL database (as keys and values), or something else.

This is not the right way of looking at event sourcing.

The idea is to record not "what changed", but rather "what happened". This distinction may seem trivial, but it is very important.

Further, the idea of an "object" is rather ephemeral and doesn't necessarily correspond to a "class" in C#. An "object" is a real-world object, and may be represented by different classes in different circumstances.

For example, if I'm talking to a banker, I may be represented by:

class Customer {
    public string AccountNumber { get; set; }
    public double CurrentBalance { get; set; }
}

But if I'm talking to a policeman, I may be represented by:

class Citizen {
    public string DriversLicense { get; set; }
    public int MovingInfractions { get; set; }
    public int PriorArrests { get; set; }
}

Here, banker and policeman would be roughly analogous to different services (or modules, or whatever you call them) in your system. They are looking at me from different angles and are, therefore, interested in different events.

The former might be affected by events like MoneyWithdrawn, MoneyDeposited, and AccountCreated.
The latter might consume events like MovingViolationCommitted or ArrestMade.

It is true that ArrestMade can be considered to be "changing the value of PriorArrests", but it is not named IncreasePriorArrests, because that's not what really happened. Naming an event like that would hide some aspects of it, because it is conceivable that the number of prior arrests might be changed by some other event, like expunging the record.

A better approach to event sourcing is to record what actually happened, and then construct the "current slice" using only those events that are interesting in the given context.

The main point is this: events are the primary thing, and objects (or records, or table, or whatever) are only derived from events, not the other way around. This is true both technically and, more importantly, logically.

It is true that many modern event sourcing libraries make the object center of everything, giving it the ability to "respond" to events by mutating its internal state. I think this is a tragic consequence of the prevalence of the enterprise flavor of OO (see C++, Java, C#) in the modern programming landscape, where it is very awkward and sometimes impossible to get away from the idea of a central object.

But the concept of event sourcing itself is closer to functional programming, where the idea of fold (known as Aggregate in LINQ) is an everyday reality and not surprising to anyone.

|improve this answer|||||
  • 1
    This is such a brilliant answer. Thank you! – HockeyJ Oct 7 '19 at 17:31
  • 1
    Glad I could help. Good luck. – Fyodor Soikin Oct 7 '19 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.