Billede

Oliver Sturm had an interesting talk on day 3 of SDD Conference. He talked about the event sourcing architecture, that can be used for highly performing and error resilient communication in a software system (applicable to both SOA and Microservice applications).

Event sourcing

Event sourcing is an architectural pattern that records all changes to an application's state as a sequence of events. These events are then stored in an event store - usually a database that is optimized for storing and querying events.

Event store

When an event is created, it is assigned a unique identifier and a timestamp. The event also includes information about the entity that was changed, the type of change that was made, and the old and new values of the entity.

The event store is typically append-only, which means that events can only be added to the end of the log. This makes it very efficient to query the event store, as only the most recent events need to be scanned.

A normal pattern is to have an endpoint that receives all requests coming in to the system and saving them in the event store. This is a database that holds all the requests that comes in to the system, so that they can be inspected at a later time if neccesary. The event store will send a message to the event bus, when the request has been saved. Normally the data fra the request is not changed in anyway ie. the data saved to the event store database is exactly the same, that is sent to the event bus.

Read model

A read model is a representation of the current state of an application that is derived from the event stream coming from the event bus (ie. the messages that was forwarded from the event store service). The read model is used to provide a consistent view of the application state to clients, even if the application has been updated multiple times since the client last requested data.

A read model will usually be responsible for a subset of the data. This could be customers, the stock in the warehouse or something completely else. This means that there will be multiple read model, each responsible for handling queries regarding a subset of the data. The read model is populated by replaying the event stream - but only the events that relate to the entities that is relevant for that read model (eg. changes to customers). This means that the event store is read from start to finish, and each event is used to update the read model.

Using VAR for failure recovery 

The event stream provides the possiblity to replay everything that happend from a certain point in time (or from the beginning if needed). This means that there's always the option to use the event messages as VAR in football, where we can replay everything that has happend over and over again (and debug it if we need to). 

An error happend due to a bug in the code, can be fixed and when the fix has been deployed, the event stream can be replayed and the data can be verified. If everything looks to be in order, we can drop the old read model database, create a new database and replay the event stream to repopulate the new database with the correct data.

Event sourcing framework for C#

The best event sourcing framework for you will depend on your specific needs and requirements. Here's a few of the most popular frameworks for C#

Axon Framework

Axon Framework is a complete event sourcing framework that provides a number of features, including event sourcing, command bus, query bus, and event store. If you are looking for a complete framework with a lot of features, then Axon Framework is a good option. 

EventStore.NET

EventStore.NET is an event store that can be used with any event sourcing framework. It provides a number of features, including append-only storage, event compaction, and event replication. If you are looking for an event store that can be used with any event sourcing framework, then EventStore.NET is a good option.

SimpleCQRS

SimpleCQRS is a lightweight event sourcing framework that is easy to use. It provides a number of features, including command bus, query bus, and event store. If you are looking for a lightweight framework that is easy to use, then SimpleCQRS is a good option. 

 

Steffen_actors_parents_children_working_hard_camera_cartoon_sty_69608d69-e3b6-498f-834a-eeb9a2fddf72.png

One of the talks on day 2 of SDD 2023, was called "Let's stop programming like 2017". The talk was held by Richard Blewitt (@RichardBlewitt) and Andrew Clymer (@AndrewClymer) from https://www.rocksolidknowledge.com/. I've been to a few talks with them and they are an absolute blast. Always lots of good laughs - just the way I like it!

In the talk they went through some of the improvements that has been made over the last years in C#. I will not go through all of them, but one really stood out to me: Pattern matching

In the bustling world of code, finding specific properties can be as challenging as spotting Waldo in a crowded scene. But fear not! C# 11's property pattern matching saves the day! It's a stylish and powerful feature that lets you uncover the hidden treasures of your objects with ease. So get out your magnifying glass and get ready to unravel the secrets of this exciting feature, transforming the search for Waldo (or any property) into a stylish and enjoyable code adventure.

Property pattern matching

One of the types of pattern matching that we can do is called property pattern matching. Property pattern matching in C# 11 is a feature that allows you to match against specific properties within objects. It provides a concise and expressive syntax for performing pattern matching based on the values of object properties.

With property pattern matching, you can easily extract relevant information or perform specific actions based on the properties of an object. It simplifies complex conditional logic by enabling you to match against property values directly within patterns.

The syntax for property pattern matching involves specifying the property name and its expected value in a pattern. This feature is particularly useful when dealing with objects that have multiple properties and you need to selectively handle different combinations of property values. It enhances code readability and conciseness, making it easier to write and understand complex matching scenarios.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public string GetGreeting(Person person)
{
    return person switch
    {
        { Name: "Alice" } => "Hello, Alice!",
        { Name: "Bob", Age: 25 } => "Hey Bob, you're 25!",
        { Age: >= 18 } => "Welcome, adult!",
        _ => "Greetings!"
    };
}

In the example above, we have a `Person` class with properties `Name` and `Age`. The `GetGreeting` method takes a `Person` object and returns a greeting based on different matching patterns.

In the first pattern, `{ Name: "Alice" }`, we match against a `Person` object with the name "Alice". If the `Name` property matches "Alice", the corresponding greeting "Hello, Alice!" is returned.

In the second pattern, `{ Name: "Bob", Age: 25 }`, we match against a `Person` object with the name "Bob" and age 25. If both properties match the specified values, the greeting "Hey Bob, you're 25!" is returned.

The third pattern, `{ Age: >= 18 }`, matches any `Person` object where the `Age` property is greater than or equal to 18. If the age condition is met, the greeting "Welcome, adult!" is returned.

Finally, the underscore `_` acts as a catch-all pattern that matches any `Person` object not matched by the previous patterns. In this case, the generic greeting "Greetings!" is returned.

Property pattern matching allows for concise and expressive code by directly matching against object properties within patterns, making it easier to handle complex conditional logic and extract values from objects.

Type pattern matching

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public string GetShapeInfo(object shape)
{
    return shape switch
    {
        Circle c => $"Circle with radius {c.Radius}",
        Rectangle r => $"Rectangle with width {r.Width} and height {r.Height}",
        _ => "Unknown shape"
    };
}

public class Circle
{
    public double Radius { get; set; }
}

public class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }
}

In the example above, we have a GetShapeInfo method that takes an object representing a shape. We use type pattern matching within the switch statement to determine the type of the shape and provide specific information based on that type.

When the shape matches the Circle type, we can access its properties directly within the pattern and return a formatted string with the radius value.

Similarly, when the shape matches the Rectangle type, we can access its properties and return a formatted string with the width and height values.

The underscore _ acts as the default pattern and matches any object that doesn't match the previous patterns. In this case, it returns "Unknown shape".

Type pattern matching allows us to check and extract specific types from objects, making it easier to handle different cases and provide type-specific behavior or information.