Notes on context-bounded user stores

Question

Where should domain user data be stored?

Options

  • in a third-party user identity-provider (e.g. Cognito, Auth0, Firebase)
  • split the user data between a users table in our bussiness-domain relational database and the identity provider.

Analysis

Queryability

Consider the need to getUsersByX where X is anything other than the primary key. The split approach requires querying a single location that is the application database. With the identity-provider-only approach, this retrieval requires reading from two locations: 1) query that app database for user id’s by X (where X is a resource which user id is attached to), and then 2) retrieve those users from the identity-service. If the identity-service supports batch user retrieval, then the entire retrieval process takes two requests. If the identity-service only support retrieving users one at a time, then multiple singular-retrievals in parallel or close succession becomes an issue for performance, cost, and rate-limiting/throttling. This ultimately depends on whether the identity provider supports batch user retrieval.

Separation of concerns

The split approach separates domain from auth concerns. All domain concerns live in the application database, and all the auth concerns (usernames, passwords, tokens) live in the identity provider. With this setup, our user stores have bounded context, where each can change independently. Switching to another identity provider doesn’t require migrating user profiles and potentially reassigning all their ids. All that is needed is to create new auth accounts and attach the new auth ids to our application’s user records. A sweeping change across all users in the business domain doesn’t require requests to the identity-provider. Lastly, a user table in the application database provides referential integrity between each user and its associated data. Without an application users table, data can be written against users that potentially don’t exist.