3 ms·
The problem I have with this style of validation is that it doesn't "make illegal states unrepresentable". If the validation is not run then it is still possibl
by maxdeviant 7y ago
The problem I have with this style of validation is that it doesn't "make illegal states unrepresentable". If the validation is not run then it is still possible for instances of `ContactData` that violate the business rules to enter the system.
> Our reservation business requires that contact data entities are only accepted if all of the following conditions are satisfied:
>
> - The e-mail address is valid
> - The phone number is valid
> - Either e-mail address, or phone number, or both are present
In this case, all of the above rules can be encoded into the type system itself. Here's how I would approach this particular problem:
struct PhoneNumber(String);
impl PhoneNumber {
fn new(value: String) -> Result<Self, ValidationError> {
if is_valid_phone_number(value) {
Ok(Self(value))
} else {
Err(ValidationError)
}
}
}
struct EmailAddress(String);
impl EmailAddress {
fn new(value: String) -> Result<Self, ValidationError> {
if is_valid_email_address(value) {
Ok(Self(value))
} else {
Err(ValidationError)
}
}
}
enum ContactData {
PhoneOnly(PhoneNumber),
EmailOnly(EmailAddress),
PhoneAndEmail {
phone: PhoneNumber,
email: EmailAddress
}
}
This approach, when applied alongside Rust's modules and visibility modifiers, would make it impossible for any of these types to be in an invalid state.