leifg / so_so_soccer

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SoSoSoccer

This is an example project on how to build a CQRS/ES system using commanded. It is a simplified version of Event Sourcing as it does not include aggregates and commands.

It also demonstrates the difference between a CRUD system and an ES system when it comes to data modelling.

This project was subject to an Event Sourcing Talk held in January 2018 in Chiang Mai, Thailand.

Task

The task is simple. Given a list of soccer matches, calculate the end season standings teable for every season and league. As an input, a sqlite database from kaggle is used (account needed): https://www.kaggle.com/mkhvalchik/soccer/data.

There are 3 possible solutions implemented in this project

The CRUD way calculating the table in application logic

Function call

Reads the matches and teams data model and calculates the standings in pure Elixir code

The CRUD way using an SQL view

Function call

Using an elaborate SQL view to create the standings table from the data model

The event sourced way

Function call

From a stream of events, build a projection

Setting it up

Download sqlite from kaggle (Ideally place the file in tmp/database.sqlite)

Start Postgres database via Docker Compose:

docker-compose up

Install dependencies

mix deps.get

Create schema:

bin/setup.sh

Import CRUD data model:

mix seed_crud_from_sqlite ./tmp/database.sqlite

Import Event Source Stream

mix seed_eventstore_from_sqlite ./tmp/database.sqlite

Start server:

mix phx.server

Note: for event sourced view, wait until projection is updated

Go to localhost:4000 to select the season and leage

Change way of calculating standings

Play around with the different representations by changing the function all in the standings controller:

    # CRUD application logic
    render(
      conn,
      "show.html",
      standings: StandingsView.crud_app(String.to_integer(season), String.to_integer(league_id))
    )

    # CRUD view logic
    render(
      conn,
      "show.html",
      standings: StandingsView.crud_view(String.to_integer(season), String.to_integer(league_id))
    )

    # Event Sourced
    render(
      conn,
      "show.html",
      standings: StandingsView.event_sourced(String.to_integer(season), String.to_integer(league_id))
    )

Benchmark

If you're interested in the performance of the individual implementations, run the benchmark:

mix run lib/benchmark.exs

About


Languages

Language:Elixir 87.8%Language:JavaScript 6.2%Language:HTML 4.8%Language:CSS 1.0%Language:Shell 0.3%