Should in Clean Architecture use cases be aware of the authentication/authorization process and command/query layer should have some abstraction of the authentication service implemented in layer below using some framework or user identifier should be passed as the part of the command/query?
Options:
Option 1:
class ChangePasswordCommand {
char[] newPassword;
}
class ChangePasswordCommandHandler {
AuthService authService;
void handle(ChangePasswordCommand command) {
User currentUser = authService.currentUser();
// logic
}
}
Option 2:
class ChangePasswordCommand {
UUID userId;
char[] newPassword;
}
class ChangePasswordCommandHandler {
UserRepository userRepository;
void handle(ChangePasswordCommand command) {
User currentUser = userRepository.findById(command.getUserId());
// logic
}
}
When you talk about use cases, this is the domain of your application. So, to answer the question, I can say: it depends on this domain.
If your domain is about the "users management" (example : LDAP application), yes! you need to implement it in your domain.
Otherwise, it's just a technical problem and you can manage it outside the domain (and call the repository directly).
Examples :
Let's assume that you have some third party authentication provider providing user structure that has the fllowing fields : id, email adddress, etc.
To just authenticate user without business rules, you can call authenticator placed in infrastruture layer from application layer.
userApplication.authenticate(String login, String password) {
userProvider.authenticate(login, password);
}
In the other hand, if you want to apply some business rules while authenticating user, you must call user domain API (interface placed in the domain layer) from application layer.
The following code is just an example. You have to clean it.
Application layer :
userApplication.authenticate(String login, String password) {
userAPI.authenticate(login, password);
}
Domain Layer :
class Member {
...
}
interface UserAPI {
Member authenticate(login, password);
}
*you can implement this interface by creating some factory or service or wathever you want. Let's say you have a Domain UserService.
class UserService implements UserAPI {
UserSPI userSPI;
Member authenticate(login, password) {
// apply some business rules
userSPI.authenticate(login, password);
}
}
interface UserSPI {
Member authenticate(login, password);
}
Infrastructure Layer :
class MemberDTO {
...
}
class userProvider implements UserSPI {
private final RemoteProvider remoteProvider;
Member authenticate(login, password) {
MemberDTO memberDTO = remoteProvider.authenticate(login, password);
return memberMapper.dtoToDomain(memberDTO);
}
}