A question regarding Domain-Driven Design (DDD) an...
# sst
l
A question regarding Domain-Driven Design (DDD) and architectural design that I suspect is likely common to many, if not most microservice apps. So, I'm wondering how people might approach this as a common design pattern. The idea is, that as a user of the system, a person is a different type of entity in different sub-domains: • In Cognito, a person is a "User" who signs up, logs in, forgets and resets passwords, etc. Authentication and authorization-related stuff. • In a banking app, a person is a "Customer". They check account balances, transfer money between accounts, etc. In my karaoke backend app, a person is a "Singer". They manage favorite song lists, request to join a queue to get a chance to sing, view their performance history, etc. In other words, a person is a different kind of entity with different properties in different sub-domains. • What are some common approaches to link the "User" in Cognito with their "Entity" in the rest of the app, so that auth concerns are orthoganal/cross-cutting to their business-related activites in the app? • Specific to AWS serverless/sst, my thoughts on a good design pattern is to use triggers, such as Pre-Signup or Post-Confirmation to create a banking "Customer" or "Singer" or what-have-you entity in DynamoDB that uses their unique identifier in Cognito as their partition key in the singers or customers DynamoDB table. How have other sst, or serverless in general, developers been approaching this problem?
t
Weird coincidence I've been thinking about how DDD intersects with serverless a lot this morning
l
I problably created a disturbance in the Force by wrestling with this problem over the last couple of days. My name is Luke, afterall.
Thanks, Dax. Those look like good reads/vids that are at least somewhat related 🙂
a
We use a unique ID that identifies the user (the human being) (cognito sub is perfect) and then run a monotable ddb design where different entity types are defined by sortkey (CUSTOMER#${IsoTimestampString}, SINGER#${IsoTimestampString}, etc.) The partition key for the item is the unique id for the human. This way you can get all entities for a human with PK = ID and begins_with(SK, SINGER) etc
d
Using sub is suboptimal, because you will never be (easily) able to migrate to a new Cognito pool if you need to change something. Can’t set sub in the migration hook.
a
a tip for this setup, if applicable to your use case, is to have a ‘BANKACCOUNT#LATEST’ and then ‘BANKACCOUNT#IsoTimestampString’ for the inactive entity objects. This allows you to perform a GET/UPDATE on the #LATEST item without first query and sorting to grab the correct item.
d
We use “username” which is random ID generated at sign-up to allow a users to change their email.
Other parts of the system use a truncated hmac_sha hash of the cognito username + some string, that we call Pseudonyms, for their user data. This allows different collections of data (usually Dynamo tables) with different userId primary-keys, derived from the Cognito username. It increases user privacy by making it harder for ourselves (and AWS) to directly correlate a cognito user-id to a DynamoDB record. Protects a little bit If for some inexplicable reason some table gets leaked or you have a malicious insider.
DDD wise, every Cognito user is a person. And a person plays many roles, but it’s still the same person.
Does every part of your system need to know the Singer’s date of birth? Probably not 😄
o
Coming from the identity world - having a separation between a user in dynamodb and their identity (cognito user) always made sense to me. Users have asked to change their social login provider (e.g. they initially accidentally signed up with work gmail and want to switch to personal etc)