How to guarantee username uniqueness with CQRS/ES?
To be honest, I thought that my previous post would be the last in the CQRS/ES series, but I forgot to discuss one more thing related to that topic. Many developers don’t know how we should handle the following scenario in our systems:
“During the creation of user’s new account I would like to verify rather a username is unique in the whole database. Should I use Event Store or Read Database for a query? Where should I check that?”
Seriously, that question is one of the most popular topics connected with CQRS on StackOverflow. Therefore in this article, we’ll try to find out the best solution for this problem.
Solution 1. Use Event Store for validation in a Command Handler
Okay so let’s say that we’d use an Event Store for username validation inside a Command Handler. But we have a problem quite similar to that from the sixth part of the series. As we’ve already established using Event Store for getting many domain objects at the same time is inefficient. Why? Imagine that each of your domain objects consists of 100 events and we have almost 5000 users register in our web application (not so big number of users). Now, to reconstruct each object (make a projection) we need to get these 100 events. That gives us 100 * 5000 = 0,5 million events. It’s ridiculous, no doubt about that. So, we need to find another solution.
Solution 2. Use Read database for validation in a Command Handler
Since the Event Store’s data structure is not comfortable for this task maybe, we should use a “Current State (Read)” database inside our Command Handler? The query using Entity Framework would be very easy:
var isAlreadyTaken = readDb.Users.Any(u => u.UserName == command.UserName);
I agree that it might look like a good solution, but it’s not because of two reasons. First, we should not use Read DB in Write side. For me, it’s kind of CQRS smell since we wanted to separate each side completely. Now with this dependency, our flow is perturbed. But even though, this solution wouldn’t work every time. Remember that because of two independent databases we have to deal with eventual consistency. What would happen in the following scenario?
Imagine that some folk created an account with “TestUser” username. His command was sent to the command handler; a proper domain object was created, and UserCreatedEvent was saved inside Event Store. The event needs to be transported via Event Bus to the appropriate handler to synchronize Read DB. But let’s say that in a moment when event waits inside the bus for processing another folk created an account with “TestUser” username. How our system would behave? Using the Read DB we would check username uniqueness, and it would pass! That is because in that time Read DB was not synchronized yet! I hope that you’ve understood that edge-case 🙂 So, as you can see that solution does not guarantee us safety every time. We need to think about something different.
Solution 3. Client validation
The next solution that I’d like to present is client validation. The idea is simple. We create an HTTP request from the client to the read side to check whether the username is already taken. If it’s not, we send the complete necessary data to the write side as a command. Otherwise, we can inform the user about validation error. That also sounds good since it does not require any weird dependencies within the write side. But it also has drawbacks. It’s only client-side validation, and it can also fail because of eventual consistency. But we can use that as a part of some “big plan.”
Solution 4. Client Validation + Saga pattern
Okay, so why should we use a client validation if it might not work every time? Because the probability of failing is quite low and we’ll also secure our app on the server-side. How? So we know that Event Store has a bad structure for that kind of task, that’s why we need to use a Read DB. But we also know that we cannot use Read DB within the write-side. So here’s the solution. We can add a constraint to a Read DB which will require a UserName uniqueness. So, in that case adding a new user with already taken login would end with an exception. Great, but we have another problem! Creating a user on the Read DB means that we’ve already inserted an event to the Event Store! Therefore, we should delete that event because read-side has thrown an exception. But remember that deleting events is not allowed. We just can’t do that since the idea is to store the entire, complete history of domain’s state. What we could do is to create UserCreationCanceledEvent and add some logic inside our domain object which would not allow reconstructing that. That sound reasonable but we need to inform our domain to create a proper event from the write-side. How? That’s where Saga pattern comes into play. Consider the following sequence diagram:
I know that diagram might look complicated, but it’s not 😉 What we should understand is the Saga’s responsibility. If user’s creation fails, we inform saga about that event. Next, a corrective command is sent to Command Bus to create mentioned UserCreationCanceledEvent which then is saved into Event Store. That’s it! Now, I know that some of you might think that’s a lot of steps to perform, and that’s true. But remember that we also have a quite good client-side validation which should protect our system in most cases, so after all it’s no big deal.
I hope that this article will help you in future. If you know any other good solution to guarantee username uniqueness feel free to share that in the comment section! Next post will be dedicated to Aurelia framework so if you’re also interested in frontend technologies or you want to get aquired with that follow me on Twitter or Facebook to be up to date with brand new articles!