Let's begin this with a short review of the problem space: One of the fundamental principals of DDD is to place the business rules as closely as possible to the places where they need to be enforced. This is an extremely important concept because it makes your system more "cohesive". Moving rules "upwards" is generally a sign of an anemic model; where the objects are just bags of data and the rules are injected with that data to be enforced.
An anemic model can make a lot of sense to developers just starting out with DDD. You create a
User model and a
EmailMustBeUnqiueRule that gets injected with the necessary information to validate the email. Simple. Elegant. The issue is that this "kind" of thinking is fundamentally procedural in nature. Not DDD. What ends up happening is you are left with a module with dozens of
Rules neatly wrapped and encapsulated, but they are completely devoid of context to the point where they can no longer be changed because it's not clear when/where they are enforced. Does that make sense? It may be self-evident that a
EmailMustBeUnqiueRule will be applied on the creation of a
User, but what about
UserIsInGoodStandingRule?. Slowly but surely, the grainularization of extracting the
Rules out of their context leaves you with a system that is hard to understand (and thus cannot be changed). Rules should be encapsulated only when the actual crunching/execution is so verbose that your model starts to loose focus.
Now on to your specific question: The issue with having the
CommandHandler throw the
Exception is that your business logic is starting to leak ("upwards") out of your domain. Why does your
CommandHandler need to know an email must be unique? The application service layer is generally used for coordination rather than implementation. The reason for this can be illustrated simply if we add a
ChangeEmail method/command to your system. Now BOTH methods/command handlers would need to include your unique check. This is where a developer may be tempted to "extract" an
EmailMustBeUniqueRule. As explained above, we don't want to go that route.
Some additional knowledge crunching can lead us to a more DDD answer. The uniqueness of an email is an invariant that must be enforced across a collection of
User objects. Is there a concept in your domain that represents a "collection of
User objects"? I think you can probably see where I'm going here.
For this particular case (and many more involving enforcing invariants across collections) the best place to implement this logic will be in your
Repository. This is especially convenient because your
Repository also "knows" the extra piece of infrastructure necessary to execute this kind of validation (the data store). In your case, I would place this check in the
add method. This makes sense right? Conceptually, it is this method that truly adds a
User to your system. The data store is an implementation detail.