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

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.

Leif Gensert

January 30, 2019
Tweet

More Decks by Leif Gensert

Other Decks in Programming

Transcript

  1. Building Event Sourced
    Apps

    View Slide

  2. View Slide

  3. View Slide

  4. My Example

    View Slide

  5. Every team plays every other team twice
    306 Matches

    View Slide

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

    View Slide

  7. View Slide

  8. View Slide

  9. Transformation
    Input
    Output
    ?

    View Slide

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

    View Slide

  11. Match
    Team1
    Team2
    League SoSo Soccer

    View Slide

  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;

    View Slide

  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)

    View Slide

  14. Problem?

    View Slide

  15. Simplicity?

    View Slide

  16. Performance?

    View Slide

  17. Change!

    View Slide

  18. T1
    T2
    T3

    View Slide

  19. There are only two
    hard things in
    Computer Science

    View Slide

  20. Rethink

    View Slide

  21. It’s a Tool

    View Slide

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

    View Slide

  23. MatchStarted
    MatchStarted
    MatchStarted
    TeamFounded
    TeamFounded
    TeamFounded
    TeamFounded
    TeamFounded
    TeamQualified
    TeamQualified
    TeamQualified
    MachEnded
    MachEnded
    MachEnded
    SeasonEnded
    SeasonStarted

    View Slide

  24. View Slide

  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

    View Slide

  26. View Slide

  27. (Re)Tell
    (Re)Tell
    (Re)Tell
    Capture
    https://github.com/leifg/so_so_soccer

    View Slide

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

    View Slide

  29. … and tell a story
    from them

    View Slide

  30. “But I don’t like Football”

    View Slide

  31. Let’s get some Calories

    View Slide

  32. Demo Time

    View Slide

  33. TrackMeal Meal MealTracked
    Command Aggregate Event
    ?

    View Slide

  34. /api/do_stuff
    Command

    View Slide

  35. /meals
    TrackMeal
    Frontend
    MealTracked
    Phx Channel

    View Slide

  36. Hacker News Vanity

    View Slide

  37. HnStory
    •StorySubmitted
    •StoryVotedOn
    •StoryHitFrontPage
    •StoryFrontPagePositionChanged

    View Slide

  38. View Slide

  39. View Slide

  40. View Slide

  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;

    View Slide

  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”

    View Slide

  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

    View Slide