Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mastering Event Sourcing: Your Parents Holidaye...

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Mastering Event Sourcing: Your Parents Holidayed in Yugoslavia

Event sourcing for a world that keeps changing

Your parents flew to Split for their summer holiday. They visited Yugoslavia. But Yugoslavia doesn't exist any more — so why does your flight tracker say they went to Croatia?

This is the question that made me realise my simple CRUD approach wasn't simple enough any more.

This talk is the story of how a hobby flight logger app outgrew its database, what event sourcing looks like in practice, and what you can take back to your projects. We'll walk through the core ideas — append-only event logs, projections, temporal queries — using real examples from aviation data where airlines merge, airports get renamed, and borders shift across decades.

You'll leave with a clear mental model of event sourcing, an understanding of when it earns its complexity, and a map of the PHP-ecosystem libraries that let you start using it tomorrow.

Avatar for Marek Matulka

Marek Matulka

March 24, 2026
Tweet

More Decks by Marek Matulka

Other Decks in Programming

Transcript

  1. Europe, 2010 Your holiday with your parents, to celebrate their

    30th anniversary… EasyJet flight from Gatwick, UK to Split, Yugo… Croatia.
  2. Living data Airlines • adopt new branding • merge with

    another, or split • cease or restart operations Airports • adopt new name • adopt new designators • move countries • open or close Countries • dissolve • gain independence • join or leave United Nation • adopt new name • adopt new flag • get occupied or annexed
  3. How it started – the naive approach flight_segments --------------- id

    flight_date flight_number airline_id departure_airport_id arrival_airport_id airlines -------- id name logo icao iata country airports -------- id name icao iata country
  4. Users: We want to see all the countries we've visited

    Changes in the early 1990s: • Czechoslovakia – split into Czechia and Slovakia • Estonia, Latvia, Lithuania – restored independence • Soviet Union – split into Armenia, Azerbaijan, Belarus, Georgia, Ukraine, … • East Germany and West Berlin – merged with Germany Recent new countries: • Timor-Leste (2002) – restored independence from Indonesia • Kosovo (2008) – split from Serbia • Aruba, Curaçao, Sint Maarten, Caribbean Netherlands (Bonaire, Saba, Sint Eustatius) (2010) – dissolution of Netherlands Antilles • South Sudan (2011) – split from Sudan
  5. Borders change, but also… Airlines… • USAir rebranded as US

    Airways ◦ adopted new name, branding, designators… • United merged with Continental ◦ kept United as a name ◦ adopted Continental's branding • Alitalia went bust number of times ◦ merged with number of airlines, to stay afloat… • British Airways absorbed bmi ◦ bmi ceased to exist • rocky history of FlyBe ◦ ceased and restarted operations ◦ resurrected as a new airline Airports… • DOH designator was used by Doha International (OTBD) till 2014, then Hammad International (OTHH) • HKG and VHHH designators moved with the airport, when Hong Kong's iconic Kai Tak airport closed • Johannesburg airport (JNB) changed it's ICAO designator in 2013 • Las Vegas Airport (LAS) was called McCarran International between 1948 and adopted Harry Reid International name in 2021
  6. The fundamental tension – current state vs historical truth flight_segments

    --------------- id flight_date flight_number airline_id departure_airport_id arrival_airport_id airlines -------- id version_id name logo icao iata country_id airports -------- id version_id name icao iata country_id countries --------- id version_id code name
  7. Every query became: • which version was active on this

    date? – required a correlated subquery on every join • every time I wrote a new query I had to decide: do I join on id or version_id? The answer wasn't always obvious, and getting it wrong meant silently incorrect data • all joins add performance penalty The fundamental tension – current state vs historical truth
  8. Don't update, append events Events represent the source of truth,

    paired with effective date — what has happened, and when.
  9. Don't update, append events Each event is paired with a

    command — commands are instructions to change our model.
  10. You don't need to get it right first time, because

    the facts are preserved regardless.
  11. Migration strategy • Built alongside legacy ORM tables ◦ Migration

    command ORM table → events • Verification ◦ Command checked records matched legacy ORM table vs projections • Repository swap ◦ Replaced ORM repositories with projection repositories
  12. What we event-sourced (and what we didn't) Event-sourced • Airline

    • Airport • Country Why? • Change over time • Historical accuracy CRUD • Aircraft Type ◦ they don't change ◦ there might be multiple variants • Flight Segment ◦ a flight segment is an event itself ◦ user may add missing details to it (e.g. aircraft tail number), but flight segment does not change over time • Traveller ◦ little benefit, audit log is simpler
  13. E ective date vs. event timestamp Effective date – when

    the fact actually happened in the real world. 1991-06-25, when Croatia declared independence Event timestamp – when you recorded the fact in the system. Today, when you imported the data.
  14. Projections are disposable Events are the source of the truth

    Clean separation between business logic (changes to models), and presentation – achieving Command Query Responsibility Segregation (CQRS).
  15. The cost Pros • historical accuracy • fast data reads

    (flat DB table) • stats can be calculated in memory Cons • slower writes • projection rebuild takes time • more moving parts (code)
  16. When it earns its complexity Your checklist: ❏ do you

    have temporal queries? ❏ audit requirements? ❏ data that changes meaning retroactively?
  17. Laravel ecosystem Spatie's Laravel Event Sourcing • Docs: spatie.be/docs/laravel-event-sourcing •

    github.com/spatie/laravel-event-sourcing ✅ Probably the best choice to start with. EventSauce • Web: eventsauce.io • Docs: eventsauce.io/docs/ • github.com/spatie/laravel-eventsauce Broadway • Docs: broadway.github.io/broadway/ • github.com/nWidart/Laravel-broadway
  18. Symfony ecosystem EventSauce • Web: eventsauce.io • Docs: eventsauce.io/docs/ •

    github.com/EventSaucePHP/EventSauce Broadway • Docs: broadway.github.io/broadway/ • github.com/broadway/broadway Prooh • Web: getprooph.org/ • github.com/prooph/event-store DIY • DBAL append-only tables with events • Bit of code
  19. Where to start ❏ pick one aggregate that has a

    temporal problem ❏ don't rewrite everything at once ❏ built it alongside existing CRUD data and verify ❏ compare CRUD vs projection ❏ you won't get it right first time round, but rebuilding projections is trivial, fixing CRUD data isn't
  20. Summary Three takeaways: • Append don't update • Projections are

    views • Historical truth is a first-class concern