How to deal with unicity constraints over repository

Hi everyone,

Use case

We want to event source users because users are a valuable asset (they pay our rents !) and we need to keep track of their changes. Each user will have an unique id, an email and a password (and lots of stuff we don’t really care about in this example).

Business rules on users :

  • users can change their email
  • you can’t have two users with the same email address

Our plan is to store each user in his own stream user-{{user_id}}. A projection will watch the “user” category and link each event into a “users” stream.

We plan tu use a CQRS design, where a “user-command” service deals with the creation and update command to users and emits events into the corresponding user-{{user_id}} stream.

We need users to figure in an LDAP so we can authenticate them.

My questions are :

  • how can we ensure, from an aggregate POV, that no user already uses a particular email address ?
  • is the idea to put (hashed) passwords in events as dangerous as it looks ?

Scenario 1 : use events !

My first idea is to delegate the email unicity checking to the LDAP (or at least a query service). For that, we create a “LDAP-sync-service”, a query service who watches the “users” stream for user creation and update attempts.

When user-command service receives a creation or email-update command, he emits a “UserCreationAttempted” or “UserEmailUpdateAttempted” event. Service LDAP-sync-service catches the event, tries to “execute” the change in the LDAP.

  • failure : LDAP-sync-service rollbacks LDAP and fires “refuseUserCreation” or “refuseUserUpdate” command to user-command service, who then fires a “userCreationFailed” or “userUpdateFailed” event.
  • success : LDAP-sync-service fires “acceptUserCreation” or “acceptUserUpdate” command to user-command service, who then fires a “userCreated” or “userUpdated” event.

pros:

  • CQRS

  • events look like business rules
    cons:

  • my “write” business rules are separated in two services, which looks weird.

  • user-command service can’t synchronously answer if the user is created/updated to his requester !

Scenario 2 : the command service directly uses LDAP

When user-command service receives a creation or email-update command, he first tries to “execute” the changes in the LDAP :

  • LDAP failure : user-command service fires a “userCreationFailed” or “userUpdateFailed” event

  • LDAP success : user-command service fires a “userCreated” or “userUpdated” event
    pros:

  • simpler, no query

  • user-command service can synchronously answer if the user is created/updated to his requester !
    cons:

  • no CQRS as the command-service is coupled to a “database” service

Scenario 3 : use projections ?

I feel like projections can help me there, but I don’t really know how. Maybe we could link user-{{user_id}} events to an email-{{user-email}} stream, but in this case wouldn’t it seem weird to my user-command service to first load an email aggregate, and then my user aggregate ?

This Post seems interesting. It also discuss email uniqueness.

https://tech.zilverline.com/2013/01/09/simple-event-sourcing-users-authentication-authorization-part-6

I hope it helped

Regards

Its odd that I wrote a post using this exact example dealing with unique constraints. http://codebetter.com/gregyoung/2010/08/12/eventual-consistency-and-set-validation/

Thanks a lot Greg and Lionel,

I think my missing keyword was “eventual consistency”. I had searched in the eventuate documentation and in this group’s archives though.

Thanks again,

Kevin