Contact Us

If you still have questions or prefer to get help directly from an agent, please submit a request.
We’ll get back to you as soon as possible.

Please fill out the contact form below and we will reply as soon as possible.

  • Contact Us
  • Home
  • System Architecture

Event Sourcing

Written by Oleksandr Sydorenko

Updated at May 5th, 2025

Contact Us

If you still have questions or prefer to get help directly from an agent, please submit a request.
We’ll get back to you as soon as possible.

Please fill out the contact form below and we will reply as soon as possible.

  • System Architecture
+ More

Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowchart; it’ll be obvious.

—Fred Brooks1

Let’s use Fred Brooks’s reasoning to define the event sourcing pattern and understand how it differs from traditional modeling and persisting of data. Examine Table 7-1 and analyze what you can learn from this data about the system it belongs to.


1 Brooks, F. P. Jr. (1974). The Mythical Man-Month: Essays on Software Engineering. Reading, MA: Addison- Wesley.

Table 7-1. State-based model

lead- id

first-name

last- name

status

phone- number

followup-on

created-on

updated-on

1

Sean

Callahan

CONVERTED

555-1246

2019-01-31T

10:02:40.32Z

2019-01-31T

10:02:40.32Z

2

Sarah

Estrada

CLOSED

555-4395

2019-03-29T

22:01:41.44Z

2019-03-29T

22:01:41.44Z

3

Stephanie

Brown

CLOSED

555-1176

2019-04-15T

23:08:45.59Z

2019-04-15T

23:08:45.59Z

4

Sami

Calhoun

CLOSED

555-1850

2019-04-25T

05:42:17.07Z

2019-04-25T

05:42:17.07Z

5

William

Smith

CONVERTED

555-3013

2019-05-14T

04:43:57.51Z

2019-05-14T

04:43:57.51Z

6

Sabri

Chan

NEW_LEAD

555-2900

2019-06-19T

15:01:49.68Z

2019-06-19T

15:01:49.68Z

7

Samantha

Espinosa

NEW_LEAD

555-8861

2019-07-17T

13:09:59.32Z

2019-07-17T

13:09:59.32Z

8

Hani

Cronin

CLOSED

555-3018

2019-10-09T

11:40:17.13Z

2019-10-09T

11:40:17.13Z

9

Sian

Espinoza

FOLLOWUP_SET

555-6461

2019-12-04T

01:49:08.05Z

2019-12-04T

01:49:08.05Z

2019-12-04T

01:49:08.05Z

10

Sophia

Escamilla

CLOSED

555-4090

2019-12-06T

09:12:32.56Z

2019-12-06T

09:12:32.56Z

11

William

White

FOLLOWUP_SET

555-1187

2020-01-23T

00:33:13.88Z

2020-01-23T

00:33:13.88Z

2020-01-23T

00:33:13.88Z

12

Casey

Davis

CONVERTED

555-8101

2020-05-20T

09:52:55.95Z

2020-05-27T

12:38:44.12Z

13

Walter

Connor

NEW_LEAD

555-4753

2020-04-20T

06:52:55.95Z

2020-04-20T

06:52:55.95Z

14

Sophie

Garcia

CONVERTED

555-1284

2020-05-06T

18:47:04.70Z

2020-05-06T

18:47:04.70Z

15

Sally

Evans

PAYMENT_FAILED

555-3230

2020-06-04T

14:51:06.15Z

2020-06-04T

14:51:06.15Z

16

Scott

Chatman

NEW_LEAD

555-6953

2020-06-09T

09:07:05.23Z

2020-06-09T

09:07:05.23Z

17

Stephen

Pinkman

CONVERTED

555-2326

2020-07-20T

00:56:59.94Z

2020-07-20T

00:56:59.94Z

18

Sara

Elliott

PENDING_PAYMENT

555-2620

2020-08-12T

17:39:43.25Z

2020-08-12T

17:39:43.25Z

19

Sadie

Edwards

FOLLOWUP_SET

555-8163

2020-10-22T

12:40:03.98Z

2020-10-22T

12:40:03.98Z

2020-10-22T

12:40:03.98Z

20

William

Smith

PENDING_PAYMENT

555-9273

2020-11-13T

08:14:07.17Z

2020-11-13T

08:14:07.17Z

It’s evident that the table is used to manage potential customers, or leads, in a tele‐ marketing system. For each lead, you can see their ID, their first and last names, when the record was created and updated, their phone number, and the lead’s current status.

By examining the various statuses, we can also assume the processing cycle each potential customer goes through:

• The sales flow starts with the potential customer in the NEW_LEAD status.

• A sales call can end with the person not being interested in the offer (the lead is CLOSED), scheduling a follow-up call (FOLLOWUP_SET), or accepting the offer (PENDING_PAYMENT).

• If the payment is successful, the lead is CONVERTED into a customer. Conversely, the payment can fail—PAYMENT_FAILED.

That’s quite a lot of information that we can gather just by analyzing a table’s schema and the data stored in it. We can even assume what ubiquitous language was used when modeling the data. But what information is missing from that table?

The table’s data documents the leads’ current states, but it misses the story of how each lead got to their current state. We can’t analyze what was happening during the lifecycles of leads. We don’t know how many calls were made before a lead became CONVERTED. Was a purchase made right away, or was there a lengthy sales journey? Based on the historical data, is it worth trying to contact a person after multiple follow-ups, or is it more efficient to close the lead and move to a more promising prospect? None of that information is there. All we know are the leads’ current states.

These questions reflect business concerns essential for optimizing the sales process. From a business standpoint, it’s crucial to analyze the data and optimize the process based on the experience. One of the ways to fill in the missing information is to use event sourcing.

The event sourcing pattern introduces the dimension of time into the data model. Instead of the schema reflecting the aggregates’ current state, an event sourcing– based system persists events documenting every change in an aggregate’s lifecycle.

Consider the CONVERTED customer on line 12 in Table 7-1. The following listing dem‐ onstrates how the person’s data would be represented in an event-sourced system:

{

"lead-id": 12,

"event-id": 0,

"event-type": "lead-initialized",

"first-name": "Casey",

"last-name": "David",

"phone-number": "555-2951",

"timestamp": "2020-05-20T09:52:55.95Z"

},

{

"lead-id": 12,

"event-id": 1,

"event-type": "contacted",

"timestamp": "2020-05-20T12:32:08.24Z"

},

{

"lead-id": 12,

"event-id": 2,

"event-type": "followup-set",

"followup-on": "2020-05-27T12:00:00.00Z",

"timestamp": "2020-05-20T12:32:08.24Z"

},

{

"lead-id": 12,

"event-id": 3,

"event-type": "contact-details-updated",

"first-name": "Casey",

"last-name": "Davis",

"phone-number": "555-8101",

"timestamp": "2020-05-20T12:32:08.24Z"

},

{

"lead-id": 12,

"event-id": 4,

"event-type": "contacted",

"timestamp": "2020-05-27T12:02:12.51Z"

},

{

"lead-id": 12,

"event-id": 5,

"event-type": "order-submitted",

"payment-deadline": "2020-05-30T12:02:12.51Z",

"timestamp": "2020-05-27T12:02:12.51Z"

},

{

"lead-id": 12,

"event-id": 6,

"event-type": "payment-confirmed",

"status": "converted",

"timestamp": "2020-05-27T12:38:44.12Z"

}

The events in the listing tell the customer’s story. The lead was created in the system (event 0) and was contacted by a sales agent about two hours later (event 1). During the call, it was agreed that the sales agent would call back a week later (event 2), but to a different phone number (event 3). The sales agent also fixed a typo in the last name (event 3). The lead was contacted on the agreed date and time (event 4) and submit‐ ted an order (event 5). The order was to be paid in three days (event 5), but the

payment was received about half an hour later (event 6), and the lead was converted into a new customer.

As we saw earlier, the customer’s state can easily be projected out from these domain events. All we have to do is apply simple transformation logic sequentially to each event:

public class LeadSearchModelProjection

{

public long LeadId { get; private set; }

public HashSet<string> FirstNames { get; private set; }

public HashSet<string> LastNames { get; private set; }

public HashSet<PhoneNumber> PhoneNumbers { get; private set; }

public int Version { get; private set; }

public void Apply(LeadInitialized @event)

{

LeadId = @event.LeadId;

FirstNames = new HashSet<string>(); LastNames = new HashSet<string>(); PhoneNumbers = new HashSet<PhoneNumber>(); FirstNames.Add(@event.FirstName); LastNames.Add(@event.LastName); PhoneNumbers.Add(@event.PhoneNumber); Version = 0;

}

public void Apply(ContactDetailsChanged @event)

{

FirstNames.Add(@event.FirstName); LastNames.Add(@event.LastName); PhoneNumbers.Add(@event.PhoneNumber); Version += 1;

}

public void Apply(Contacted @event)

{

Version += 1;

}

public void Apply(FollowupSet @event)

{

Version += 1;

}

public void Apply(OrderSubmitted @event)

{

Version += 1;

}

public void Apply(PaymentConfirmed @event)

{

Version += 1;

}

}

Iterating an aggregate’s events and feeding them sequentially into the appropriate overrides of the Apply method will produce precisely the state representation mod‐ eled in the table in Table 7-1.

Pay attention to the Version field that is incremented after applying each event. Its value represents the total number of modifications made to the business entity. More‐ over, suppose we apply a subset of events. In that case, we can “travel through time”: we can project the entity’s state at any point of its lifecycle by applying only the rele‐ vant events. For example, if we need the entity’s state in version 5, we can apply only the first five events.

Finally, we are not limited to projecting only a single state representation of the events! Consider the following scenarios.

Was this article helpful?

Yes
No
Give feedback about this article

Related Articles

  • Discovering Domain Knowledge
  • Business Problems
  • Knowledge Discovery
  • Communication
  • What Is a Ubiquitous Language?

info@smartphonekey.com

  • Home
  • How It Works
  • Features
  • Residents and Tenants
  • Property Managers
  • Airbnb Hosts
  • Products
  • Blog
  • Guide for Usage and Installation
  • Our Team
  • Contact Us
  • Privacy Policy
  • Terms of Service
  • Facebook
  • Instagram
  • LinkedIn
© 2025, Smartphonekey.com Powered by Shopify
Expand