amazon-web-servicesgraphqlamazon-dynamodbaws-amplifyaws-appsync

How to setup Amplify Datastore schema for single table design


For my project, I need to be able to query and change my data offline, syncing the changes whenever the connection is restored. Therefore, DynamoDB with Amplify Appsync and Datastore seems like the best option, but we are struggling with some conflicting recommendations.

Our data is structured around Items, which have Locations that should be changed and synced in real-time. As a Location could contain multiple Items and an Item could have multiple Locations, we deal with many-to-many relations. We should be able to query all Items, all Locations, all Locations for a single Item, all Items for a Location. This is the base requirement of the data structure, but we should be able to add queries for things like last modified items etc. Next to this, we have a multi-tenant setup, so there should be a way to authorize users to parts of the data. We will probably use user groups to filter the data.

Most resources online state that in order to take full advantage of DynamoDB, a single table design approach is the way to go. This would mean that we could use a structure as shown in this documentation, adding sortkeys to make additional queries.

The Datastore documentation says the @model directive should be used to be able to set up all mutations, queries and subscriptions. In order to make the relations between the Items and Locations, we should use the @manyToMany directive as stated here. All together this would result in the following schema:

type Item 
    @model
    @auth(rules: [{ allow: groups, groupsField: "groups" }])
    {
        ID: ID!
        groups: String!
        locations: [Location] @manyToMany(relationName: "ItemLocation")
        ...
    }

type Location 
    @model
    @auth(rules: [{ allow: groups, groupsField: "groups" }])
    {
        ID: ID!
        groups: String!
        items: [Item] @manyToMany(relationName: "ItemLocation")
        ...
    }

When we deploy this schema using the Amplify CLI, all resources are generated without problems. However, if we look at the DynamoDB tables, we find that there are multiple tables generated: Item, Location and ItemLocation. The @model directive automatically deploys a table, as does the @manyToMany directive. Dynamodb documentation does not use these directives, so it should be possible to make a single table from the given requirements. I am not able to find how to do this from the Datastore/Amplify side, or how to connect Datastore to an existing DynamoDB as Datastore relies on the @model directive to store data locally (or that is what I suspect). This is not in line with the DynamoDB standard, and we would like to change this setup to a single table design.

I have been looking in the Amplify, Datastore and DynamoDB documentation and in online rescources, but was not able to find any guidance or solutions. Hopefully someone can push me in the right direction, thanks!


Solution

  • TL;DR Amplify's backend tools create the DynamoDB and Appsync boilerplate for you. This high-level convenience comes at the cost of low-level control, including control over how many tables your app has.

    You are right that the terminology can be confusing. Here are some clarifications for you:

    For my project, I need to be able to query and change my data offline, syncing the changes whenever the connection is restored. Therefore, DynamoDB with Amplify Appsync and Datastore seems like the best option

    Datastore is not a constraint. The Amplify js client, including the client-side DataStore offline-synching magic works directly with AppSync. You can build DynamoDB and AppSync resources yourself (without Amplify's backend abstractions) and still use DataStore in the client.

    Most resources online state that in order to take full advantage of DynamoDB, a single table design approach is the way to go.

    Often, not always. Single-table design can have an efficiency payoff for many uses cases and is a solid default choice. However, there are also cases in which the pattern is less helpful.

    The @model directive automatically deploys a table, as does the @manyToMany directive. Dynamodb documentation does not use these directives, so it should be possible to make a single table from the given requirements

    Only when you ditch Amplify backend for a DIY solution. The @model and @manyToMany directives are Amplify backend syntax that hints to the Amplify CLI how it should build the AppSync schema and DynamoDB tables. Single-table designs use sophisticated compound key structures, which are beyond even the great powers of the Amplify backend CLI.

    how to connect Datastore to an existing DynamoDB as Datastore relies on the @model directive

    It connects via AppSync. DataStore is a client-side tool (think Apollo Client) that works with AppSync. DataStore has no instantiation in the Cloud. AppSync resolvers call DynamoDB to read/write data.

    This is not in line with the DynamoDB standard, and we would like to change this setup to a single table design.

    In sum, your choice is between Amplify backend convenience + multiple tables or DIY flexibility + single table. DataStore works with either option.