Building Event Sourced Apps

Building Event Sourced Apps

Designing your software around a story!

What is Event Sourcing?

Instead of defining a global data model that fits all of your use cases think about the things your system does. Record the events that occur and then build multiple data models that fit your individual use cases.

Sounds very abstract? In this talk, Leif will elaborate on some side projects including use cases with the hope to encourage you to switch to more event-centric thinking.

84cd9fd20832381fc78084aac4cb4b6c?s=128

Leif Gensert

January 30, 2019
Tweet

Transcript

  1. 2.
  2. 3.
  3. 7.
  4. 8.
  5. 12.

    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;
  6. 13.

    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)
  7. 14.
  8. 17.
  9. 18.
  10. 20.
  11. 24.
  12. 25.

    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
  13. 26.
  14. 32.
  15. 38.
  16. 39.
  17. 40.
  18. 41.

    Offer Price: £1000 Buyer: E-Corp Status: “accepted” SELECT SUM(price) FROM

    offers WHERE status = ‘accepted’ GROUP BY buyer_id; Offer Price: £1000 Buyer: E-Corp Status: “paid” SELECT SUM(price) FROM offers WHERE status in (‘accepted’, ‘paid’) GROUP BY buyer_id;
  19. 42.

    Offer Price: £1000 Buyer: E-Corp Status: “accepted” SELECT SUM(price) FROM

    offers WHERE status = ‘accepted’ GROUP BY buyer_id; Offer Price: £1000 Buyer: E-Corp Status: “rejected”
  20. 43.

    Event Sourcing is Great for Side Projects It’s also Great

    for Real World Applications leif.io If you know the business bit.ly/buildingconduit