简体   繁体   中英

Data modelling one to many relationship in DynamoDB

I am working in Dynamo DB for the first time. My assignment is Game Management System where it has 3 entities Game, Tournament and User. The relationship between each entity is.

  • 1 Game has multiple users

  • 1 User has multiple tournaments

  • 1 Game has multiple tournaments

I have identified the following access patterns. User can login via gameId and userId. Be it anycase we should retrieve complete Game details hence the data in dynamoDb is stored as below.

  • Fetch GameDetails by GameId
  • Fetch GameDetails by userId

在此处输入图像描述

challenge here is we will have either GameId or UserId at a time. I should send back complete GameDetail object. GameDetail object should be maintained across the GameId. For example If user logs in using userId 101 and play a game "abc" then all the items with gameId should be updated.

Considering such scenario how to data model DynamoDb. I thought having gameId as partition key and userId as sort key and GSI with inverted index (userId as partitionkey and gameId as sortKey)

As I mentioned earlier the challenge is we will have either GameId or userId at a time in which case we cannot update without sort key. Experienced people please help.

There are three DynamoDB concepts that I think will help here.

The first relates to granularity of Items. In DynamoDB I like my individual Items to be fine-grained. Then if I need to assemble a more complex object, I can pull together a number of fine-grained Items. The advantage is that I can then pull properties of the fine-grained object out into attributes, and I can make Global Secondary Indexes to create different views of the object.

The second relates to immutability. In DynamoDB I like to use Items to represent true facts that have happened and will never change. Then if I need to determine the state of the system, I can pull together all of those immutable events, and perform computations on them. The advantage is that I don't have to worry about conflicting updates to an Item or to worry about losing historical information.

The third relates to pulling complexity out of the database and into the application. In DynamoDB I like to move computations involving multiple Items out of the database, and perform them within my application. There is not much cost to doing so, since the amount of data for one domain object, like a game, an employee, or an order, is often manageable. The advantage is less complexity in the data model, and fewer opportunities for complicated operations performed within the database where they could affect its performance.

These relate to your system as follows.

For granularity, I would model the Score, not the Game. Assemble the state of the Game by pulling in all the Scores for all the Tournaments within the Game and doing computations on them.

For immutability, record a Score for a particular Tournament as an immutable event. Then you don't have to worry about updating existing Items when a Score is determined for a Tournament. If the Score has to change, and you want to store the Score between changes, then record Points instead of Scores, and similar logic applies. Once a Point is scored, it forever has been scored and won't change (but you can score negative Points), and you can determine the score for a Tournament by pulling together all the Point events.

For pulling out complexity, you will be performing things like determining the winner of a Game using computations in your application, either front-end or back-end, using a potentially large collection of Scores. This is generally fine, since compute resources and browsers can usually handle computations on data sets of the size that would be expected for a Game.

I have one note on terminology. In English usage in the United States, you would call a collection of games a tournament. It may be that in other parts of the world, or for particular sports I don't know about, a game is a collection of tournaments. Either way, we effectively have “Big Competitions” and “Little Competitions,” where a Big Competition is a collection of Little Competitions. In your post, you're using Big Competition for Game, and Little Competition for Tournament. In my suggested table design below, I'm using generic terms BigComp to abbreviate “Big Competition” and LittleComp to abbreviate “Little Competition.” I'm calling this out, and using my generic terms, in case others are confused as I was by the notion of a game as a collection of tournaments.

I would model your system with the following Item types and Global Secondary Indexes.

Constructions like bigcomp#<BigComp Id> mean the literal string bigcomp# followed by a Game Id like 4839457. I do this so that I can use a PK for different types of Items without worrying about collisions in name.

Item type: Score
PK: bigcomp#<BigComp Id>
SK: littlecomp#<LittleComp Id>#userid#<User Id>#score
Attribute: ScoreUserId
Attribute: ScoreValue
**Note: User Id and ScoreUserId are repetitions of the same info, the User Id.**

Global Secondary Index UserScores
PK: ScoreUserId
Projections: All attributes

Item type: Login
PK: userid#<User Id>
SK: <sortable date and time string>
**Note: This allows you to show the last login time.**

Item type: BigComp Enrollment
PK: bigcomp#<BigComp Id>
SK: bigcompenrollment
Attribute: BigCompEnrolledUserId
**Note: The attribute is the User Id of the user who has joined this BigComp.**

GlobalSecondaryIndex BigCompEnrolledUserId-BigComp
PK: BigCompEnrolledUserId
Projections: All attributes

Item type: LittleComp Enrollment
PK: bigcomp#<BigComp Id>
SK: littlecompenrollment#littlecomp#<LittleComp Id>
Attribute: LittleCompEnrolledUserId

GlobalSecondaryIndex LittleCompEnrolledUserId-LittleComp
PK: LittleCompEnrolledUserId
Projections: All attributes

Here's how some access patterns would work. I'll use the convention of a Big Competition being a Game, and a Little Competition being a Tournament, since that's how it is in the original post.

User joins a Game. Write Item of type BigComp Enrollment.

User joins a Tournament. Write Item of type LittleComp Enrollment.

Another User joins the Tournament. Write another Item of type LittleComp Enrollment.

The first User logs out, and then wants to re-join the Tournament based on the Game Id. Query PK: bigcomp#<BigComp Id> , SK: begins_with(littlecompenrollment#) , with BigComp Id set to the known Game Id, and you'll have a collection of all the Tournaments for this Game, and their Users. The application will have to handle authentication, to make sure the User is who they say they are.

Two Users compete in their Tournament, each getting a Score. Write two Items of type Score.

Who won a given Tournament? Query all the Score Items for the specific Game (BigComp) and Tournament (LittleComp).

Who won the Game? Get all the Scores for the specific Game (BigComp). Use logic in your application to figure out the winner.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM