Boa pergunta. Toda chamada à camada de persistencia deve ser feita na camada de aplicação. Caso algum validador precise consultar algum repositorio (seja do proprio aggregate ou de outro), então é bem capaz que essa validação tenha que ficar num serviço de domínio.
Imagine que vc tenha que gravar um estudante e que vc tenha que verificar se já exista um mesmo e-mail cadastrado no banco. Nesse caso, usar um serviço de domínio é o ideal. A abordagem que gosto é passar o serviço como parâmetro num método do agregado e chamar a validação de lá.
Serviço do domínio do estudante
public class EstudanteService {
private final EstudanteRepository estudanteRepository;
public EstudanteService(EstudanteRepository estudanteRepository) {
this.estudanteRepository = estudanteRepository;
}
public boolean seEmailJaExiste(String email) {
boolean existe = estudanteRepository.verificarSeEmailExiste(email);
return existe; // ou tu pode lançar um exceção em vez de um boolean, fica a seu criterio
}
}
Aggregate Root
public class Estudante {
private EstudanteId id;
private String email;
//demais atributos
Estudante() {}
public void registrarEmail(String novoEmail) {
validarEmail(novoEmail);
this.email = novoEmail;
}
private validarEmail(String novoEmail, EstudanteService estudanteService) {
boolean valido = //aqui vc valida se o e-mail foi preenchido e se está no formato válido
if (!emailValido) {
throw new DominioException("O email \"" + novoEmail + "\" não é válido.");
}
if (estudanteService.seEmailJaExiste(novoEmail)) {
throw new DominioException("O email \"" + novoEmail + "\" já existe cadastrado no sistema.");
}
}
}
Uma observação importante é se o serviço de domínio possuir algum aspecto de infraestrutura, é melhor tirá-lo do domínio e colocá-lo em infraestrutura mesmo. E no domínio apenas usar uma interface implementada pela classe do serviço desse domínio, assim:
public interface EstudanteService {
// ...
}
public class EstudanteServiceImpl implements EstudanteService {
private OutlookExternalEmailValidation servico; //exemplo de serviço externo que não tem nada a ver com o domínio
// ...
}
Com isso, na camada de aplicação, vc poderia fazer algo assim:
public class AlterarEmailAppService {
// supondo que vc esteja recebendo estudanteRepository e estudanteService por injeção
private EstudanteRepository estudanteRepository;
private EstudanteService estudanteService;
public void registrarEmail(EstudanteId idEstudante, String novoEmail) {
Estudante estudante = estudanteRepository.findById(idEstudante);
estudante.registrarEmail(novoEmail, estudanteService);
}
}