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

Event Sourcing - The Story Telling of Processes

Event Sourcing - The Story Telling of Processes

Almost every application has to deal with state. No matter if you like relational databases or prefer to go NoSQL the result is the same: your application only has one state.

Unfortunately, business requirements are not that static. They change constantly and so do the underlying processes. So how can you prepare your application to quickly adapt to changing processes without constantly migrating your database?

The key to event sourcing is recording what happened and build relevant data models of these events.

In this session, I will guide you through an example project that has been designed with event sourcing and how it compares to a classical CRUD application.

84cd9fd20832381fc78084aac4cb4b6c?s=128

Leif Gensert

January 23, 2018
Tweet

Transcript

  1. Event Sourcing The Story Telling of (Business) Processes

  2. None
  3. Event Sourcing The Story Telling of (Business) Processes

  4. None
  5. It’s a Tool

  6. Every single program can be reduced to Input Transformation Output

    * Former Boss
  7. Example Time

  8. None
  9. Every team plays every other team twice 306 Matches

  10. Home Team Away Team

  11. Home Team Away Team +3 +0

  12. Home Team Away Team +3 +0 +1 +1

  13. None
  14. None
  15. Transformation Input Output

  16. Transformation Input Output

  17. Transformation Input Output

  18. Transformation Input Output ?

  19. https://www.kaggle.com/mkhvalchik/soccer/data

  20. Match Team1 Team2 League SoSo Soccer

  21. None
  22. sum(m.away_team_points) as points from (select *, case when away_team_goal >

    home_team_goal then 3 when home_team_goal = away_team_goal then 1 else 0 end as away_team_points, case when away_team_goal > home_team_goal then 1 else 0 end as away_team_wins, case when away_team_goal = home_team_goal then 1 else 0 end as away_team_draws, case when away_team_goal < home_team_goal then 1 else 0 end as away_team_losses from matches) m join leagues l on m.league_id = l.id group by season, away_team_api_id) t join leagues l on t.league_id = l.id join teams te on te.api_id = t.team_id group by (t.team_id, t.season) order by points desc, goal_difference desc, goals_for desc;
  23. sum(m.away_team_points) as points from (select *, case when away_team_goal >

    home_team_goal then 3 when home_team_goal = away_team_goal then 1 else 0 end as away_team_points, case when away_team_goal > home_team_goal then 1 else 0 end as away_team_wins, case when away_team_goal = home_team_goal then 1 else 0 end as away_team_draws, case when away_team_goal < home_team_goal then 1 else 0 end as away_team_losses from matches) m join leagues l on m.league_id = l.id group by season, away_team_api_id) t join leagues l on t.league_id = l.id join teams te on te.api_id = t.team_id group by (t.team_id, t.season) order by points desc, goal_difference desc, goals_for desc;
  24. def standings(season, league_id) Repo.all(m in Match, where: m.season == ^season

    and m.league_id == ^league_id) |> Map.merge(%{row.home_team_api_id => home_team_values(row) }, &add_up/3) |> Map.merge(%{row.away_team_api_id => away_team_values(row) }, &add_up/3) |> Enum.sort_by(fn s -> {s.points, s.goal_difference, s.goals_for} end, &>=/2) end defp home_team_values(row) do %{ games: 1, wins: Rules.wins(row.home_team_goal, row.away_team_goal), draws: Rules.draws(row.home_team_goal, row.away_team_goal), losses: Rules.losses(row.home_team_goal, row.away_team_goal), goals_for: row.home_team_goal, goals_against: row.away_team_goal, points: Rules.points(row.home_team_goal, row.away_team_goal) } end defp add_up(_k, nil, v2), do: v2 defp add_up(_k, v1, v2) when is_integer(v1), do: v1 + v2 defp add_up(_k, v1, v2) when is_map(v1), do: Map.merge(v1, v2, &add_up/3)
  25. Problem?

  26. Simplicity?

  27. Performance?

  28. Change!

  29. None
  30. None
  31. None
  32. None
  33. None
  34. None
  35. None
  36. T1 T2 T3

  37. T1 T2 T3

  38. There are only two hard things in Computer Science …

  39. Rethink

  40. It’s a Tool

  41. It’s a Tool that helps you capture stuff that happened

  42. None
  43. TeamFounded TeamFounded TeamFounded TeamFounded TeamFounded

  44. TeamFounded TeamFounded TeamFounded TeamFounded TeamFounded TeamQualified TeamQualified TeamQualified

  45. TeamFounded TeamFounded TeamFounded TeamFounded TeamFounded TeamQualified TeamQualified TeamQualified SeasonStarted

  46. MatchStarted MatchStarted MatchStarted TeamFounded TeamFounded TeamFounded TeamFounded TeamFounded TeamQualified TeamQualified

    TeamQualified SeasonStarted
  47. MatchStarted MatchStarted MatchStarted TeamFounded TeamFounded TeamFounded TeamFounded TeamFounded TeamQualified TeamQualified

    TeamQualified MachEnded MachEnded MachEnded SeasonStarted
  48. MatchStarted MatchStarted MatchStarted TeamFounded TeamFounded TeamFounded TeamFounded TeamFounded TeamQualified TeamQualified

    TeamQualified MachEnded MachEnded MachEnded SeasonEnded SeasonStarted
  49. None
  50. project %TeamQualifiedForSeason{} = qualified do Ecto.Multi.insert(multi, :standings, %Standing{ season_id: qualified.season_id,

    team_api_id: qualified.team_api_id, team_long_name: qualified.team_long_name, league_id: qualified.league_id, sort_key: sort_key(0, 0, 0) }) end project %MatchEnded{} = ended do season_id = id_from_stream_id(ended.season_id) home_team = Standing.by_team_and_season(ended.home_team_api_id, season_id) away_team = Standing.by_team_and_season(ended.away_team_api_id, season_id) home_team_diff = team_changeset(home_team, ended.home_team_goal, ended.away_team_goal) away_team_diff = team_changeset(away_team, ended.away_team_goal, ended.home_team_goal) multi |> Ecto.Multi.update(:home_team, home_team_changeset) |> Ecto.Multi.update(:away_team, away_team_changeset) end
  51. None
  52. (Re)Tell (Re)Tell (Re)Tell Capture

  53. It’s a Tool that helps you capture events …

  54. … and tell a story from them

  55. Commands Aggregates Process Handlers

  56. Greg Young (yt/LDW0QWie21s) SoSo Soccer (gh/leifg/so_so_soccer) Commanded (gh/commanded) leif.io