Kevin Busch
Kevin Busch

Reputation: 191

CQRS Projection One-To-Many / Many-to-Many

Let's assume i want to create a sports event Event-Sourcing Project with CQRS.

How would i create the projection for that?

Game with Winner

GameID Location TeamNameOne TeamNameTwo Winner PointsTeamOne PointsTeamTwo
1234 Munich Avengers Suicide Squad Avengers 48 30

But how would i design the Team has many players projection? With a JSON Field?

TeamID TeamName Players
432 Avengers [{"name": "Man, Iron", "number": 34}, {"name": "America, Captain": "number": 1}...]

Or with a line for each player?

TeamId TeamName PlayerId PlayerName
432 Avengers 34 Stark, Tony
432 Avengers 1 America, Captain

The same with Events for each Game, that could be much, depending on how granular i want to track each activity.

I thought about a DocumentBased Database for the Events in a Game, so each Game could include all the activities in one Field (Like in the Players-Column for the Teams). But then it would get complex too, because Game contains Acitvity contains Acitvity's Player

Is a GraphDB like Neo4J better for that? I think, but then i have to start creating "queries" and the projectors are not independent anymore.

And then the question for the Aggregate design. Because i am not sure if i should add a List of Player IDs in a TeamAggregate and a List of Events for a Game.

Upvotes: 0

Views: 219

Answers (1)

Richard Howell-Peak
Richard Howell-Peak

Reputation: 487

I've been thinking about this issue a lot myself, and it seems to me that the more 'read-optimised' you want, the more data you have to store (and the more complex the writing becomes).

Your point about each activity containing a copy of the user record is a great example.

You could imagine on the front-end, that there is a tool-tip for each activity that happens, which when the user hovers over the tool-tip displays a link to that particular player and their profile picture.

Having that read-model 'ready-to-view' would mean potentially copying user data many times into each event.

Then if a user changes details (like a new profile picture), then you have to iterate over large data structures and update the new value across the board. This adds a lot of complexity to making updates to this projection.

In terms of how to store this data, I'd probably go with a key-value document. I find those well suited to displaying on a front-end because they form a hierarchal tree, which is the same as HMTL, so there is a natural mapping between the two.

Like many things in software, there are trade-offs. The less joining and more 'ready-to-view' you want the data, the more data duplication and more complexity of keeping consistency.

You can make the joins cheaper by using key-value stores (as they have near constant lookup time).

So you could have this type of structure:

{
  gameId:'sdk',
  gameEvents: [
    {
      playerId: 'abc',
      eventType: 'FOUL',
    }
    // etc.
  ],
  players: {
    abc: {
      name: 'billy',
      profilePicture: 'billy.png',
    },
    // etc.
  }
}

Then all your front-end has to do, is each component where the tool-tip is rendered, you can do like:

<div>
  <tooltip>
    <content> {{ event.eventType }} </content>
    <hover> {{ data.players[event.playerId].name }} </content>
  </tooltip>
</div>

Upvotes: 1

Related Questions