I am wondering how I would validate some actions in my domain when a parameter of an action needs to be validated, but that parameter's entity is actually in a different domain so I can't retrieve it.
Let's say I have the following situation:
// "Document" Domain
public class Document
{
Guid Uuid {get;set;}
Guid UserUuid {get;set;}
// Other document properties..
}
// "User" Domain
public class User
{
Guid Uuid {get;set;}
UserType Type {get;set;}
// Other user properties..
}
public enum UserType
{
NormalUser = 0,
Employee = 1
}
As you can see, these 2 classes are in 2 domains. In my situation, both of these are in the same service (DocumentService
), but they are in different assemblies. Project.Domain.DomainA
and Project.Domain.DomainB
. They have no reference to eachother.
There are also classes like UserRepository
and DocumentRepository
which perform data actions. Furthermore, I use CQRS to issue commands. So, for example, creating a document through an api would be like this:
API -> CommandHandler -> Domain -> Persistence
Let's say I need to implement the following:
Allow employees to share a document by sending them to other employees. If the employee tries to share the document to a user who is a NormalUser
, an error should occur.
The first part sounds easy enough. I would add a Share
method to Document
to implement the Sharing functionality. I pass the Uuid
instead of the User
, because the document domain has no knowledge of the domain where User
is located.
void Share(Guid userUuid)
{
// Share logic, events.. etc..
}
But now I need to validate that the user's type is Employee
.
Normally that would be easy enough: User.Type == UserType.Employee
. But in this case, I have no access to the User
type so I can't perform this test in my domain like this.
So, the following isn't possible:
public void Share(User user)
{
if (user.Type != UserType.Employee)
{
throw new Exception();
}
}
I see a few options, but I would like to know what you guys think is best.
- Validate this logic in the commandhandler:
var document = documentRepository.GetDocumentByUuid(uuid); var user = userRepository.GetUserByUuid(userUuid); if (user.Type != UserType.Employee) { throw new Exception(); } document.Share(user.Uuid);
- To me, this is a bad idea. If another commandhandler or any other place in the codebase would try to share a document, this validation would not take place. Furthermore, I believe this validation should take place in my domain.
- Validate this logic in a validator service of some kind:
// New interface in the Document domain interface IDocumentSharingValidator { bool CanShareToUser(Guid userUuid); } // This is the Share method in the Document class void Share(Guid userUuid, IDocumentSharingValidator validator) { if(validator.CanShareToUser(userUuid)) { // YAY! } }
- This seems OK. But where would I place the implementation of
IDocumentSharingValidator
? It needs to talk to the data layer to retrieve the User, so I see 3 choices:- Put it in the
Application
layer and make it use theUserRepository
to do validation.- To me, this is the best option right now. But it doesn't feel like good DDD for some reason? It is not limited to my Document domain anymore; it is just part of the application layer now.
- Put it in the
DocumentRepository
(Which is in theData
layer) and make it talk to theUser
table (We use EF Core, so that would bedbContext.Users
)- Ugly. We still talk to a User table in our Document Repository, so there is a bit of coupling.
- Ok, so we put it in the
UserRepository
and make it talk to theUser
table.- Ugly because the UserRepository now uses an implements an interface from the Document domain.
- Put it in the
- This seems OK. But where would I place the implementation of
Can you guys help me out? What would be a good idea? And how could I improve my DDD skills about this for the future? :)
Thanks!