I am currently developing a CQRS application that has a login page. In the LoginCommand
, one user is fetched from the database, and if it can login (enrypted password matches given encrypted password), a token is assigned to the user.
In CQRS, the command usually receives the ID of the element the command is aimed to, in order to fetch the Domain Aggregate it identifies and execute logic on it. However, in that case what I have from the user is the email. Despite that being an unique field, I am not sure if it is wrong to use that field to fetch the aggregate, despite being an unique field.
I can also think of other situations with the same problem, like trying to identify a Post
by a given semantic URL that does not contain the ID of the post.
As executing queries inside the Command is forbiden, and attaching the user ID from the login form is unlikely, what options do I have to fetch the user given that situation? Should I query the read model outside the command (e.g. the controller)?
Speaking from a domain point of view, an ID
associated with a User
record is simply a surrogate key. It does not have a corresponding representation in the real world and is only meant to help you persist and retrieve data.
So if email
is the unique field for your User
records, by all means, use it as you would use an identifier in your command.
That doesn't necessarily mean that you can get rid of your id
field.
You would still want to have a surrogate key like an id
field in your User
record, because you may want to give your users the option to change their email address. Even with a changed email address, you need to be able to identify the user uniquely throughout the system, and that's where a surrogate key comes handy. You would also want a surrogate key for performance reasons; it is almost always better to use an Integer
or UUID
field instead of a String
email address as a primary key, or in reference fields.
You should also differentiate between a Command
and its corresponding Command Handler
. A Command
is just a DTO that encapsulates the change that happened in the external world, or a change that needs to committed to the database. In that sense, they are immutable and should not perform queries or update themselves in any way.
A Command Handler
(which is similar to an Application Service in nature but backgrounded) consumes the data in the command. In there, you can query your repository and retrieve records. In fact, this will be a necessity to do any kind of duplicate or reference key validations.