I am building an application that tries to abide by clean architecture. I understand that the repository is meant to abstract away the persistence layer and return back entities in terms of the domain language. But, does that mean that it is also supposed to check and throw domain errors if something goes wrong. Let's consider a situation where I want to add a user via the user repository. I can do the following:
// in user repo
const add = (user: User): void => {
try {
// do some database stuff
} catch() {
throw new EmailAlreadyInUse(user.email);
}
}
But is this implementation advisable? We are now relying upon the database to have been setup correctly with the correct unique key schema to enforce a domain rule (no two users can register with the same email). This seems to me like we are potentially spilling domain rules to the perisitence layer.
Would it make more sense to throw this exception from the use case layer in stead.
const AddNewUserUseCase = (userRepository, email) => {
const user = userRepository.findByEmail(email);
if(user) {
throw new EmailAlreadyInUseError(email)
}
else {
const user = new User(email);
userRepository.add(user);
}
}
This works and removes any spillage from the persistance layer. but I'd have to do this every single place I'd want to add a user. What is the recommended pattern you would go for? Do you have another approach you'd encourage? Where would you do those checks to throw errors.