We are in the process of redesigning few our REST API endpoints to transition to a micro service architecture.
Here we are working on the endpoint /invitations/:id/confirm
.
This endpoint creates a User
, Account
using the provided Invitation
.
We have 3 aggregates Invitation
, User
and Account
.
The nominal flow we are currently following is:
- Check if
Invitation
exists - Make sure the invitation can be confirmed
- Create
User
- Create
Account
- Delete
Invitation
- Return
UserId
This operation is done in-process which explained why we can return a UserId right away. We simply load our aggregates from the db, perform the associated business logic and persist the result.
Introducing micro services will require asynchronous processing. In other words, we should send a command to a bus and return status code 202.
In our plan, we want to fire a command named RequestInvitationConfirmation
. Basic validation will occur while instantiating this command.
Then this command will be sent through the bus to a consumer in charge of:
- Loading the invitation aggregates (make sure it exists)
- Calling the RequestConfirmation methods (will check that the invitation can be confirmed)
- Raising the InvitationConfirmationRequested
event
The InvitationConfirmationRequested
event will trigger a SAGA in charge of orchestrating the cross services communication
OnInvitationConfirmationRequested
- Send
CreateUser
command
- Send
OnUserCreated
- Send
CreateAccount
command
- Send
OnAccountCreated
- Send
DeleteInvitation
command
- Send
OnInvitationDeleted
- Raise
InvitationConfirmed
- Raise
Since it's asynchronous we need to provide a way to get the current operation state. I saw (https://www.adayinthelifeof.nl/2011/06/02/asynchronous-operations-in-rest/, https://asyncrestapi.docs.apiary.io/#) that a common approach
is to offer a /queue/:id
OR /actions/:id
endpoints.
This is where we get confused. How can you offer a single endpoint when states may be totally different from a SAGA to another?
Thx