|
|
|
|
|
by maxdeviant
2479 days 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. |
|