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.

Leif Gensert

January 23, 2018
Tweet

More Decks by Leif Gensert

Other Decks in Programming

Transcript

  1. 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;
  2. 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;
  3. 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)
  4. 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