1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
use std::convert::TryFrom; use std::hash::Hash; /// A trait that defines an `Entity`, which is any object with a unique and globally persistent identity. /// /// The generic type `K` should match the same type as the internal globally unique id used for the entity. /// Be careful when choosing what to return here. The result of [`id()`] will be used as the primary key /// for the entity when communicating with a database via a repository. /// /// # Example /// ```rust /// use domain_patterns::models::Entity; /// /// struct User { /// user_id: String, /// email: String, /// password: String, /// } /// /// impl Entity<String> for User { /// fn id(&self) -> String { /// self.user_id.clone() /// } /// } /// ``` /// /// [`id()`]: ./trait.Entity.html#tymethod.id pub trait Entity<K: Hash + Eq> { fn id(&self) -> K; } /// A trait that defines a `ValueObject` which is an immutable holder of value, that validates that value /// against certain conditions before storing it. /// /// # Example /// ```rust /// use std::{fmt, error}; /// use std::convert::TryFrom; /// use regex::Regex; /// use domain_patterns::models::ValueObject; /// /// /// #[derive(Clone)] /// struct Email { /// address: String, /// } /// /// impl PartialEq for Email { /// fn eq(&self, other: &Self) -> bool { /// self.address == other.address /// } /// } /// /// #[derive(Debug, Clone)] /// struct EmailValidationError; /// /// impl fmt::Display for EmailValidationError { /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { /// write!(f, "Email failed to validate.") /// } /// } /// /// impl error::Error for EmailValidationError { /// fn source(&self) -> Option<&(dyn error::Error + 'static)> { /// None /// } /// } /// /// impl TryFrom<String> for Email { /// type Error = EmailValidationError; /// /// fn try_from(value: String) -> Result<Self, Self::Error> { /// if !Self::validate(&value) { /// return Err(EmailValidationError); /// } /// /// Ok(Email { /// address: value /// }) /// } /// } /// /// impl ValueObject<String> for Email { /// type Error = EmailValidationError; /// /// fn validate(value: &String) -> bool { /// let email_rx = Regex::new( /// r"^(?i)[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$" /// ).unwrap(); /// /// email_rx.is_match(value) /// } /// /// fn value(&self) -> &String { /// return &self.address; /// } /// } /// /// let email = Email::try_from("test_email@email.com".to_string()).unwrap(); /// ``` pub trait ValueObject<T>: Clone + PartialEq + TryFrom<T> { /// The implementer of this trait must point this type at some sort of `Error`. This `Error` should communicate that there was some /// kind of validation error that occurred when trying to create the value object. type Error; /// `validate` takes in incoming data used to construct the value object, and validates it against /// given constraints. An example would be if we had an `Email` struct that implements `ValueObject`. /// The constraints we would check would ensure that the incoming data is a valid email address. /// /// Note: `validate` should be called by your implementation of `try_from`. fn validate(value: &T) -> bool; /// `value` return a reference to the internal value held in the value object. This should be the only /// way that we access the internal data. Mutation methods should always generate a new value object. fn value(&self) -> &T; }