Suppose the MailConfiguration class, which defines the options for sending emails:
public class MailConfiguration { private AddressesPart addressesPart; private String subject; private FilesAttachments filesAttachments; private String bodyPart; public MailConfiguration(AddressesPart addressesPart, String subject, FilesAttachments filesAttachements, String bodyPart) { Validate.notNull(addressesPart, "addressesPart must not be null"); Validate.notNull(subject, "subject must not be null"); Validate.notNull(filesAttachments, "filesAttachments must not be null"); Validate.notNull(bodyPart, "bodyPart must not be null"); this.addressesPart = addressesPart; this.subject = subject; this.filesAttachements = filesAttachements; this.bodyPart = bodyPart; }
So, I use two value objects: AddressesPart and FilesAttachment.
Theses of the two value objects have similar structures, so I'm going to expose AddressesPart here:
public class AddressesPart { private final String senderAddress; private final Set recipientToMailAddresses; private final Set recipientCCMailAdresses; public AddressesPart(String senderAddress, Set recipientToMailAddresses, Set recipientCCMailAdresses) { validate(senderAddress, recipientToMailAddresses, recipientCCMailAdresses); this.senderAddress = senderAddress; this.recipientToMailAddresses = recipientToMailAddresses; this.recipientCCMailAdresses = recipientCCMailAdresses; } private void validate(String senderAddress, Set recipientToMailAddresses, Set recipientCCMailAdresses) { AddressValidator addressValidator = new AddressValidator(); addressValidator.validate(senderAddress); addressValidator.validate(recipientToMailAddresses); addressValidator.validate(recipientCCMailAdresses); } public String getSenderAddress() { return senderAddress; } public Set getRecipientToMailAddresses() { return recipientToMailAddresses; } public Set getRecipientCCMailAdresses() { return recipientCCMailAdresses; } }
And related validator: AddressValidator
public class AddressValidator { private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; public void validate(String address) { validate(Collections.singleton(address)); } public void validate(Set addresses) { Validate.notNull(addresses, "List of mail addresses must not be null"); for (Iterator it = addresses.iterator(); it.hasNext(); ) { String address = (String) it.next(); Validate.isTrue(address != null && isAddressWellFormed(address), "Invalid Mail address " + address); } } private boolean isAddressWellFormed(String address) { Pattern emailPattern = Pattern.compile(EMAIL_PATTERN); Matcher matcher = emailPattern.matcher(address); return matcher.matches(); } }
So I have two questions:
1) If for some reason later we want to check the address mail differently (for example, to include / exclude some aliases that correspond to the existing mail list), should I set the IValidator view as a constructor parameter? like, for example, the following, rather than citing a specific dependency (as I did):
public AddressValidator(IValidator myValidator) { this.validator = myValidator; }
In fact, this will respect principle D of the SOLID principle: dependency injection.
However, if we follow this logical, will most value objects have an abstract validator, or is it just outsmarting most of the time (thinking YAGNI?)?
2) I read in some articles than with regard to DDD, all checks should be present and present only in Aggregate Root, this means MailConfiguration in this case.
Am I right when you consider that immutable objects should never be in unchesive state? Thus, will validation in the constructor, as I have done, be preferable in the corresponding object (and thus avoiding the aggregate in order to worry about checking it for βchildrenβ?