26

We are trying out CQRS. We have a validation situation where a CustomerService (domain service) needs to know whether or not a Customer exists. Customers are unique by their email address. Our Customer repository (a generic repository) only has Get(id) and Add(customer). How should the CustomerService find out if the Customer exists?

JontyMC
  • 6,038
  • 6
  • 38
  • 39

4 Answers4

26

Take a look at this blog post: Set based validation in the CQRS Architecture.

It addresses this very issue. It's a complex issue to deal with in CQRS. What Bjarte is suggesting is to query the reporting database for existing Customer e-mail addresses and issue a Compensating Command (such as CustomerEmailAddressIsNotUniqueCompensatingCommand) back to the Domain Model if an e-mail address was found. You can then fire off appropriate events, which may include an UndoCustomerCreationEvent.

Read through the comments on the above blog post for alternative ideas.

Adam D. suggests in a comment that validation is a domain concern. As a result, you can store ReservedEmailAddresses in a service that facilitates customer creation and is hydrated by events in your event store.

I'm not sure there's an easy solution to this problem that feels completely clean. Let me know what you come up with!

Good luck!

user247702
  • 23,641
  • 15
  • 110
  • 157
Kevin Swiber
  • 1,546
  • 11
  • 13
  • 22
    As mentioned in the other answer, you shouldn't perform queries against your view model as a part of processing commands. If anything, those queries should be performed by the client before sending the command, choosing not to send the command based on the results. – Udi Dahan Jun 27 '10 at 18:42
  • 10
    @Udi Dahan I don't really get how this works from an SOA perspective with this username example. When I receive a CreateNewUserCommand, surely I need to check that the username doesn't already exist? I can't assume that the client has already done this check, particularly if the client could be a third party app? I understand the rationale for not performing queries when processing commands, but I can think of a few scenarios where when processing a command I need to analyse data that is outside of the aggregate that the command is intended to operate on... – David Masters Nov 03 '10 at 13:44
  • 12
    Third party apps would connect via a well-behaved "client" which could be a web service, and it is the one that does the check against the view model, and if the checks pass, sends the one-way message to the "server". – Udi Dahan Nov 04 '10 at 19:11
  • 3
    @Udi Dahan That's a fair point about a well-behaved client. Thinking about it, that's quite an obvious solution! – David Masters Jan 27 '11 at 16:28
  • 3
    @Udi Dahan, But what if there are several clients, the same behavior has to be implemented in all of them. The behavior is certainly domain code and doesn't belong in application layer. – andho Jul 17 '11 at 17:55
  • 2
    @andho There's more to software than just layers, and the same component can be deployed to multiple tiers. – Udi Dahan Jul 18 '11 at 11:15
  • 3
    I realize this is possibly a stale post but, with all due respect to Udi, the idea that we can and should code trusting that the client has performed all necessary validation is contrary to many lessons taught and learned over the years in the real-world and only really applies if you can absolutely, positively, 100% guarantee that you can trust the caller. Unless you are developing in a closed environment where you control everything and do not support integration or extension (or team development), this is not realistic. In the rest of the cases (majority?), you have to code defensively. – SonOfPirate Apr 11 '12 at 13:05
  • 4
    The whole point of SOA, which was the basis of David's comment, is to support integration and extension, etc. As such, one of the most basic rules is to NEVER trust. As a result, we should always verify/validate before committing. – SonOfPirate Apr 11 '12 at 13:07
  • As an alternative response to David's question, I typically use an Application Service to support the public API in my SOA applications. It then becomes the Application Service's responsibiliy to verify the information BEFORE it submits the command. So, in your example, the MembershipService would verify that the requested username does not exist and, if so, instantiate, hydrate and send the CreateNewUserCommand for handling. – SonOfPirate Apr 11 '12 at 13:09
  • 1
    @SonOfPirate If you were to create an API, its methods would check with the query service before issuing the command... That seems perfectly acceptable to me? If your point is that you need to re-use your application service for the API, then surely that's a design fault of yours not with this style of architecture? I would recommend using certificates for authentication between your own client and the app service. Your API should be a seperate service; as Udi says, a well behaved 'client' of its own. – David Masters May 29 '12 at 09:52
  • 2
    @DavidMasters "When I receive a CreateNewUserCommand, surely I need to check that the username doesn't already exist?" - You can check it using a query database, but query databases are not necessary in sync with the event storage because of eventual consistency, so rarely, but you can get a false result. In this case 2 users will be registered with the same email address and so you have to compensate, for example delete one of them. This can happen for example by having a bad UI in which you can send the same from twice with 2 clicks... – inf3rno Sep 25 '14 at 04:46
  • 4
    @inf3rno or when your projection or eventbus gets stuck. Imagine this situation over the weekend and a user that executes the `CreateNewUserCommand` in repeat, ending up with 100+ user aggregates for the same user. This really is a huge point of failure which nobody seems to know how to address correctly. (I don't know it either, otherwise Google wouldn't have brought me here :p) – huysentruitw Nov 08 '17 at 10:16
5

This issues does not have to be that complex:

  1. Check your reporting store for customer uniqueness before submitting the UpdateCustomer command.
  2. Add a constraint to your DB for uniqueness on email address. When executing the command, handle the exception and send a notification to the user using a reply channel. (hences never firing CustomerUpdated events to the reporting store.

Use the database for what it's good for and don't get hung up on ORM limitations.

Mark Lindell
  • 852
  • 8
  • 19
5

This post by Udi Dahan http://www.udidahan.com/2009/12/09/clarified-cqrs/ contains the following paragraph:

"Also, we shouldn’t need to access the query store to process commands – any state that is needed should be managed by the autonomous component – that’s part of the meaning of autonomy."

I belive Udi suggested simply adding a unique constraint to the database.

But if you don't feel like doing that, based on the statement above, I would suggest just adding the "ByEmail" method to the repository and be done with it - but then again Udi would probably have a better suggestion..

t0PPy
  • 311
  • 3
  • 12
2

Hope I am not too late...but we faced a similar situation in our project, we actually intercept the command executor and attach it with the set of rules created for that command, which in turn uses a query to fetch the data.

So in this case, We can have a class by the name, CustomerEmailMustBeUniqueRule which is fetched by the RuleEngine when the command "RegisterCustomerCommand" is about to be executed by RegisterCustomerCommandExecutor. This rule class has the responsibility to query the database to find if the email id exist and stop the execution by raising the invalid flag...

Abhay Naik
  • 410
  • 3
  • 15