I was in that situation a lot of times since i do some clean architecture and not depend on crazy hugh validation frameworks.
I see 2 different approaches here:
#1 Validation in entities
For instance:
public class Basket {
private BasketId id;
public Basket (BasketId id) { this.id = id; } }
public class BasketId {
private String value;
public BasketId (String value) throws InvalidArgumentException {
if (value.equals("")) { // So here could be the validation
throw new InvalidArgumentException();
}
}
}
public class GetBasketInteractor {
public void execute(GetBasketRequest request) {
try {
BasketId id = new BasketId(request.basketId);
} catch (InvalidArgumentException e) {
// return some error
}
}
}
What i think about this:
Pros:
- Validation in 1 place, nobody can create a broken Basket entity
Cons:
- I think it violates the Single Responsibility Principle, when more than validation will be added to the BasketId class
- If there is validation in the Basket entity aswell, and other businessrules too, that certainly violates the SRP
- Kind of feels weird to try catch a constructor
#2 Validation in Validation classes
public class Basket {
private BasketId id;
public Basket (BasketId id) { this.id = id; } }
public class BasketId {
private String value;
public BasketId (String value) throws InvalidArgumentException {
this.value = value;
}
}
public class GetBasketInteractor {
public void execute(GetBasketRequest request) {
// Probably injected via dependency injection, but for simplicity..
validator = new BasketRequestValidator(); // probably composes a lot of single validators like NotEmptyValidator or RegexValidator
if (validator.isValid(request)) {
// return some error
}
}
}
What i think about this:
Pros:
- Single Responsibility Principle fulfilled
- No scary try catch in execute() method
Cons:
- It is possible to create a broken Basket entity
There is a third option, that is validating in the Usecase Interactor but i think that is not a good place, since the validation might not be reusable.