1
\$\begingroup\$

I was struggling for some time with proper implementation of domain validation in the context of domain entity and it's properties. By the time validation comes down to check if property value matches pattern and/or already exists.

First of all, I implemented Validator interface, which is generic interface. Every object which implements Validator, should only provide own implementation of validate method. I'm placing Validator body below:

public interface Validator<T> {

    public Set<ErrorMessage> validate(T toValidate);
}

Secondly, I implemented few following specialized Validators: EmailValidator, UsernameValidator, MobileValidator. Each of them takes adventage ValidationMessageTranslator, which enapsulates ResourceBundleLoader (custom implementation for resource bundle loading). This way, each validator delegates fetching of readable message for end user to other specialized class. I'm placing source code for one of them, which is UsernameValidator. I'm also placing source code for Username object for the sake of clarity:

public class UsernameValidator implements Validator<Username> {

private ValidationMessageTranslator validationMessageTranslator;

    private UserAccountRepository userAccountRepository;

    private UsernameValidator(ValidationMessageTranslator validationMessageTranslator, UserAccountRepository userAccountRepository) {

        this.validationMessageTranslator = validationMessageTranslator;
    }

    public static UsernameValidator useWith(ValidationMessageTranslator validationMessageTranslator, UserAccountRepository userAccountRepository) {

        return new UsernameValidator(validationMessageTranslator, userAccountRepository);
    }

    @Override
    public Set<ErrorMessage> validate(Username username) {

        Set<ErrorMessage> errorMessages = new HashSet<>();

        if(!(username.conform("")) || (userAccountRepository.existsByCredentialsLoginUsername(username))) {

            errorMessages.add(ErrorMessage.of(ValidationConstant.INVALID_EMAIL, PropertyNameResolver.resolve(username)));
        }

        return errorMessages;
    }
}

And here is the Username implementation:

public class Username {

    private String username;

    private Username(String username) {
        this.username = username;
    }

    public static Username create(String username) {
        return new Username(username);
    }

    public boolean conform(String regex) {
        return username.matches(regex);
    }
}

Next I implemented ValidatorExecutor along with ValidatorExecutorIn (it acts as ValidatorExecutor collection element) and ustom checked exception which is ValidationException. As i mentioned, ValidatorExecutor has only one property, that is of a collection type (Set). It takes reponsibility only for running validators passed as collection and if at least one validator resulted in some error messages, it throws above mentioned exception. This exception also takes only one parameter as it is constructed, which is collection of error messages. To go further, this Exception will be properly served in web layer level by some sort of controller advice. Below, I'm presenting source code for all three ValidatorExecutor, ValidatorExecutorIn, ValidationException:

ValidationExecutor:

public class ValidatorExecutor {

    Set<ValidatorExecutorIn> validatorExecutorIns;

    private ValidatorExecutor(Set<ValidatorExecutorIn> validatorExecutorIns) {

        this.validatorExecutorIns = validatorExecutorIns;
    }

    public static ValidatorExecutor of(Set<ValidatorExecutorIn> validatorExecutorIns) {

        return new ValidatorExecutor(validatorExecutorIns);
    }

    public void execute() throws ValidatorException {

        if((validatorExecutorIns == null) || (validatorExecutorIns.isEmpty())) {

            throw new IllegalArgumentException("At least one validator should be set");
        }

        Set<ErrorMessage> errorMessages = new HashSet<>();

        validatorExecutorIns.forEach(validatorExecutorIn -> errorMessages.addAll(validatorExecutorIn.run()));

        if(!(errorMessages.isEmpty())) {

            throw new ValidatorException(errorMessages);
        }
    }
}

ValidationExecutorIn:

public class ValidatorExecutorIn {

    private Validator validator;

    private Object validatedProperty;

    private ValidatorExecutorIn(Validator validator, Object validatedProperty) {

        this.validator = validator;
        this.validatedProperty = validatedProperty;
    }

    public static ValidatorExecutorIn create(Validator validator, Object validatedProperty) {

        return new ValidatorExecutorIn(validator, validatedProperty);
    }

    public Set<ErrorMessage> run() {

        return validator.validate(validatedProperty);
    }
}

ValidationException:

public class ValidatorException extends Exception {

    private Set<ErrorMessage> errorMessages;

    public ValidatorException(Set<ErrorMessage> errorMessages) {

        this.errorMessages = errorMessages;
    }

    public Set<ErrorMessage> getErrorMessages() {

        return errorMessages;
    }
}

I wish anyone considers my code readable, reusable and generally oo. Anyway. Can I ask you to review my code? If you find some place to improve it or I should get some more information about any specific area of oo, please give some constructive feedback. It's just the learning curve, so I'm ready for everything.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.