Setup
I'm using ASPNET Core with EFCore.
I've setup MassTransit to help in the replication of Users
and Tokens
between services.
Each User
has multiple Tokens
and they are connected by Token.UserId
foreign key.
The Issue
When a new User
is created, two things happen:
User
and fresh Token
are stored in the databaseUserCreated
and TokenCreated
are sent to the message bus simulatenouslyIn effect, Token
(since it's smaller) is processed before User
and an error pops saying that User of given Id defined in Token.UserId doesn't exist in table Users
- which is true, because User
is created asynchronously along with Token
.
I've found a few solutions, like removing foreign key constraints or implementing a delay in Consume
method - both of which seemed to be lazy solutions that'll eventually come back to me.
Is there a way to perhaps put both events on the same queue and execute them in order they are added to it or should I just include the initial Token in UserCreated
?
When it comes to message contract design, particularly when producing events...
Each event should be a complete representation of a specific event. This means that an event should not reference other data sources, should not include foreign keys, and should not require callbacks to a backing service to be able to do something in response to the event.
In your scenario, you may want to either include enough user information in the TokenCreated
event such that a new user could be added based on the data in that event, or create a third event that includes both data sets – perhaps a UserTokenCreated
. That third event would be produced when both a new user and new token are created at the same time.
Another option would be to use a saga to combine the two events, but that seems more complexity than you need.
You could also use the broker's delayed message feature to reschedule delivery of the message in the future by some delay instead of simply blocking a consumer slot by using Task.Delay() as you seem to be doing now.
Hmm, I was thinking about putting Token info inside
UserCreated
event, but that seemed like inevitably binding these resources together - however, just like with CAP Theorem and microservices in general - I might have to take that tradeoff for greater good.It's a reasonable trade-off. Honestly, so is removing the FK (which I hardly ever use them anyway). However, I'd focus on NOT having to do joins at the edge/caching layer and would use a flat model to avoid query complexity.
Absolutely. Thanks for investing the time to help me out.