<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rapid Software Patterns]]></title><description><![CDATA[Practical patterns that simplify and speed up software development in .NET and adaptable to any platform.]]></description><link>https://dmitrydezuk.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 09:05:00 GMT</lastBuildDate><atom:link href="https://dmitrydezuk.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Implementing Cleaner Validation in DDD]]></title><description><![CDATA[In the previous article, I described one approach to validation in DDD. Specifically, I showed how it applies the Result pattern in factory methods of Value Objects to be able to reuse the same guarding checks maintaining their invariants. A drawback...]]></description><link>https://dmitrydezuk.com/implementing-cleaner-validation-in-ddd</link><guid isPermaLink="true">https://dmitrydezuk.com/implementing-cleaner-validation-in-ddd</guid><category><![CDATA[DDD]]></category><category><![CDATA[Validation]]></category><category><![CDATA[clean code]]></category><dc:creator><![CDATA[Dmitry Dezuk (Dezhurnyuk)]]></dc:creator><pubDate>Fri, 10 Jan 2025 05:07:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/KZRg5VM_KO0/upload/f82302a21793632fc8427100fefda298.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous article, I described one approach to validation in DDD. Specifically, I showed how it applies the Result pattern in factory methods of Value Objects to be able to reuse the same guarding checks maintaining their invariants. A drawback in that solution is that the factory methods returning Result&lt;T&gt; have to supply error messages if passed parameters don’t meet pre-conditions for a valid Value Object. Rather you would want to use the error messages in the UI to give a hint or a warning to a user so they can self-correct their input. Thus that validation mixes UI concerns into the core domain while the error/warning/notification communication should typically belong to a presentation or another thin outer layer of an application where you need flexibility to format, visualize, or localize such messages.</p>
<p>One way to factor out the alien responsibility is to use error codes in Validation instead. Those error codes could be nicely organized in the collection of properties of a static class. I will also change the Error property name in Result to ErrorCode to convey my intent. (As you remember you can only improve readability and maintainability if you use maximum explicitness).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Result</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _errorCode;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> ErrorCode
    {
        <span class="hljs-keyword">get</span>
        {
            <span class="hljs-keyword">if</span> (IsSuccess)
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidOperationException(<span class="hljs-string">"No error when success"</span>);
            <span class="hljs-keyword">return</span> _errorCode;
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> IsSuccess =&gt; <span class="hljs-keyword">string</span>.IsNullOrEmpty(_errorCode);

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-title">Result</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> error</span>)</span>
    {
        _errorCode = error;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">Success</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span> =&gt; <span class="hljs-keyword">new</span> Result&lt;T&gt;(<span class="hljs-keyword">value</span>, <span class="hljs-literal">null</span>);
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">Success</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">new</span> Result(<span class="hljs-literal">null</span>);
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">Failure</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">string</span> error</span>)</span> =&gt; <span class="hljs-keyword">new</span> Result&lt;T&gt;(<span class="hljs-keyword">default</span>, error);
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">Failure</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> error</span>)</span>
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrEmpty(error))
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(error));

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Result(error);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">Equals</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> obj</span>)</span>
    {
        <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">is</span> Result other)
        {
            <span class="hljs-keyword">return</span> IsSuccess == other.IsSuccess &amp;&amp; _errorCode == other._errorCode;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">int</span> <span class="hljs-title">GetHashCode</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> _errorCode?.GetHashCode() ?? <span class="hljs-number">0</span>;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Error</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">InEmail</span>
    {
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result EmailNullOrWhitespace { <span class="hljs-keyword">get</span>; } = Result.Failure(<span class="hljs-string">"EMAIL_NULL_OR_WHITESPACE"</span>);
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result InvalidEmailFormat { <span class="hljs-keyword">get</span>; } = Result.Failure(<span class="hljs-string">"INVALID_EMAIL_FORMAT"</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">InCustomerName</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> LAST_NAME = <span class="hljs-string">"LAST_NAME"</span>;
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result LastNameNullOrWhitespace { <span class="hljs-keyword">get</span>; } = NameNullOrWhitespace(LAST_NAME);
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result LastNameMaxLengthExceeded { <span class="hljs-keyword">get</span>; } = NameMaxLengthExceeded(LAST_NAME);
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result LastNameHasInvalidCharacters { <span class="hljs-keyword">get</span>; } = NameHasInvalidCharacters(LAST_NAME);


        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> FIRST_NAME = <span class="hljs-string">"FIRST_NAME"</span>;
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result FirstNameNullOrWhitespace { <span class="hljs-keyword">get</span>; } = NameNullOrWhitespace(FIRST_NAME);
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result FirstNameMaxLengthExceeded { <span class="hljs-keyword">get</span>; } = NameMaxLengthExceeded(FIRST_NAME);
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result FirstNameHasInvalidCharacters { <span class="hljs-keyword">get</span>; } = NameHasInvalidCharacters(FIRST_NAME);

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">NameNullOrWhitespace</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> namePart</span>)</span> =&gt; Result.Failure(<span class="hljs-string">$"<span class="hljs-subst">{namePart}</span>_NULL_OR_WHITESPACE"</span>);
        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">NameMaxLengthExceeded</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> namePart</span>)</span> =&gt; Result.Failure(<span class="hljs-string">$"<span class="hljs-subst">{namePart}</span>_MAX_LENGTH_EXCEEDED"</span>);
        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">NameHasInvalidCharacters</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> namePart</span>)</span> =&gt; Result.Failure(<span class="hljs-string">$"<span class="hljs-subst">{namePart}</span>_HAS_INVALID_CHARACTERS"</span>);
    }
}
</code></pre>
<p>Given those changes I can easily rewrite the factory methods as follows</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">Email</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> EMAIL_PATTERN = <span class="hljs-string">@"^[^@\s]+@[^@\s]+\.[^@\s]+$"</span>;

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _value;

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Email</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        _value = <span class="hljs-keyword">value</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">CanCreate</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(<span class="hljs-keyword">value</span>))
        {
            <span class="hljs-keyword">return</span> Error.InEmail.EmailNullOrWhitespace;
        }

        <span class="hljs-keyword">if</span> (!IsValidEmail(<span class="hljs-keyword">value</span>))
        {
            <span class="hljs-keyword">return</span> Error.InEmail.InvalidEmailFormat;
        }

        <span class="hljs-keyword">return</span> Result.Success();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Email</span>(<span class="hljs-params"></span>) : <span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-literal">null</span></span>)</span> { }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result&lt;Email&gt; <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        <span class="hljs-keyword">var</span> result = CanCreate(<span class="hljs-keyword">value</span>);
        <span class="hljs-keyword">if</span> (!result.IsSuccess)
        {
            <span class="hljs-keyword">return</span> Result.Failure&lt;Email&gt;(result.ErrorCode);
        }

        <span class="hljs-keyword">return</span> Result.Success(<span class="hljs-keyword">new</span> Email(<span class="hljs-keyword">value</span>));
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> _value;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidEmail</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
    {
        <span class="hljs-keyword">return</span> Regex.IsMatch(email, EMAIL_PATTERN);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">Email email</span>)</span>
    {
        <span class="hljs-keyword">return</span> email._value;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">Email</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
    {
        <span class="hljs-keyword">var</span> result = Create(email);
        <span class="hljs-keyword">if</span> (!result.IsSuccess)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(result.ErrorCode);
        }

        <span class="hljs-keyword">return</span> result.Value;
    }
}

<span class="hljs-comment">//----------------------------------------------------</span>

<span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">CustomerName</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MAX_NAME_LENGTH = <span class="hljs-number">100</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> NamePattern = <span class="hljs-string">@"^[A-Z][a-zA-Z]*$"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
    {
        FirstName = firstName;
        LastName = lastName;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">CanCreate</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(firstName))
        {
            <span class="hljs-keyword">return</span> Error.InCustomerName.FirstNameNullOrWhitespace;
        }

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(lastName))
        {
            <span class="hljs-keyword">return</span> Error.InCustomerName.LastNameNullOrWhitespace;
        }

        <span class="hljs-keyword">if</span> (firstName.Length &gt; MAX_NAME_LENGTH)
        {
            <span class="hljs-keyword">return</span> Error.InCustomerName.FirstNameMaxLengthExceeded;
        }

        <span class="hljs-keyword">if</span> (lastName.Length &gt; MAX_NAME_LENGTH)
        {
            <span class="hljs-keyword">return</span> Error.InCustomerName.LastNameMaxLengthExceeded;
        }

        <span class="hljs-keyword">if</span> (!IsValidName(firstName))
        {
            <span class="hljs-keyword">return</span> Error.InCustomerName.FirstNameHasInvalidCharacters;
        }

        <span class="hljs-keyword">if</span> (!IsValidName(lastName))
        {
            <span class="hljs-keyword">return</span> Error.InCustomerName.LastNameHasInvalidCharacters;
        }

        <span class="hljs-keyword">return</span> Result.Success();
    }

    <span class="hljs-comment">//default </span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"></span>) : <span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-literal">null</span>, <span class="hljs-literal">null</span></span>)</span> { }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result&lt;CustomerName&gt; <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
    {
        <span class="hljs-keyword">var</span> result = CanCreate(firstName, lastName);
        <span class="hljs-keyword">if</span> (!result.IsSuccess)
        {
            <span class="hljs-keyword">return</span> Result.Failure&lt;CustomerName&gt;(result.ErrorCode);
        }
        <span class="hljs-keyword">return</span> Result.Success(<span class="hljs-keyword">new</span> CustomerName(firstName, lastName));
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{FirstName}</span> <span class="hljs-subst">{LastName}</span>"</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> name</span>)</span>
    {
        <span class="hljs-keyword">return</span> Regex.IsMatch(name, NamePattern);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">CustomerName customerName</span>)</span>
    {
        <span class="hljs-keyword">return</span> customerName.ToString();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> fullName</span>)</span>
    {
        <span class="hljs-keyword">var</span> names = fullName.Split(<span class="hljs-string">' '</span>);
        <span class="hljs-keyword">if</span> (names.Length != <span class="hljs-number">2</span>)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Full name must contain exactly two parts: first name and last name."</span>);
        }
        <span class="hljs-keyword">var</span> result = Create(names[<span class="hljs-number">0</span>], names[<span class="hljs-number">1</span>]);
        <span class="hljs-keyword">if</span> (!result.IsSuccess)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(result.ErrorCode);
        }

        <span class="hljs-keyword">return</span> result.Value;
    }
}
</code></pre>
<p>To convert error code to formatted message we can introduce an extension helper method in UI or other outer layer communicating with the external world. The below is just the simplest implementation possible. You can add infinite logic here to format, localize, or discriminate the messages on various conditions.</p>
<pre><code class="lang-csharp">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">UserErrorMessages</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">CannotBeNullOrEmpty</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> propertyName</span>)</span> =&gt; <span class="hljs-string">$"<span class="hljs-subst">{propertyName}</span> cannot be null or whitespace"</span>;
        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">TooLong</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> propertyName</span>)</span> =&gt; <span class="hljs-string">$"<span class="hljs-subst">{propertyName}</span> too long"</span>;
        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">NotInValidFormat</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> propertyName</span>)</span> =&gt; <span class="hljs-string">$"<span class="hljs-subst">{propertyName}</span> is not in valid format."</span>;

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-title">Dictionary</span>&lt;<span class="hljs-title">Result</span>, <span class="hljs-title">string</span>&gt; _errorMessageMappings</span> = <span class="hljs-keyword">new</span> Dictionary&lt;Result, <span class="hljs-keyword">string</span>&gt;()
        {
             { Error.InEmail.EmailNullOrWhitespace, CannotBeNullOrEmpty(<span class="hljs-string">"Email"</span>) },
             { Error.InEmail.EmailNullOrWhitespace, NotInValidFormat(<span class="hljs-string">"Email"</span>) },

             { Error.InCustomerName.FirstNameNullOrWhitespace, CannotBeNullOrEmpty(<span class="hljs-string">"First Name"</span>) },
             { Error.InCustomerName.FirstNameMaxLengthExceeded, TooLong(<span class="hljs-string">"First Name"</span>) },
             { Error.InCustomerName.FirstNameHasInvalidCharacters, NotInValidFormat(<span class="hljs-string">"First Name"</span>) },

             { Error.InCustomerName.LastNameNullOrWhitespace, CannotBeNullOrEmpty(<span class="hljs-string">"Last Name"</span>) },
             { Error.InCustomerName.LastNameMaxLengthExceeded, TooLong(<span class="hljs-string">"Last Name"</span>) },
             { Error.InCustomerName.LastNameHasInvalidCharacters, NotInValidFormat(<span class="hljs-string">"Last Name"</span>) },
        };

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">GetErrorMessage</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> Result result</span>)</span> =&gt; _errorMessageMappings[result];
    }
</code></pre>
<p>Finally, let’s review the sample presentation logic of a typical WPF application. The presentation logic is typically encapsulated in a view model.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">CreateCustomerViewModel</span> : <span class="hljs-title">INotifyPropertyChanged</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> HomeInsuranceService _insuranceService;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> _firstName;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> _lastName;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> _email;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> _errorMessage;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">bool</span> _isProcessing;

    <span class="hljs-comment">// Implementation of INotifyPropertyChanged for UI updates</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">event</span> PropertyChangedEventHandler PropertyChanged;
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnPropertyChanged</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> propertyName</span>)</span>
    {
        PropertyChanged?.Invoke(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> PropertyChangedEventArgs(propertyName));
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">CreateCustomerViewModel</span>(<span class="hljs-params">HomeInsuranceService insuranceService</span>)</span>
    {
        _insuranceService = insuranceService ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(insuranceService));

        <span class="hljs-comment">// Initialize the create customer command</span>
        CreateCustomerCommand = <span class="hljs-keyword">new</span> RelayCommand(
            execute: <span class="hljs-keyword">async</span> () =&gt; <span class="hljs-keyword">await</span> CreateCustomerAsync(),
            canExecute: () =&gt; CanCreateCustomer()
        );
    }

    <span class="hljs-comment">// Properties with validation and UI notification</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName
    {
        <span class="hljs-keyword">get</span> =&gt; _firstName;
        <span class="hljs-keyword">set</span>
        {
            <span class="hljs-keyword">if</span> (_firstName != <span class="hljs-keyword">value</span>)
            {
                _firstName = <span class="hljs-keyword">value</span>;
                OnPropertyChanged(<span class="hljs-keyword">nameof</span>(FirstName));

            }
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName
    {
        <span class="hljs-keyword">get</span> =&gt; _lastName;
        <span class="hljs-keyword">set</span>
        {
            <span class="hljs-keyword">if</span> (_lastName != <span class="hljs-keyword">value</span>)
            {
                _lastName = <span class="hljs-keyword">value</span>;
                OnPropertyChanged(<span class="hljs-keyword">nameof</span>(LastName));

            }
        }
    }


    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Email
    {
        <span class="hljs-keyword">get</span> =&gt; _email;
        <span class="hljs-keyword">set</span>
        {
            <span class="hljs-keyword">if</span> (_email != <span class="hljs-keyword">value</span>)
            {
                _email = <span class="hljs-keyword">value</span>;
                OnPropertyChanged(<span class="hljs-keyword">nameof</span>(Email));

            }
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> ErrorMessage
    {
        <span class="hljs-keyword">get</span> =&gt; _errorMessage;
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">set</span>
        {
            <span class="hljs-keyword">if</span> (_errorMessage != <span class="hljs-keyword">value</span>)
            {
                _errorMessage = <span class="hljs-keyword">value</span>;
                OnPropertyChanged(<span class="hljs-keyword">nameof</span>(ErrorMessage));
            }
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> IsProcessing
    {
        <span class="hljs-keyword">get</span> =&gt; _isProcessing;
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">set</span>
        {
            <span class="hljs-keyword">if</span> (_isProcessing != <span class="hljs-keyword">value</span>)
            {
                _isProcessing = <span class="hljs-keyword">value</span>;
                OnPropertyChanged(<span class="hljs-keyword">nameof</span>(IsProcessing));

            }
        }
    }


    <span class="hljs-keyword">public</span> ICommand CreateCustomerCommand { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">CanCreateCustomer</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">if</span> (IsProcessing) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;

        <span class="hljs-keyword">return</span> !<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(FirstName) &amp;&amp;
               !<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(LastName) &amp;&amp;
               !<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(Email)
    }

    <span class="hljs-comment">// Async method to handle customer creation</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">CreateCustomerAsync</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">try</span>
        {
            IsProcessing = <span class="hljs-literal">true</span>;
            ErrorMessage = <span class="hljs-keyword">string</span>.Empty;

            <span class="hljs-comment">// Call the service method</span>
            <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> Task.Run(() =&gt; _insuranceService.CreateCustomer(
                FirstName, LastName,
                Street, City, State, Zip,
                Email,
                DateOfBirth,
                parsedIncome));

            <span class="hljs-keyword">if</span> (result.IsSuccess)
            {
                ErrorMessage = <span class="hljs-keyword">string</span>.Empty;
            }
            <span class="hljs-keyword">else</span>
            {
                <span class="hljs-comment">//mapping the result to formatted error notification</span>
                ErrorMessage = result.GetErrorMessage();
            }
        }
        <span class="hljs-keyword">catch</span> (Exception ex)
        {
            ErrorMessage = <span class="hljs-string">$"An unexpected error occurred"</span>;
        }
        <span class="hljs-keyword">finally</span>
        {
            IsProcessing = <span class="hljs-literal">false</span>;
        }
    }
}
</code></pre>
<p>as indicated above the validation bits can be repeated on various levels. They are just needed to enhance user experience. Here there is a CanCreateCustomer method that ensures the required fields and reports validation error on a shorter notification loop. The message would pop up on a screen faster and that is beneficial for the user who does not have to wait for the round trip to the core domain. That is why such validation has good reason to exist even if it partially repeats the core domain validation.</p>
<p><strong>Summary</strong></p>
<ul>
<li><p>Here we saw a cleaner approach to validation in Domain-Driven Design (DDD) by using error codes instead of error messages in the core domain.</p>
</li>
<li><p>The article highlights the drawbacks of mixing UI concerns with core domain logic when using error messages in factory methods of Value Objects.</p>
</li>
<li><p>The proposed solution involves organizing error codes in a static class and renaming the Error property in Result to ErrorCode for clarity.</p>
</li>
<li><p>An extension helper method can be introduced in the UI or outer layer to convert error codes into formatted messages, allowing for flexibility in message presentation.</p>
</li>
<li><p>The article provides an example of presentation logic in a WPF application, emphasizing the importance of validation at various levels to enhance user experience.</p>
</li>
<li><p>It explains the benefit of having validation logic in the presentation layer for faster feedback to the user, even if it partially overlaps with core domain validation.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Implementing Validation]]></title><description><![CDATA[If you have completed domain mode, your application is almost ready. You can start solving problems that your modeled domain was designed for right after. Just put a thin layer of logic in an application service API reading user input and communicati...]]></description><link>https://dmitrydezuk.com/implementing-validation</link><guid isPermaLink="true">https://dmitrydezuk.com/implementing-validation</guid><category><![CDATA[DDD]]></category><category><![CDATA[Validation]]></category><category><![CDATA[application layer]]></category><dc:creator><![CDATA[Dmitry Dezuk (Dezhurnyuk)]]></dc:creator><pubDate>Sat, 14 Dec 2024 04:32:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/OfHZYig9SQc/upload/91f5f9c219747df49f8ac3644e11f65d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you have completed domain mode, your application is almost ready. You can start solving problems that your modeled domain was designed for right after. Just put a thin layer of logic in an application service API reading user input and communicating it to the domain model scripting and orchestrating all the necessary calls to it. You can avoid too much ceremony and hook up all the UI input fields right to the parameters of the domain model methods.</p>
<p>If your domain is well-designed it will not allow for something bad to happen. It will maintain all the invariants and prevent any kind of system input abuse. But if you do that one thing will suffer and that thing is the user experience. Similarly, a child will suffer if he is given an electric toy car and that car does not have a cover or remote control and all of its wires are sticking out, and to be able to start the engine you need to connect two bare wires. Any mistake in the input of such a system will protect the domain by crashing the application on one of those many exception throws. Any mistake in operating such a car can lead to a burned fuse or break an electronic component. In both cases, you are fine. Your car will not explode, and you won’t get burned. The system won’t send unexpected emails or write incorrect data to the database because it fails fast. However, the user experience will dramatically deteriorate as that person will have to start from scratch.</p>
<p>Here is where the validation comes into play as a handy method to give a user a shield, a second chance, a protection so he can retry without going through the inconveniences of restarting the app (or buying another toy). It is up to you where to put that protection that prohibits calling domain methods before input looks good enough. The validation can be added on the client or server sides, or also on both sides and that is fine if we duplicate validation logic sometimes between too.</p>
<p>Validation does not only filter out bad input that can’t sift through that defense to reach a highly guarded core domain tier. Good validation also provides friendly hints, guidance, clues, and suggestions on how to fix the input that it passes through and engages domain logic without any friction. The domain logic should be very precise and limited and often the input controls allow too many varieties of inputs introducing the same issue as primitive types in a domain model. That is why to make your application successful among users, you need to work hard on changing the behavior of such controls to limit the manifold of all possible inputs according to your core domain expectations (i.e invariants) and to show helpful notifications to correct typing as soon as typo or error is detected. There is a huge space for creativity here as well as many 3d party UI frameworks.</p>
<p>Let’s see the evolution of code from no validation to good validation while writing code for the same use case of adding a customer to an insurance company system.</p>
<pre><code class="lang-csharp">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">HomeInsuranceService</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> Database _database;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HomeInsuranceService</span>(<span class="hljs-params">Database database</span>)</span>
        {
            _database = database ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(database));
        }

        <span class="hljs-comment">//parameters are passed from UI</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">CreateCustomer</span>(<span class="hljs-params">
            <span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName,
            <span class="hljs-keyword">string</span> street, <span class="hljs-keyword">string</span> city, <span class="hljs-keyword">string</span> state, <span class="hljs-keyword">string</span> zip,
            <span class="hljs-keyword">string</span> email, 
            DateTime dateOfBirth,
            <span class="hljs-keyword">decimal</span> annualIncome</span>)</span>
        {
            <span class="hljs-keyword">var</span> customerId = CustomerID.NewCustomerID();
            <span class="hljs-keyword">var</span> customerName = CustomerName.Create(firstName, lastName);
            <span class="hljs-keyword">var</span> customerEmail = Email.Create(email);
            <span class="hljs-keyword">var</span> today = DateTime.Today;
            <span class="hljs-keyword">var</span> customerDateOfBirth = DateOfBirth.Create(dateOfBirth, today);
            <span class="hljs-keyword">var</span> customerHomeAddress = USHomeAddress.Create(street, city, state, zip);
            <span class="hljs-keyword">var</span> customerAnnualIncome = AnnualIncome.Create(annualIncome);
            <span class="hljs-keyword">var</span> customer = Customer.Create(customerId, customerName, customerHomeAddress, customerEmail, customerDateOfBirth, today, customerAnnualIncome);
            _database.SaveCustomer(customer);
        }
    }
</code></pre>
<p>You can see that value objects and Customer entities are created via factory methods instead of using a constructor. That is a typical practice in DDD and is done for many reasons.</p>
<ul>
<li><p>The factory method call is easier to read and does not include the programming language keyword “new.” It conveys the intent much better as it is closely related to the ubiquitous language of the domain experts.</p>
</li>
<li><p>The factory method allows for better encapsulation. Depending on the implementation details and object creation strategies, you can return a singleton, a new instance, or various subtypes introducing polymorphic behavior.</p>
</li>
<li><p>By using factory methods, you can separate the concerns of object creation from the object's behavior.</p>
<p>  However, the most convenient feature of the factory method in Value Object is closely related to validation because they are a great mechanism for reusing guarding checks. But let’s see a naive method to add validation into our functionality creating a customer from the input that arrived straight from UI. Again our goal is to prevent exception throwing in case of a bad input and possibly add some good error notification so a user can get an idea of what to fix before the next retry. Exceptions are also bad due to an incredibly negative effect on general performance. Thus validation comes in pretty handy for achieving optimal performance as well.</p>
</li>
<li><pre><code class="lang-csharp">          <span class="hljs-function"><span class="hljs-keyword">public</span> class <span class="hljs-title">Result</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> error</span>)</span>
          {
              <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Error { <span class="hljs-keyword">get</span>; } = error;
              <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> Success =&gt; <span class="hljs-keyword">string</span>.IsNullOrEmpty(error);

              <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result OK = <span class="hljs-keyword">new</span> Result(<span class="hljs-literal">null</span>);
              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">Failure</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> error</span>)</span>
              {
                  <span class="hljs-keyword">if</span>(<span class="hljs-keyword">string</span>.IsNullOrEmpty(error))
                      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(error));

                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Result(error);
              }
          }

          <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">HomeInsuranceService</span>
          {
              <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">CreateCustomer</span>(<span class="hljs-params">
                  <span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName,
                  <span class="hljs-keyword">string</span> street, <span class="hljs-keyword">string</span> city, <span class="hljs-keyword">string</span> state, <span class="hljs-keyword">string</span> zip,
                  <span class="hljs-keyword">string</span> email, 
                  DateTime dateOfBirth,
                  <span class="hljs-keyword">decimal</span> annualIncome</span>)</span>
              {
                  <span class="hljs-keyword">var</span> customerId = CustomerID.NewCustomerID();
                  <span class="hljs-keyword">var</span> result = CustomerName.CanCreate(firstName, lastName);
                  <span class="hljs-keyword">if</span>(!result.Success)
                      <span class="hljs-keyword">return</span> result;
                  <span class="hljs-keyword">var</span> customerName = CustomerName.Create(firstName, lastName);

                  result = Email.CanCreate(email);
                  <span class="hljs-keyword">if</span>(!result.Success)
                      <span class="hljs-keyword">return</span> result;
                  <span class="hljs-keyword">var</span> customerEmail = Email.Create(email);

                  <span class="hljs-comment">/*
                      Same approach for other value objects...
                  */</span>

                  <span class="hljs-keyword">var</span> customer = Customer.Create(customerId, customerName, customerHomeAddress, customerEmail, customerDateOfBirth, today, customerAnnualIncome);
                  _database.SaveCustomer(customer);

                  <span class="hljs-keyword">return</span> Result.OK;
               }
          }

          <span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">CustomerName</span>
          {
              <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MAX_NAME_LENGTH = <span class="hljs-number">100</span>;
              <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> NamePattern = <span class="hljs-string">@"^[A-Z][a-zA-Z]*$"</span>;

              <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; }
              <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>; }

              <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
              {
                  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(firstName))
                  {
                      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"First name cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(firstName));
                  }

                  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(lastName))
                  {
                      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Last name cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(lastName));
                  }

                  <span class="hljs-keyword">if</span> (firstName.Length &gt; MAX_NAME_LENGTH || lastName.Length &gt; MAX_NAME_LENGTH)
                  {
                      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">$"First and last names cannot exceed <span class="hljs-subst">{MAX_NAME_LENGTH}</span> characters."</span>);
                  }

                  <span class="hljs-keyword">if</span> (!IsValidName(firstName) || !IsValidName(lastName))
                  {
                      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Names must contain only letters and start with a capital letter."</span>);
                  }

                  FirstName = firstName;
                  LastName = lastName;
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">CanCreate</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
              {
                  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(firstName))
                  {
                      <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"First name cannot be null or whitespace."</span>);
                  }

                  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(lastName))
                  {
                      <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"Last name cannot be null or whitespace."</span>);
                  }

                  <span class="hljs-keyword">if</span> (firstName.Length &gt; MAX_NAME_LENGTH || lastName.Length &gt; MAX_NAME_LENGTH)
                  {
                      <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">$"First and last names cannot exceed <span class="hljs-subst">{MAX_NAME_LENGTH}</span> characters."</span>);
                  }

                  <span class="hljs-keyword">if</span> (!IsValidName(firstName) || !IsValidName(lastName))
                  {
                      <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"Names must contain only letters and start with a capital letter."</span>);
                  }

                  <span class="hljs-keyword">return</span> Result.OK;
              }

              <span class="hljs-comment">//default </span>
              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"></span>):<span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-literal">null</span>, <span class="hljs-literal">null</span></span>)</span> { }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> CustomerName <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
              {
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> CustomerName(firstName, lastName);
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
              {
                  <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{FirstName}</span> <span class="hljs-subst">{LastName}</span>"</span>;
              }

              <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> name</span>)</span>
              {
                  <span class="hljs-keyword">return</span> Regex.IsMatch(name, NamePattern);
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">CustomerName customerName</span>)</span>
              {
                  <span class="hljs-keyword">return</span> customerName.ToString();
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> fullName</span>)</span>
              {
                  <span class="hljs-keyword">var</span> names = fullName.Split(<span class="hljs-string">' '</span>);
                  <span class="hljs-keyword">if</span> (names.Length != <span class="hljs-number">2</span>)
                  {
                      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Full name must contain exactly two parts: first name and last name."</span>);
                  }
                  <span class="hljs-keyword">return</span> Create(names[<span class="hljs-number">0</span>], names[<span class="hljs-number">1</span>]);
              }
          }

          <span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">Email</span>
          {
              <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> EMAIL_PATTERN = <span class="hljs-string">@"^[^@\s]+@[^@\s]+\.[^@\s]+$"</span>;

              <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _value;

              <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Email</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
              {
                  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(<span class="hljs-keyword">value</span>))
                  {
                      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Email cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(<span class="hljs-keyword">value</span>));
                  }

                  <span class="hljs-keyword">if</span> (!IsValidEmail(<span class="hljs-keyword">value</span>))
                  {
                      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Invalid email format."</span>, <span class="hljs-keyword">nameof</span>(<span class="hljs-keyword">value</span>));
                  }

                  _value = <span class="hljs-keyword">value</span>;
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">CanCreate</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
              {
                  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(<span class="hljs-keyword">value</span>))
                  {
                      <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"Email cannot be null or whitespace."</span>);
                  }

                  <span class="hljs-keyword">if</span> (!IsValidEmail(<span class="hljs-keyword">value</span>))
                  {
                      <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"Invalid email format."</span>);
                  }

                  <span class="hljs-keyword">return</span> Result.OK;
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Email</span>(<span class="hljs-params"></span>):<span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-literal">null</span></span>)</span> { }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Email <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
              {
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Email(<span class="hljs-keyword">value</span>);
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
              {
                  <span class="hljs-keyword">return</span> _value;
              }

              <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidEmail</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
              {
                  <span class="hljs-keyword">return</span> Regex.IsMatch(email, EMAIL_PATTERN);
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">Email email</span>)</span>
              {
                  <span class="hljs-keyword">return</span> email._value;
              }

              <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">Email</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
              {
                  <span class="hljs-keyword">return</span> Create(email);
              }
          }
</code></pre>
<p>  That is quite an improvement as we introduced validating helper methods to tell whether the passed parameters are good to go. The validation result is reported in a Result object that encapsulates the outcome of a validation. The outcome must have error details in a non-empty string if validation fails. The logic only reaches the customer factory method only and only if all prior validation results represent success. We placed validation next to the constructor of Value Objects to show that they have the same guarding checks. However, validation could be placed somewhere else as it is strictly speaking a separate concern. There is no reason to not go DRY here though and make those guarding checks shareable for better maintainability and this is how our factory methods get to our rescue.</p>
</li>
<li><p>If we extend the Result class with a property that can optionally return a value, we can reuse validation code in invariant checks.</p>
</li>
<li><pre><code class="lang-csharp">  <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Result</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _error;

        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Error
        {
            <span class="hljs-keyword">get</span>
            {
                <span class="hljs-keyword">if</span> (IsSuccess)
                    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidOperationException(<span class="hljs-string">"No error when success"</span>);
                <span class="hljs-keyword">return</span> _error;
            }
        }

        <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> IsSuccess =&gt; <span class="hljs-keyword">string</span>.IsNullOrEmpty(_error);

        <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-title">Result</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> error</span>)</span>
        {
            _error = error;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">Success</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span> =&gt; <span class="hljs-keyword">new</span> Result&lt;T&gt;(<span class="hljs-keyword">value</span>, <span class="hljs-literal">null</span>);
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">Success</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">new</span> Result(<span class="hljs-literal">null</span>);
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">Failure</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">string</span> error</span>)</span> =&gt; <span class="hljs-keyword">new</span> Result&lt;T&gt;(<span class="hljs-keyword">default</span>, error);
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">Failure</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> error</span>)</span>
        {
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrEmpty(error))
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(error));

            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Result(error);
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">Equals</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> obj</span>)</span>
        {
            <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">is</span> Result other)
            {
                <span class="hljs-keyword">return</span> IsSuccess == other.IsSuccess &amp;&amp; _error == other._error;
            }
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">int</span> <span class="hljs-title">GetHashCode</span>(<span class="hljs-params"></span>)</span>
        {
            <span class="hljs-keyword">return</span> HashCode.Combine(IsSuccess, _error);
        }
    }

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; : <span class="hljs-title">Result</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> T _value;

        <span class="hljs-keyword">public</span> T Value
        {
            <span class="hljs-keyword">get</span>
            {
                <span class="hljs-keyword">if</span> (!IsSuccess)
                    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidOperationException(<span class="hljs-string">"No value when there is an error"</span>);
                <span class="hljs-keyword">return</span> _value;
            }
        }

        <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">internal</span> <span class="hljs-title">Result</span>(<span class="hljs-params">T <span class="hljs-keyword">value</span>, <span class="hljs-keyword">string</span> error</span>) : <span class="hljs-title">base</span>(<span class="hljs-params">error</span>)</span>
        {
            _value = <span class="hljs-keyword">value</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">Equals</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> obj</span>)</span>
        {
            <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">is</span> Result&lt;T&gt; other)
            {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">base</span>.Equals(obj) &amp;&amp; EqualityComparer&lt;T&gt;.Default.Equals(_value, other._value);
            }
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">int</span> <span class="hljs-title">GetHashCode</span>(<span class="hljs-params"></span>)</span>
        {
            <span class="hljs-keyword">return</span> HashCode.Combine(<span class="hljs-keyword">base</span>.GetHashCode(), _value);
        }
    }
</code></pre>
<p>  Value Objects</p>
</li>
<li><pre><code class="lang-csharp">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">CustomerName</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MAX_NAME_LENGTH = <span class="hljs-number">100</span>;
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> NamePattern = <span class="hljs-string">@"^[A-Z][a-zA-Z]*$"</span>;

        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>; }

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
        {
            FirstName = firstName;
            LastName = lastName;
        }

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">CanCreate</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
        {
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(firstName))
            {
                <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"First name cannot be null or whitespace."</span>);
            }

            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(lastName))
            {
                <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"Last name cannot be null or whitespace."</span>);
            }

            <span class="hljs-keyword">if</span> (firstName.Length &gt; MAX_NAME_LENGTH || lastName.Length &gt; MAX_NAME_LENGTH)
            {
                <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">$"First and last names cannot exceed <span class="hljs-subst">{MAX_NAME_LENGTH}</span> characters."</span>);
            }

            <span class="hljs-keyword">if</span> (!IsValidName(firstName) || !IsValidName(lastName))
            {
                <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"Names must contain only letters and start with a capital letter."</span>);
            }

            <span class="hljs-keyword">return</span> Result.Success();
        }

        <span class="hljs-comment">//default </span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"></span>) : <span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-literal">null</span>, <span class="hljs-literal">null</span></span>)</span> { }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result&lt;CustomerName&gt; <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
        {
            <span class="hljs-keyword">var</span> result = CanCreate(firstName, lastName);
            <span class="hljs-keyword">if</span> (!result.IsSuccess)
            {
                <span class="hljs-keyword">return</span> Result.Failure&lt;CustomerName&gt;(result.Error);
            }
            <span class="hljs-keyword">return</span> Result.Success(<span class="hljs-keyword">new</span> CustomerName(firstName, lastName));
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
        {
            <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{FirstName}</span> <span class="hljs-subst">{LastName}</span>"</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> name</span>)</span>
        {
            <span class="hljs-keyword">return</span> Regex.IsMatch(name, NamePattern);
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">CustomerName customerName</span>)</span>
        {
            <span class="hljs-keyword">return</span> customerName.ToString();
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> fullName</span>)</span>
        {
            <span class="hljs-keyword">var</span> names = fullName.Split(<span class="hljs-string">' '</span>);
            <span class="hljs-keyword">if</span> (names.Length != <span class="hljs-number">2</span>)
            {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Full name must contain exactly two parts: first name and last name."</span>);
            }
            <span class="hljs-keyword">var</span> result = Create(names[<span class="hljs-number">0</span>], names[<span class="hljs-number">1</span>]);
            <span class="hljs-keyword">if</span>(!result.IsSuccess) { 
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(result.Error);
            }

            <span class="hljs-keyword">return</span> result.Value;
        }
    }
</code></pre>
</li>
<li><pre><code class="lang-csharp">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">Email</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> EMAIL_PATTERN = <span class="hljs-string">@"^[^@\s]+@[^@\s]+\.[^@\s]+$"</span>;

        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _value;

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Email</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
        {
            _value = <span class="hljs-keyword">value</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title">CanCreate</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
        {
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(<span class="hljs-keyword">value</span>))
            {
                <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"Email cannot be null or whitespace."</span>);
            }

            <span class="hljs-keyword">if</span> (!IsValidEmail(<span class="hljs-keyword">value</span>))
            {
                <span class="hljs-keyword">return</span> Result.Failure(<span class="hljs-string">"Invalid email format."</span>);
            }

            <span class="hljs-keyword">return</span> Result.Success();
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Email</span>(<span class="hljs-params"></span>) : <span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-literal">null</span></span>)</span> { }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result&lt;Email&gt; <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
        {
            <span class="hljs-keyword">var</span> result = CanCreate(<span class="hljs-keyword">value</span>);
            <span class="hljs-keyword">if</span>(!result.IsSuccess)
            {
                <span class="hljs-keyword">return</span> Result.Failure&lt;Email&gt;(result.Error);
            }

            <span class="hljs-keyword">return</span> Result.Success(<span class="hljs-keyword">new</span> Email(<span class="hljs-keyword">value</span>));
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
        {
            <span class="hljs-keyword">return</span> _value;
        }

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidEmail</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
        {
            <span class="hljs-keyword">return</span> Regex.IsMatch(email, EMAIL_PATTERN);
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">Email email</span>)</span>
        {
            <span class="hljs-keyword">return</span> email._value;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">Email</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
        {
            <span class="hljs-keyword">var</span> result = Create(email);
            <span class="hljs-keyword">if</span> (!result.IsSuccess)
            {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(result.Error);
            }

            <span class="hljs-keyword">return</span> result.Value;
        }
    }
</code></pre>
<p>  Customer Entity and Application Service called from UI layer (it could be WPF view model, Razor view or MVC/WebApi Controller)</p>
</li>
<li><pre><code class="lang-csharp">
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Customer</span>
    {
        <span class="hljs-keyword">public</span> CustomerID ID { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> CustomerName Name { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> USHomeAddress Address { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> Email Email { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> DateOfBirth DateOfBirth { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> AnnualIncome AnnualIncome { <span class="hljs-keyword">get</span>; }

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Customer</span>(<span class="hljs-params">CustomerID id, CustomerName name, USHomeAddress address, Email email, DateOfBirth dateOfBirth, AnnualIncome annualIncome</span>)</span>
        {
            <span class="hljs-keyword">this</span>.ID = id;
            <span class="hljs-keyword">this</span>.Name = name;
            <span class="hljs-keyword">this</span>.Address = address;
            <span class="hljs-keyword">this</span>.Email = email;
            <span class="hljs-keyword">this</span>.DateOfBirth = dateOfBirth;
            <span class="hljs-keyword">this</span>.AnnualIncome = annualIncome;
        }

        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">uint</span> MIN_AGE = <span class="hljs-number">18</span>;
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">decimal</span> MIN_INCOME = <span class="hljs-number">80</span>_000;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result&lt;Customer&gt; <span class="hljs-title">Create</span>(<span class="hljs-params">CustomerID id, CustomerName name, USHomeAddress address, Email email, DateOfBirth dateOfBirth, DateTime currentDate, AnnualIncome annualIncome</span>)</span>
        {
            <span class="hljs-keyword">if</span> (dateOfBirth.GetAge(currentDate) &lt; MIN_AGE)
            {
                <span class="hljs-keyword">return</span> Result.Failure&lt;Customer&gt;(<span class="hljs-string">$"Customer must be at least <span class="hljs-subst">{MIN_AGE}</span> years old."</span>);
            }
            <span class="hljs-keyword">if</span> (annualIncome.Value &lt; MIN_INCOME)
            {
                <span class="hljs-keyword">return</span> Result.Failure&lt;Customer&gt;(<span class="hljs-string">$"Annual gross income should be at least $<span class="hljs-subst">{MIN_INCOME}</span>$."</span>);
            }

            <span class="hljs-keyword">return</span> Result.Success&lt;Customer&gt;(<span class="hljs-keyword">new</span> Customer(id, name, address, email, dateOfBirth, annualIncome));
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">HomeInsuranceService</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> Database _database;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HomeInsuranceService</span>(<span class="hljs-params">Database database</span>)</span>
        {
            _database = database ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(database));
        }

        <span class="hljs-comment">//parameters are passed from UI</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">CreateCustomer</span>(<span class="hljs-params">
            <span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName,
            <span class="hljs-keyword">string</span> street, <span class="hljs-keyword">string</span> city, <span class="hljs-keyword">string</span> state, <span class="hljs-keyword">string</span> zip,
            <span class="hljs-keyword">string</span> email,
            DateTime dateOfBirth,
            <span class="hljs-keyword">decimal</span> annualIncome</span>)</span>
        {
            <span class="hljs-keyword">var</span> customerId = CustomerID.NewCustomerID();

            <span class="hljs-keyword">var</span> customerNameResult = CustomerName.Create(firstName, lastName);
            <span class="hljs-keyword">if</span> (!customerNameResult.IsSuccess)
                <span class="hljs-keyword">return</span> customerNameResult;

            <span class="hljs-keyword">var</span> customerEmailResult = Email.Create(email);
            <span class="hljs-keyword">if</span> (!customerEmailResult.IsSuccess)
                <span class="hljs-keyword">return</span> customerEmailResult;

            <span class="hljs-keyword">var</span> customerResult = Customer.Create(customerId, 
                    customerNameResult.Value, 
                    customerHomeAddressResult.Value, 
                    customerEmailResult.Value, 
                    customerDateOfBirthResult.Value, 
                    today, 
                    customerAnnualIncomeResult.Value);
            <span class="hljs-keyword">if</span> (!customerResult.IsSuccess)
                <span class="hljs-keyword">return</span> customerResult;
            _database.SaveCustomer(customerResult.Value);

            <span class="hljs-keyword">return</span> Result.Success();
        }
    }
</code></pre>
</li>
<li><p>Now that this code does not repeat any checks and still has the rigidity or vigilance on invalid input but with the added convenience of validation. Now, the client code or a user will be better off figuring out how to pass valid data to your domain model. We were able to combine validation with checking invariants because we made our domain mode API more condensed. CanCreate methods were removed from public access and Factory extended the return value to Result object. That is pretty much in line with our original motto to provide a bare minimum of what consumer code or users can do with your core domain API. It is still worth stressing one more time that validation and invariant checks are different concerns and it is fine to keep them separate in the code base. Validation can do more sophisticated analysis of data, but oftentimes they are close enough so you can try to combine them in the Result pattern without compromising on DDD rules. This Result design pattern is implementation details and does not ruin primary aspects of observable behavior that should keep objects (value or entity) always valid through the sequence of calls.</p>
</li>
<li><p>I would argue that in addition to the core validation in the Application Layer (i.e. in HomeInsuranceService), it may or even should be added on other architectural levels. The same set of rules can be also evaluated in the javascript code (client-side presentation layer) or be embedded in the WPF/Razor UI controls. These supplemental validations are not meant to replace the core validation and should not be considered as duplicated code. Those layers are designed to enhance users’ experience by giving feedback faster and using more engaging communication. Sometimes technologies such as ASP.NET have gimmicks to share validation code (using various pre-built validator components) but generally, that is not shareable and you don’t need to push for being DRY here. However, some sensible shareable bits would be beneficial. You can reuse a regex expression constants and bind it through code behind or binding expression. Whatever is practical is fine but generally not required.</p>
</li>
<li><p>By looking at the above implementation you can see a “smell” in validation. Formatting or providing the error message is not one of its designated concerns. The message belongs to the Presentation Layer as it knows exactly how to display, format, and present an error message or notification. The application can support multiple languages after all and our domain layer should be language-agnostic in that sense. I will show how this aspect is addressed in “clean” Validation in the next article.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Implementing an Entity]]></title><description><![CDATA[After completing the code for all Value Objects held by a customer, we can revisit the DDD-friendly model of the Customer Entity itself, which will impose one more invariant addition to all the invariants Value Objects are encapsulating. The domain e...]]></description><link>https://dmitrydezuk.com/implementing-an-entity</link><guid isPermaLink="true">https://dmitrydezuk.com/implementing-an-entity</guid><category><![CDATA[DDD]]></category><category><![CDATA[entity]]></category><category><![CDATA[patterns]]></category><dc:creator><![CDATA[Dmitry Dezuk (Dezhurnyuk)]]></dc:creator><pubDate>Thu, 12 Dec 2024 04:49:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/MeGmdPNe36w/upload/61b46725a6b080f18568c55b6fd23d40.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After completing the code for all Value Objects held by a customer, we can revisit the DDD-friendly model of the Customer Entity itself, which will impose one more invariant addition to all the invariants Value Objects are encapsulating. The domain expert requires the customer to be mature enough and should have an annual gross income of at least 80K.</p>
<pre><code class="lang-csharp">
<span class="hljs-keyword">abstract</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Entity</span>&lt;<span class="hljs-title">TId</span>&gt; <span class="hljs-keyword">where</span> <span class="hljs-title">TId</span> : <span class="hljs-title">IEquatable</span>&lt;<span class="hljs-title">TId</span>&gt;
{
    <span class="hljs-keyword">public</span> TId ID { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Entity</span>&lt;<span class="hljs-title">TId</span>&gt;(<span class="hljs-params">TId id</span>)</span>
    {
        Id = id;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Customer</span> : <span class="hljs-title">Entity</span>&lt;<span class="hljs-title">TId</span>&gt;
{
    <span class="hljs-keyword">public</span> CustomerName Name { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> USHomeAddress Address { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> Email Email { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> DateOfBirth DateOfBirth { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> AnnualIncome AnnualIncome { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Customer</span>(<span class="hljs-params">CustomerID id, CustomerName name, USHomeAddress address, Email email, DateOfBirth dateOfBirth, AnnualIncome annualIncome</span>) : 
        <span class="hljs-title">base</span>(<span class="hljs-params">id</span>)</span>
    {
        <span class="hljs-keyword">this</span>.Name = name;
        <span class="hljs-keyword">this</span>.Address = address;
        <span class="hljs-keyword">this</span>.Email = email;
        <span class="hljs-keyword">this</span>.DateOfBirth = dateOfBirth;
        <span class="hljs-keyword">this</span>.AnnualIncome = annualIncome; 
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">uint</span> MIN_AGE = <span class="hljs-number">18</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">decimal</span> MIN_INCOME = <span class="hljs-number">80</span>_000;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Customer <span class="hljs-title">Create</span>(<span class="hljs-params">CustomerID id, CustomerName name, USHomeAddress address, Email email, DateOfBirth dateOfBirth, AnnualIncome annualIncome, DateTime currentDate</span>)</span>
    {
        <span class="hljs-keyword">if</span> (dateOfBirth.GetAge(currentDate) &lt; MIN_AGE )
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">$"Customer must be at least <span class="hljs-subst">{MIN_AGE}</span> years old."</span>, <span class="hljs-keyword">nameof</span>(dateOfBirth));
        }
        <span class="hljs-keyword">if</span>(annualIncome.Value &lt; MIN_INCOME)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">$"Annual gross income should be at least $<span class="hljs-subst">{MIN_INCOME}</span>$."</span>, <span class="hljs-keyword">nameof</span>(annualIncome));
        }

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Customer(id, name, address, email, dateOfBirth, annualIncome);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">AnnualIncome</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">decimal</span> MinIncome = <span class="hljs-number">100</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">decimal</span> MaxIncome = <span class="hljs-number">100</span>_000_000;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">decimal</span> Value { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AnnualIncome</span>(<span class="hljs-params"></span>) : <span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-keyword">decimal</span>.Zero</span>)</span> { }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">AnnualIncome</span>(<span class="hljs-params"><span class="hljs-keyword">decimal</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">value</span> &lt; MinIncome || <span class="hljs-keyword">value</span> &gt; MaxIncome)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">$"Annual income must be between <span class="hljs-subst">{MinIncome}</span>$ and <span class="hljs-subst">{MaxIncome}</span>$."</span>, <span class="hljs-keyword">nameof</span>(<span class="hljs-keyword">value</span>));
        }

        Value = <span class="hljs-keyword">value</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> AnnualIncome <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">decimal</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AnnualIncome(<span class="hljs-keyword">value</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> Value.ToString(<span class="hljs-string">"C"</span>); <span class="hljs-comment">// Format as currency</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">decimal</span>(<span class="hljs-params">AnnualIncome annualIncome</span>)</span>
    {
        <span class="hljs-keyword">return</span> annualIncome.Value;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">AnnualIncome</span>(<span class="hljs-params"><span class="hljs-keyword">decimal</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        <span class="hljs-keyword">return</span> Create(<span class="hljs-keyword">value</span>);
    }
}
</code></pre>
<p>Notice how the Entity differs from the Value Object. It possesses an identity represented by some ID that is a characteristic that does not change throughout the entity's life, whether it mutates or not. We stressed that fact in our domain model by introducing an Entity base class. Entities can be tracked and referenced by their IDs from other entities. That is also why the ID should be equitable to at least say whether IDs represent the same entity object or not.</p>
<p>Entity also strengthens generic weaker invariants of Value Object by putting additional constraints on top of them. The goal here is to be as profuse with constraints as possible. More constraints lead to less uncertainty and more robust code. The more limiting concepts you bring from the real-world domain into the code, the less opportunity you leave for misinterpretation or malfunction.</p>
<p>The same rule applies here that the coder domain model should be pure, i.e. technology agnostic, without side effects or external dependencies such as logging, system clock, emails, databases, network calls, IO system, communication bus, benchmarks, alerts, frameworks, etc. Prefer packing all the sensible invariants into immutable Value Objects and doing all the state transitioning in Entities. Follow a functional style of programming wherever possible and make your methods honest. I will elaborate on those topics a bit later.</p>
<p>The point of the domain model layer is to give you absolute freedom from the technical aspect of your programming language and executing environment so you can remain unbiased and focused solely on domain knowledge and the “offline” business you are creating your solution for. The more logic you can squeeze into that layer the better as you automatically increase the level of control over your code. That code is highly testable, debuggable, readable, and easy to work because accidental complexity is at a minimum here.</p>
<p>You might yell, hmm, how in the hell I am going to be efficient technically so that the domain model runs fast on modern hardware, and provides runtime transparency by leveraging logging, telemetry, health checks, heart beats? You want async/await, multi-threading, structured logging, service bus, serialization, security/authorization management, storage, communication, and so on. The quick answer those concerns have no place in the domain model and generally get offset to the outer boundaries of the system but that does not mean that the domain model should be completely ignorant of those pure technical use cases. Yes, you should not embed the features above in the declaration of your entities or value objects because those features don’t have real-life double or analog in a real domain. Still, you can provide technology-agnostic hookups that can be leveraged in those technical scenarios.</p>
<p>Yes, domain logic should be <strong>synchronous and thread-unsafe, i.e. free of any multithreading or asynchronous/delayed processing/synchronization</strong> but it can be implemented granular enough for parallelism orchestrated by the application layer. Yes, we don’t add explicit loggers or telemetry publishers but we provide domain events that can be intercepted and logged on upper layers so we can understand what is going on in the domain layer. Yes, we don’t have methods in entities to save or get them from a database but we have application services “repositories” to do so efficiently. Many more things are not allowed in the core domain to keep it clean, fast, reliable, and easy to work with but in reality, it is not limiting you in your technology stack. You will still be able to add nicely all the other aspects and concerns but do it in a specific way so you don’t compromise the purity of the domain model with extrenious code.</p>
<p>If the core domain, i.e. the heart of the system is designed right you should be able to observe and track the harmony of the code modeling it. When the code is written correctly following the rules I am going to be talking about here you should feel immediate satisfaction, fulfillment, and confidence in the reliability of your work. If you feel the opposite, i.e. something smells or gut feeling puts you in slight discomfort, that is a sign you are missing something your frame is shaky and you can’t build a reliable structure on top of it. The appreciation of symmetry is in our nature. I would argue that we are born with it. Every symmetry is perceived as beautiful by our mind and the beauty represents the most stable constructions in the universe. Look at all those natural biological ornaments, fractals, spirals, Fibonacci sequences, or golden ratios. Those are the bricks of perfection and the more such bricks you incorporate into your software solution, the more adequate and robust it should be.</p>
<p>The canonical domain model should look like a hierarchy of objects where limitations/invariants are added from the leaf to the root layer. The root is usually represented by an entity that is called an aggregate root in domain-driven design. The aggregate is the smallest group of interconnected entities maintaining transactional consistency. The invariants in the aggregate are held at all times and changes are applied atomically over all objects included in that aggregate. The hierarchy (“tree“ like) is a very convenient structure well understood by our mind as it finds manifestation in almost every object in nature that is why it is beneficial to stick to the “tree” hierarchy in the domain model.</p>
<p>Also, psychologists say we can’t operate with more than 7 objects in our memory at a time so I take that number as a rule of thumb when designing a system. Every modeled concept should not consist of more than 7 constituents. Methods should not do more than 7 decision making, entities/value objects should not contain more than 7 properties, given aggregate (group of objects bound by certain rules or limitations) should be comprised of more than 7 of other constituents, a context (“bounded context“ in DDD terms) should not contain more than 7 aggregates.</p>
<p>I will expand on the concepts that did not find detailed review in this article later. For now, I would like to linger on a very important aspect of software design which is Validation. I will talk about validation in the next article and show where it belongs in relation to the core domain.</p>
]]></content:encoded></item><item><title><![CDATA[Implementing a Value Object]]></title><description><![CDATA[In the previous article, I introduced the concept of Value Objects as handy wrappers around primitive C# types. These objects ensure that they represent a valid value and enforce Customer invariants, which are the explicit and implicit rules that are...]]></description><link>https://dmitrydezuk.com/implementing-a-value-object</link><guid isPermaLink="true">https://dmitrydezuk.com/implementing-a-value-object</guid><category><![CDATA[value objects]]></category><category><![CDATA[DDD]]></category><category><![CDATA[C#]]></category><dc:creator><![CDATA[Dmitry Dezuk (Dezhurnyuk)]]></dc:creator><pubDate>Thu, 05 Dec 2024 05:53:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/mgJSkgIo_JI/upload/657e2a25aeb09d959f17d20a821ee3ab.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous article, I introduced the concept of Value Objects as handy wrappers around primitive C# types. These objects ensure that they represent a valid value and enforce Customer invariants, which are the explicit and implicit rules that are always held. The Value Objects protect the invariants on the property level regardless of any container entity or another value object because they are designed to be reusable by Entities and other container Value Objects. They remove a good deal of uncertainty, carving a subset representing valid states out of the universe of possible values of their primitive types.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733374335310/7b550f73-46e1-4e94-9af0-57625750d899.png" alt class="image--center mx-auto" /></p>
<p>We can’t avoid primitive types, as they are the building blocks of our code, but we need to tame them to our needs by rigorously controlling them in Value Objects.</p>
<p>Let’s write code for the value objects from the previous article by starting with the simplest one</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">struct</span> CustomerID
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> CustomerID <span class="hljs-title">Empty</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">new</span> CustomerID(<span class="hljs-number">0</span>);
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> CustomerID <span class="hljs-title">NewCustomerID</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">new</span> CustomerID((<span class="hljs-keyword">uint</span>)IdGenerator.GetNextId());

    <span class="hljs-comment">//it can be Guid, string, or any other type chosen based</span>
    <span class="hljs-comment">//on performance and convenience requirements</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">uint</span> Value;

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">CustomerID</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        Value = <span class="hljs-keyword">value</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">Equals</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> obj</span>)</span>
    {
        <span class="hljs-keyword">return</span> obj <span class="hljs-keyword">is</span> CustomerID iD &amp;&amp; Equals(iD);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">Equals</span>(<span class="hljs-params">CustomerID other</span>)</span>
    {
        <span class="hljs-keyword">return</span> Value == other.Value;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">int</span> <span class="hljs-title">GetHashCode</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> Value.GetHashCode();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-keyword">operator</span> ==(CustomerID left, CustomerID right)
    {
        <span class="hljs-keyword">return</span> left.Equals(right);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-keyword">operator</span> !=(CustomerID left, CustomerID right)
    {
        <span class="hljs-keyword">return</span> !(left == right);
    }
}

<span class="hljs-comment">//Guid has the most convenient built-in generator but it may not be performant enough</span>
<span class="hljs-comment">//on the storage side</span>
<span class="hljs-keyword">internal</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IdGenerator</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">long</span> _currentId = <span class="hljs-number">0</span>;

    <span class="hljs-comment">// called at application startup using some seeding strategy</span>
    <span class="hljs-function"><span class="hljs-keyword">internal</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">SetIdGenerator</span>(<span class="hljs-params"><span class="hljs-keyword">long</span> seed</span>)</span>
    {
        _currentId = seed;
    }

    <span class="hljs-function"><span class="hljs-keyword">internal</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">long</span> <span class="hljs-title">GetNextId</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> Interlocked.Increment(<span class="hljs-keyword">ref</span> _currentId);
    }
}
</code></pre>
<p>Value Objects are typically immutable as they are identified by the values they hold, rather than by some identity or reference in memory. So if you need it to mutate, you just create a new Value Object with changed values replacing the old one. Thus you simplify the code (see previous article for details) and avoid many other potential problems with Value Objects when sharing them or using them in multi-threaded applications. That is why the concept of equality is very important here as it defines how instances of these objects are compared and identified. Since they don’t have an identity, equality is the only way to tell whether two objects represent the same characteristic and that is often handy in all kinds of reconciliation and lookup in the application logic. That makes Value Objects a good choice for sorting, filtering, and finding. C# also encourages to implementation of GetHashCode whenever we implement equality and that makes them great for Dictionary keys or HashSets. Now CustomerId can be easily used for Customer lookup by its ID.</p>
<p>All that equality plumbing can be easily collapsed into a few lines of code with the newer C# syntax. We don’t add any additional invariants as the unit type already represents the intent to store the ID as a positive number.</p>
<pre><code class="lang-csharp">    <span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-keyword">struct</span> <span class="hljs-title">CustomerID</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span> Value</span>)</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> CustomerID <span class="hljs-title">Empty</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">new</span> CustomerID(<span class="hljs-number">0</span>);
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> CustomerID <span class="hljs-title">NewCustomerID</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">new</span> CustomerID((<span class="hljs-keyword">uint</span>)IdGenerator.GetNextId());

        <span class="hljs-comment">// we can still add more code to make specialized constructor private</span>
    }
</code></pre>
<p>I digressed though and let’s move on to the Email where we have a bit more handmade constraints.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">Email</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> EMAIL_PATTERN = <span class="hljs-string">@"^[^@\s]+@[^@\s]+\.[^@\s]+$"</span>;

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _value;

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Email</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(<span class="hljs-keyword">value</span>))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Email cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(<span class="hljs-keyword">value</span>));
        }

        <span class="hljs-keyword">if</span> (!IsValidEmail(<span class="hljs-keyword">value</span>))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Invalid email format."</span>, <span class="hljs-keyword">nameof</span>(<span class="hljs-keyword">value</span>));
        }

        _value = <span class="hljs-keyword">value</span>;
    }

    <span class="hljs-comment">//prohibiting default constructor</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Email</span>(<span class="hljs-params"></span>):<span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-literal">null</span></span>)</span> { }

    <span class="hljs-comment">//that is the only entry point to create instance</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Email <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Email(<span class="hljs-keyword">value</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> _value;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidEmail</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
    {
        <span class="hljs-keyword">return</span> Regex.IsMatch(email, EMAIL_PATTERN);
    }

    <span class="hljs-comment">//adds more incapsulation rather than exposing Value property of string type</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">Email email</span>)</span>
    {
        <span class="hljs-keyword">return</span> email._value;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">Email</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
    {
        <span class="hljs-keyword">return</span> Create(email);
    }
}
</code></pre>
<p>You see how impossible it is to use a random string as an email now. When writing code for a core domain, design your objects in a way that is impossible to misuse by throwing blunt exceptions on unexpected input or creating always valid specialized types that control the correctness of your code at compilation time. Most of the relevant “clean code” principles are met automatically. We follow DRY (we don’t repeat email validation logic as we can use Email type in other Entities), SOLID (validation is put in one place, and one can inherit from Email to create more specialized versions of them such as a company or personal email, the email record can be used in sorting, filtering, lookups, etc.), KISS (it looks simple, clear, and readable), Fail Fast (it fails right at the boundary of instantiation right in the constructor), YAGNI (it is even lacking Value property as it is not defined in ubiquitous language, i.e. project requirements handed in by domain experts). Also, the object is immutable, which avoids sharing and handling issues in multi-threaded environments.</p>
<p>Here is “CustomerName” Value Object with even more constraints to its attributes</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">CustomerName</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MAX_NAME_LENGTH = <span class="hljs-number">100</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> NamePattern = <span class="hljs-string">@"^[A-Z][a-zA-Z]*$"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(firstName))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"First name cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(firstName));
        }

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(lastName))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Last name cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(lastName));
        }

        <span class="hljs-keyword">if</span> (firstName.Length &gt; MAX_NAME_LENGTH || lastName.Length &gt; MAX_NAME_LENGTH)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">$"First and last names cannot exceed <span class="hljs-subst">{MaxLength}</span> characters."</span>);
        }

        <span class="hljs-keyword">if</span> (!IsValidName(firstName) || !IsValidName(lastName))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Names must contain only letters and start with a capital letter."</span>);
        }

        FirstName = firstName;
        LastName = lastName;
    }

    <span class="hljs-comment">//default </span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"></span>):<span class="hljs-title">this</span>(<span class="hljs-params"><span class="hljs-literal">null</span>, <span class="hljs-literal">null</span></span>)</span> { }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> CustomerName <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName</span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> CustomerName(firstName, lastName);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{FirstName}</span> <span class="hljs-subst">{LastName}</span>"</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> name</span>)</span>
    {
        <span class="hljs-keyword">return</span> Regex.IsMatch(name, NamePattern);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">CustomerName customerName</span>)</span>
    {
        <span class="hljs-keyword">return</span> customerName.ToString();
    }

    <span class="hljs-comment">//adding a shortcut to create name from a single string</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">CustomerName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> fullName</span>)</span>
    {
        <span class="hljs-keyword">var</span> names = fullName.Split(<span class="hljs-string">' '</span>);
        <span class="hljs-keyword">if</span> (names.Length != <span class="hljs-number">2</span>)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Full name must contain exactly two parts: first name and last name."</span>);
        }
        <span class="hljs-keyword">return</span> Create(names[<span class="hljs-number">0</span>], names[<span class="hljs-number">1</span>]);
    }
}
</code></pre>
<p>It incorporates quite a few restrictions that might not be obvious till you start thinking of all the possible ways the object can be abused by client code. Domain experts may not be that detailed to tell that they need a cap on string length or that the system should not allow special characters in the names but that is your job as a developer to think through all those patterns leading to the combinatorial explosion of invalid or abusive states. You can also check with your stakeholders about all those additional restrictions and limitations and they will tell with 99% certainty that it is a great idea. Never fear to overdo that. It is better to weaken your grip later and remove a couple of constraints (exception throws) than debugging through issues or dealing with malicious hacker attacks exploiting oversites in your model.</p>
<p>Let’s move on to the home address of a customer. We know that at least in the first versions of our system it will be a US address. Let’s code for that and put as many limitations as we could possibly think of.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">USHomeAddress</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MaxStreetLength = <span class="hljs-number">100</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MaxCityLength = <span class="hljs-number">50</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MaxStateLength = <span class="hljs-number">2</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MaxZipCodeLength = <span class="hljs-number">10</span>;

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> StreetPattern = <span class="hljs-string">@"^[a-zA-Z0-9\s,'-]*$"</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> CityPattern = <span class="hljs-string">@"^[a-zA-Z\s,'-]*$"</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> StatePattern = <span class="hljs-string">@"^[A-Z]{2}$"</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> ZipCodePattern = <span class="hljs-string">@"^\d{5}(-\d{4})?$"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Street { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> City { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> State { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> ZipCode { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">USHomeAddress</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> street, <span class="hljs-keyword">string</span> city, <span class="hljs-keyword">string</span> state, <span class="hljs-keyword">string</span> zipCode</span>)</span>
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(street))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Street cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(street));
        }

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(city))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"City cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(city));
        }

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(state))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"State cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(state));
        }

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(zipCode))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"ZipCode cannot be null or whitespace."</span>, <span class="hljs-keyword">nameof</span>(zipCode));
        }

        <span class="hljs-keyword">if</span> (street.Length &gt; MaxStreetLength || city.Length &gt; MaxCityLength ||
            state.Length &gt; MaxStateLength || zipCode.Length &gt; MaxZipCodeLength)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Address components exceed maximum length."</span>);
        }

        <span class="hljs-keyword">if</span> (!IsValidComponent(street, StreetPattern) || !IsValidComponent(city, CityPattern) ||
            !IsValidComponent(state, StatePattern) || !IsValidComponent(zipCode, ZipCodePattern))
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Invalid address format."</span>);
        }

        Street = street;
        City = city;
        State = state;
        ZipCode = zipCode;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> USHomeAddress <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> street, <span class="hljs-keyword">string</span> city, <span class="hljs-keyword">string</span> state, <span class="hljs-keyword">string</span> zipCode</span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> USHomeAddress(street, city, state, zipCode);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{Street}</span>, <span class="hljs-subst">{City}</span>, <span class="hljs-subst">{State}</span> <span class="hljs-subst">{ZipCode}</span>"</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsValidComponent</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> component, <span class="hljs-keyword">string</span> pattern</span>)</span>
    {
        <span class="hljs-keyword">return</span> Regex.IsMatch(component, pattern);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">string</span>(<span class="hljs-params">USHomeAddress homeAddress</span>)</span>
    {
        <span class="hljs-keyword">return</span> homeAddress.ToString();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">explicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">HomeAddress</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> address</span>)</span>
    {
        <span class="hljs-keyword">var</span> parts = address.Split(<span class="hljs-string">','</span>);
        <span class="hljs-keyword">if</span> (parts.Length &lt; <span class="hljs-number">3</span>)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Address must contain at least street, city, and state."</span>);
        }

        <span class="hljs-keyword">var</span> street = parts[<span class="hljs-number">0</span>].Trim();
        <span class="hljs-keyword">var</span> city = parts[<span class="hljs-number">1</span>].Trim();
        <span class="hljs-keyword">var</span> stateZip = parts[<span class="hljs-number">2</span>].Trim().Split(<span class="hljs-string">' '</span>);

        <span class="hljs-keyword">if</span> (stateZip.Length != <span class="hljs-number">2</span>)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Address must contain a valid state and zip code."</span>);
        }

        <span class="hljs-keyword">var</span> state = stateZip[<span class="hljs-number">0</span>].Trim();
        <span class="hljs-keyword">var</span> zipCode = stateZip[<span class="hljs-number">1</span>].Trim();

        <span class="hljs-keyword">return</span> Create(street, city, state, zipCode);
    }
}
</code></pre>
<p>Do you see how bloated the Customer entity would be if we put all those validation rules there? The example is still contrived as there is always much more than you can regulate or add here to represent real-life use cases.</p>
<p>Next and the last one is DateOfBirth</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">DateOfBirth</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MinAge = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> MaxAge = <span class="hljs-number">150</span>;

    <span class="hljs-keyword">public</span> DateTime Value { <span class="hljs-keyword">get</span>; }

    <span class="hljs-comment">//default should throw exception</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">DateOfBirth</span>(<span class="hljs-params"></span>):<span class="hljs-title">this</span>(<span class="hljs-params">DateTime.MinValue, DateTime.MinValue</span>)</span> { }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">DateOfBirth</span>(<span class="hljs-params">DateTime <span class="hljs-keyword">value</span>, DateTime currentDate</span>)</span>
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">value</span> == DateTime.MinValue)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Date of birth cannot be the minimum date value."</span>, <span class="hljs-keyword">nameof</span>(<span class="hljs-keyword">value</span>));
        }

        <span class="hljs-keyword">var</span> age = CalculateAge(<span class="hljs-keyword">value</span>, currentDate);
        <span class="hljs-keyword">if</span> (age &lt; MinAge || age &gt; MaxAge)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">$"Age must be between <span class="hljs-subst">{MinAge}</span> and <span class="hljs-subst">{MaxAge}</span> years old."</span>, <span class="hljs-keyword">nameof</span>(<span class="hljs-keyword">value</span>));
        }

        Value = <span class="hljs-keyword">value</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> DateOfBirth <span class="hljs-title">Create</span>(<span class="hljs-params">DateTime <span class="hljs-keyword">value</span>, DateTime currentDate</span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DateOfBirth(<span class="hljs-keyword">value</span>, currentDate.Date);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> Value.ToString(<span class="hljs-string">"yyyy-MM-dd"</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">uint</span> <span class="hljs-title">GetAge</span>(<span class="hljs-params">DateTime currentDate</span>)</span> =&gt; CalculateAge(Value, currentDate);

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">CalculateAge</span>(<span class="hljs-params">DateTime dateOfBirth, DateTime currentDate</span>)</span>
    {
        <span class="hljs-keyword">var</span> age = currentDate.Year - dateOfBirth.Year;
        <span class="hljs-keyword">if</span> (dateOfBirth.Date &gt; currentDate.AddYears(-age)) age--;
        <span class="hljs-keyword">return</span> age;
    }
}
</code></pre>
<p>This code ensures not only the correctness of the date but also that it belongs to a person/customer who is allowed to interact with the system. Please make a note that it does not call DateTime.Today or DateTime.Now that would make the implementation “impure” in a functional sense since it would have implicit “external“ dependency on the system clock and that is something to avoid. Not only does it make the unit test harder but also leads to unpredictable output as it changes over time and I don’t even mention all those complications with time zones.</p>
<p>That completes the list of Value Objects and I will get back to the Customer Entity in the next article.</p>
]]></content:encoded></item><item><title><![CDATA[Enhancing Core Domain Code with Simple Updates]]></title><description><![CDATA[Let me illustrate ideas laid out in previous articles with examples and show the actual code following all the promoted principles. You will see how easy it is to build complex systems by adhering to simple rules.
Let’s pick up a domain. I chose spec...]]></description><link>https://dmitrydezuk.com/simple-code-improvement-to-fight-entropy</link><guid isPermaLink="true">https://dmitrydezuk.com/simple-code-improvement-to-fight-entropy</guid><category><![CDATA[primitive-obsession]]></category><category><![CDATA[code-improvement]]></category><category><![CDATA[oop]]></category><dc:creator><![CDATA[Dmitry Dezuk (Dezhurnyuk)]]></dc:creator><pubDate>Tue, 26 Nov 2024 05:34:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/f1H7QezOwwQ/upload/3b382fc213c6b0b19955ef8926d21f0f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let me illustrate ideas laid out in previous articles with examples and show the actual code following all the promoted principles. You will see how easy it is to build complex systems by adhering to simple rules.</p>
<p>Let’s pick up a domain. I chose specifically the one I have never worked in such as Insurance Management System. You are tasked with a system maintaining the business processes of an insurance company working with real estate. We will deal with hundreds of requirements but let’s start with the easiest ones such as customer registration. That simple act of registration usually includes many rules and requirements but I will list a few basic ones.</p>
<ul>
<li><p>The customer should have a unique ID generated by the System that can be used to reference this customer in the URL or do a lookup when a support agent is trying to pull the customer’s record. There are some requirements for that ID. It is a string with a length of 10 that contains alphanumeric characters.</p>
</li>
<li><p>The customer should also have a valid email, First and Last Name, and Date of Birth</p>
</li>
<li><p>We can pick a dozen other attributes for instance Primary Address, Insured Property Address, Employer, Salary, etc but we will keep it simple for now.</p>
</li>
</ul>
<p>The Naive approach following OOP is to create a Customer with read-only properties and a constructor like below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Customer</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> ID { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>;  }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Address { <span class="hljs-keyword">get</span>;  }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Email { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> DateTime DateOfBirth { <span class="hljs-keyword">get</span>; }


    <span class="hljs-keyword">const</span> <span class="hljs-keyword">uint</span> REASONABLE_MAX_AGE = <span class="hljs-number">120</span>;
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> AVERAGE_DAYS_IN_A_YEAR = <span class="hljs-number">365.2425f</span>;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Customer</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> id, <span class="hljs-keyword">string</span> firstName, <span class="hljs-keyword">string</span> lastName, <span class="hljs-keyword">string</span> address, <span class="hljs-keyword">string</span> email, DateTime dateOfBirth</span>)</span>
    {
        <span class="hljs-keyword">this</span>.ID = id ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(id));
        <span class="hljs-keyword">this</span>.FirstName = firstName ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(firstName));
        <span class="hljs-keyword">this</span>.LastName = lastName ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(lastName));
        <span class="hljs-keyword">this</span>.Address = address ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(address));
        <span class="hljs-keyword">this</span>.Email = email ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(email));

        <span class="hljs-keyword">var</span> years = (DateTime.Now - dateOfBirth).TotalDays / AVERAGE_DAYS_IN_A_YEAR;
        <span class="hljs-keyword">if</span>(years&lt;<span class="hljs-number">0</span> || years&gt; REASONABLE_MAX_AGE)
        {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Age is not reasonable"</span>);
        }

        <span class="hljs-keyword">this</span>.DateOfBirth = dateOfBirth;
    }
}
</code></pre>
<p>OOP is happy as the state is encapsulated, and the constructor gives some protection against invalid input. You cannot create an empty Customer or a customer without any of the required fields. But this is exactly where the snowball of possible issues emerges.</p>
<p>A good domain model would allow for interaction with a client in a legit way and prohibit any possible deviation from the modeled restrictions (aka invariants). What can we do here?</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> weirdValidCustomer = <span class="hljs-keyword">new</span> Customer(
    <span class="hljs-string">"!special symbol not allowed"</span>,
    <span class="hljs-string">"  start with empty characters?"</span>,
    <span class="hljs-string">"32 last name starts with a number"</span>,
    <span class="hljs-string">"Is PO Box OK?"</span>,
    <span class="hljs-string">"---I don't have one--"</span>,
    DateTime.Today.AddYears(<span class="hljs-number">30</span>).Add(TimeSpan.FromMinutes(<span class="hljs-number">20</span>)));<span class="hljs-comment">// we know the time of birth?</span>
</code></pre>
<p>You can bog down such a system by passing a string from the full content of “War and Peace” by Tolstoy. Millions of invalid inputs and their combinations can be passed resulting in a combinatorial explosion of possible buggy states.</p>
<p>Light restrictions cause enormous possibilities for exploitation (by hackers), confusion, or wasted debugging hours. Entropy starts thriving in such conditions inevitably leading to code degradation and chaos.</p>
<p>What you should do is not go to another extreme and start adding a huge number of checks to the customer constructor. But that would still be better than not having any checks. Rather think of yourself as a sculptor, focus on one thing at a time, and cut all undefined, unnecessary, irrelevant. Do it piece by piece, step by step until you face a wall of the underlying requirements that you can’t go any further.</p>
<p>Being a sculptor when modeling the domain means creating new abstractions and delegating all the logic restricting our states (i.e. reducing our entropy) to those abstractions till you are left with very few states just necessary to implement the given use cases from a spec.</p>
<p>The reality is that the subset of valid unique IDs is much smaller when a manifold of all strings. The same goes for strings, names, and emails where the number of good instances of those strings is incomparably less than the number of all the strings the string type allows for. We need an abstraction expressing those valid subsets. We can create and name them accordingly without even going deeper into how they should properly function. We will get to them later when we zoom in our focus on them.</p>
<p>It is so easy just to create new classes where we pretend all rules are already enforced and this is a very efficient move to remove all uncertainty and proliferation of possible invalid states. Here we create new classes CustomerId, CustomerName, Address, Email, and DateOfBirth with all needed to maintain their invariants, i.e. rules defined by the spec explicitly or implicitly. The customer has very little left to enforce rules in its internal constituents. It does nothing but ensure all the required constituents are supplied. That makes it very robust and leaves no opportunity for incorrect instantiation of a Customer object.</p>
<pre><code class="lang-csharp">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">CustomerId</span> {…}
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">CustomerName</span> {…}
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">HomeAddress</span> {…}
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Email</span> {…}
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DateOfBirth</span> {…}

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Customer</span>
    {
        <span class="hljs-keyword">public</span> CustomerID ID { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> CustomerName Name { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> HomeAddress Address { <span class="hljs-keyword">get</span>;  }
        <span class="hljs-keyword">public</span> Email Email { <span class="hljs-keyword">get</span>; }
        <span class="hljs-keyword">public</span> DateOfBirth DateOfBirth { <span class="hljs-keyword">get</span>; }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Customer</span>(<span class="hljs-params">CustomerID id, CustomerName name, Address address, Email email, DateOfBirth dateOfBirth</span>)</span>
        {
            <span class="hljs-keyword">this</span>.ID = id ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(id));
            <span class="hljs-keyword">this</span>.Name = name ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(name));
            <span class="hljs-keyword">this</span>.Address = address ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(address));
            <span class="hljs-keyword">this</span>.Email = email ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(email));
            <span class="hljs-keyword">this</span>.DateOfBirth = dateOfBirth ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(email));
        }
    }
</code></pre>
<p>Our code became simpler, much simpler and it eliminates many more invalid states than the first version. It is just verifying the fact that the parameter is supplied and it knows if it is supplied it cannot be in an invalid state. That technique combats a well-known antipattern known as Primitive Obsession where Domain classes use language primitive types (string, int, DateTime, float, etc.) rather than specialized types representing Domain concepts and that makes it hard for domain objects to ensure the correctness of its parts at all times. Think of it. If you use email or address as properties in quite a few classes you will have to re-run the same guarding checks over and over. It violates the DRY (don’t repeat yourself) rule. Even if you extract the guarding logic and put it in helper methods, it would still be easy to make a mistake. If you assign a primitive type value to the properties in many places it is just a matter of time when you forget to use the helper method to confirm validity.</p>
<p>Specialized types build that fortress around your preserved state that makes your model always valid. There is one characteristic of such types that simplifies their implementation and utilization. That is immutability. If specialized type states never change from their creation, we can put all the heavy guarding logic in their constructor. There won’t be any other manipulators or methods in them as they are immutable by definition. If we need a change in their state, we just create a new object with the same constructor and swap the old object with the new one.</p>
<p>Such types are called “value objects” because they are defined by nothing but their values and they don’t have an identity (unique features outside of the set of values of their properties). You can replace one “value object” with another as long as it the have same values in their data frames.</p>
<p>One way of modeling value objects is to C# structs as they have some intrinsic properties of value objects. Structs are passed by value, they are compared by internal state by default. You won’t get NRE (NullReferenceException) using them so you don’t need that guarding check for Null. But the drawback is that the struct always has a parameterless constructor so you can always create a zero object and if it is not part of a valid subset, you will have to burden the caller with additional code checking against using zero value. I would prefer using a class if there is no performance penalty here or if I have the luxury of using .net core, “record” would be a better option to model value objects.</p>
]]></content:encoded></item><item><title><![CDATA[Safeguarding the Core Domain]]></title><description><![CDATA[I will keep talking about the core domain where all the rules are strictly enforced and any violation results in an exception. The core domain can be seen as an electric toy car without a cover and all the shields protecting it from the external worl...]]></description><link>https://dmitrydezuk.com/safeguarding-the-core-domain</link><guid isPermaLink="true">https://dmitrydezuk.com/safeguarding-the-core-domain</guid><category><![CDATA[Validation]]></category><category><![CDATA[error handling]]></category><category><![CDATA[application layer]]></category><category><![CDATA[Domain-Driven Design (DDD)]]></category><dc:creator><![CDATA[Dmitry Dezuk (Dezhurnyuk)]]></dc:creator><pubDate>Tue, 19 Nov 2024 06:01:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/YT0AdCJ-iHY/upload/127488d34e6a294be90966718f6eaa9f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I will keep talking about the core domain where all the rules are strictly enforced and any violation results in an exception. The core domain can be seen as an electric toy car without a cover and all the shields protecting it from the external world.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731996264181/9678fb8f-ebb5-4db1-a328-d3cfd37219fe.jpeg" alt class="image--center mx-auto" /></p>
<p>You will see all the wires are dangling and electric elements sticking out. It is not meant to be exposed to the external world without protection. The whole idea is to minimize any deviation from a predefined and preferably single way to operate. If the rules are violated by you pulling the wires, submerging the car into the water, or applying power to the wrong terminal, the circuit should detect and fail fast with fuses going off. It should break and stop the entire mechanism from going into undocumented conditions. Thus it does not cause damage, hurt, or burn anyone. The cost of a chain of errors is high so it is cheaper to self-destruct. That pretty much applies to software as we model reality.</p>
<p>What if we don’t fail fast in the core domain and allow invalid arguments in a method performing business operations? The consequences are vast and violating business requirements could be the less serious problem. If methods preconditions or object invariants are not properly checked, any bug or malicious call exploiting this deficiency can corrupt or compromise the database, send unplanned emails, shut down production processes, or exhaust server resources. The core domain has no right to fall short of maintaining its invariants and checking methods pre and post-conditions. And it is the only thing it should do, i.e., defend against unauthorized use.</p>
<p>With all that the domain entities are complex enough and it is unfair to ask them for anything else. It should do the most straightforward thing: to fail hard throwing brief info of what is violated and not offer a second chance for corrected input. Like a sculptor making a statue cuts off all unnecessary parts, so does the developer create the domain model (the heart of the system) by limiting all possible states and interactions with users or other systems according to the agreed requirements from stakeholders. That is the best way to tame accidental complexity.</p>
<p>But how to protect our modeled domain from invalid input in a client-friendly way so that the system does not escalate and throw things at you on any hiccup? Clients would be reluctant to play that shooter game and start from the beginning on the first shot. Meet the validation. It is that toy cover or a shield protecting our work from unintended interaction nicely so our device does not blow up on the first mistake. The point is it is not part of the core domain that just throws straightforward errors. The validation allows any input, tries to make sense of it, and decides whether to allow it through or do something else but not throw exceptions or terminate the process. It has many graceful options such as showing a nice message on how to remediate, highlighting your typing in a text box, redirecting you to support chat, or anything else appealing from a usability perspective.</p>
<p>Validation is typically sitting in the Application layer as part of the application facade defending internals from risky interactions. The core domain can be a helper here though. It can provide shared helper methods to check certain conditions in a “functional“ way (i.e. without creating or maintaining a state) or allow for a “specification” pattern. The latter is quite popular as it allows to reuse of many domain checks in a structured way. That generally helps be DRY. The checks are the same but reactions to the errors are different. Domain model immediately throws while validation assumes bad input and tries to suggest a workaround.</p>
<p>Still, I think that is enough for the article and I will toss some examples in the next one.</p>
]]></content:encoded></item><item><title><![CDATA[Conquering Software Complexity with Simple Tactics]]></title><description><![CDATA[We all write code to solve people’s problems, but we rarely think that every line or character of code adds to another class of problems that originate in our software. Before you know it, your solution to the original problem eventually becomes anot...]]></description><link>https://dmitrydezuk.com/conquering-software-complexity-with-simple-tactics</link><guid isPermaLink="true">https://dmitrydezuk.com/conquering-software-complexity-with-simple-tactics</guid><dc:creator><![CDATA[Dmitry Dezuk (Dezhurnyuk)]]></dc:creator><pubDate>Mon, 11 Nov 2024 06:54:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/z0nVqfrOqWA/upload/87406700c0f48d793ed8d5547bf37f44.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We all write code to solve people’s problems, but we rarely think that every line or character of code adds to another class of problems that originate in our software. Before you know it, your solution to the original problem eventually becomes another problem with much more complexity. Do you know that joke about regular expressions that says that when you have a problem at hand and solve it with a regular expression, you are now dealing with two issues? You face consequences only when you neglect or don’t recognize the existence of that threat though.</p>
<p>Unexperienced developers may start feeling that monster in the room without fully recognizing it. They are given a seemingly easy task in the beginning that sounds simple but all of a sudden something gets in their way and deters from their direct responsibility to write code. They are already feeling the breath of that monster but they don’t know what that is. They initially gave optimistic promises and ETAs and now what? They just can’t deliver because they are facing a wall, and that wall has a name. Meet “accidental complexity” which is an added complexity of the original problem and it stems from all the tools, programming languages, and techniques we are using to model a solution in software. The domain itself is usually complex enough and it features “essential complexity”. Stakeholders summon us IT guys to help because their complexity can’t fit one head (or their team heads) anymore. They delegate all that complexity to us and we truly need to re-delegate that to software nicely otherwise we and our support team become an easy target for frustrated customers. To be effective developers need to know how to tame accidental complexity in their stack so that they have more time to work on more relevant tasks. Ideally, they should spend more time on clarifying problem domain requirements, and nuances, and how to model them adequately with programming language bricks as this is where the value is. Corporate stakeholders focus on real business goals and decisions and they don’t care how your project is organized, how much technical debt it obtained, what language you used, whether you rely on a garbage collector, and so on. You are to manage all that risk to be successful and there are ways and approaches to do that.</p>
<p>The software serves a particular purpose and every piece of it is the source of so-called entropy and combinatorial explosion. What is entropy? It is the natural tendency of a system to degrade or transition from an organized structure into a chaotic matter if there is no constant effort to get the order back. The same is happening with software design. Code entropy is the gradual degradation of code quality over time. Every code is subject to it and eventually becomes increasingly complex, hard to understand, and more prone to errors. That stems from rushed patchy development, unclear contradicting requirements, technical debt accumulating, and a lack of consistent refactoring. Eventually, it leads to project delays, increased costs, and failures. Software becomes so fragile that it operates predominately in undocumented and unforeseen states and code paths producing noisy outputs, crashing, throwing errors, causing data corruption, and many other undesirable outcomes.</p>
<p>Another enemy is the combinatorial explosion. It inevitably leads us to an explosion of edge cases that practically can’t be 100% covered with automated tests. For each new parameter, input field, or feature, the number of potential interactions and code paths grows exponentially, not linearly. Testing every possible combination becomes practically unachievable, especially as the application scales. With the growth of variables, states, and lines of code, some combinations of them are never foreseen by the developer and often manifest as bugs. They are particularly troublesome because they don’t show up during typical testing environments but can emerge in production under narrow conditions, causing reliability issues and complicating debugging.</p>
<p>The ideal situation is when a function or an action is served without a single line of code. It is useful to consider whether there is a good reason to go with software in the first place. Maybe there are alternative ways to solve a problem efficiently without coding. Many “no code” tools can be the way to proceed. Like in martial arts, the best way to win a battle is to avoid it. Try to solve the problem without software, but if the software is unavoidable, you have to strike first, and here are the martial arts to win this battle.</p>
<p>If we need software, you have to be sure your solution is minimal and simple enough for the given requirements. I would even consider whether having just one button is good enough. Because if you add a text box or any additional input, you have to start considering entropy and the explosion of internal states.</p>
<p>The most common techniques for me to combat all those issues are Object Oriented Programming (OOP) coupled with Domain Driven Design (DDD) and the “Fail Fast” principle. Why, because they are at the heart of fighting complexity, reducing possible states, good code quality, and readability. Of course, other principles of “clean“ code still apply but the two above are simple and effective weapons against complexity.</p>
<p>It is very important to employ those principles on day one to avoid facing the consequences of accidental complexity. You should fight it with all your force and power. Of course, they are huge topics by themselves, but I will cover them piece by piece in practical examples. Here are some excerpts.</p>
<ul>
<li><p>Start with your domain model first</p>
</li>
<li><p>don’t think about logging, monitoring, performance, garbage collection, exception handling, or interacting with databases, emails, or external systems. You will get back to all that later when your domain model is satisfactorily mature.</p>
</li>
<li><p>Don’t let bad input at all cost. Check every public method parameter fiercely and throw and throw exceptions if their values are not from the expected range allowing only a single happy path.</p>
</li>
<li><p>Remove all possible methods and code paths that are not executed through declared requirements. We are not writing a flexible library here for numerous use cases.</p>
</li>
<li><p>Try to make all the method parameters (including return values) domain objects.</p>
</li>
<li><p>Throw errors mercilessly on every unclear or undocumented condition. Never try to fix, or implement a workaround for invalid input in core domain level. That is not happening here. Every step left or right off the trail causes harsh exceptions thrown from the core domain realm.</p>
</li>
<li><p>You should never allow any further execution at first traces of something going south. Don’t think about the client and how bad it would feel to crash. It is not your business here.</p>
</li>
<li><p>All properties should be part of the domain. No nulls or semi-valid objects are tolerated.</p>
</li>
<li><p>Use test-driven approach writing test first to overcome paralysis to start coding</p>
</li>
<li><p>Use no external or out-of-process impure dependencies in your core domain. Everything should be written there as if all objects are available in fast memory.</p>
</li>
</ul>
<p>Rejoice in your core domain. You are the king there. Only in the core domain, do you have the luxury to fully fight essential complexity and not to deal with accidental complexity, and combat entropy with maximum chances to win. That is why you need to push as much code as possible in there and push a very thin layer to the boundary of your application where all the orchestration and integration with external actors takes place. You have all the tactical tools at your disposal to organize your domain following the golden standards of efficiency and minimalism. This is where you think of OOP, SOLID, singletons, visitors, factories, and so on.</p>
<p>Unlike the core domain, you are limited in all other layers.</p>
<ul>
<li><p>Utility methods should be flexible to tolerate garbage input</p>
</li>
<li><p>The validation layer shouldn’t throw but rather inform about problems and not use exception throwing at all.</p>
</li>
<li><p>Logging should allow a gazillion parameters to log every possible combination of parameters of various types. Otherwise, the tool won’t be convenient.</p>
</li>
<li><p>You don’t create a diagram of classes to read/write from/to a file or a database.</p>
</li>
<li><p>Your view models or controllers should look more like a transaction script orchestrating and stitching everything else together.</p>
</li>
</ul>
<p>Automated testing with DDD is bliss as you can achieve 100% coverage here without engaging test doubles, mocks, or spies as the core domain does not need much reliance on external dependencies.</p>
<p>Practicing those recommendations is doing a huge favor to your future self and everyone taking ownership of the code after you. You will see that following just a few primary principles from above lets you build software fast and almost flawlessly from day one.</p>
<p>Let’s consider a more or less realistic use case. Say you are developing a hotel booking system and you get the most basic requirement first. You need to implement a feature to register as a customer.</p>
<p>See the code in the next blog.</p>
<p>Stay connected!</p>
]]></content:encoded></item><item><title><![CDATA[Software Is Hard but Simple]]></title><description><![CDATA[There are many approaches to building software such as test first, domain first, database first, event sourcing first, and use case first. On top of that, there are different software patterns such as Ports and Adapters, Onion, Hexagon, Active Record...]]></description><link>https://dmitrydezuk.com/software-is-hard-but-simple</link><guid isPermaLink="true">https://dmitrydezuk.com/software-is-hard-but-simple</guid><category><![CDATA[architecture]]></category><category><![CDATA[patterns]]></category><dc:creator><![CDATA[Dmitry Dezuk (Dezhurnyuk)]]></dc:creator><pubDate>Fri, 08 Nov 2024 05:32:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/KMn4VEeEPR8/upload/a8b7d59b9ac192b05f57e7351bf5801a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are many approaches to building software such as test first, domain first, database first, event sourcing first, and use case first. On top of that, there are different software patterns such as Ports and Adapters, Onion, Hexagon, Active Record, Transaction Script, 3 or N Tiers, Domain Models, CQRS, MVC, Event Sourcing, Microservices, Monoliths, and a combination of them. These all describe the strategic design while there are many others that talk about <a target="_blank" href="https://refactoring.guru/design-patterns/csharp">tactics</a>. Besides, well-known coding patterns patterns (Singleton, Bidge, Factory, Builder, and so on), that category also includes OOP, SOLID, and other named principals such as "fail fast", KISS, DRY, YAGNI, Composition Over Inheritance, the Law of Demeter, etc.</p>
<p>These are all good for theorizing, training your intelligence, passing exams, and showing off with coworkers. However, the usefulness of that knowledge is pretty limited when it comes to small or medium-sized projects with a couple of devs. The enterprise world is a much better fit for all those architectural guidelines. And to be honest it is hard to reconcile them even there as they often advise following contradicting approaches, leading to unavoidable analysis paralyses or poor code. Code that is hard to work with, to say the least. That becomes so confusing that even experienced senior developers get puzzled and make suboptimal decisions.</p>
<p>All those thick classical books and articles still leave simple daily questions unanswered. Here are some that sound pretty naïve.</p>
<ul>
<li><p>Where to do the validation? Is that in this class or the one that contains it? How should we react to the result of validation? Should we throw or return a success flag with an error message? What about Debug.Assert checks? Should we repeat similar validation on every level of a call stack or use DRY and put it in the lower-level method?</p>
</li>
<li><p>Should we intercept exceptions, where, and how?</p>
</li>
<li><p>Is it ok to create an object that is not valid till we call a couple of initializing methods or set a couple of properties?</p>
</li>
<li><p>Do we check all or some method parameters? How should we react to invalid parameters? Should we try to fix invalid input or throw an exception? Should the exception contain as many details as it can, or just give a brief and broad description? Where do we catch exceptions?</p>
</li>
<li><p>Is it ok to copy and paste logic if it is similar but different or should we always go DRY?</p>
</li>
<li><p>How many lines are ok to be in a method?</p>
</li>
<li><p>Can we return null here or there or should we never return null and rather use a null object pattern?</p>
</li>
<li><p>There are multiple ways to organize a diagram of classes for a given problem. What diagram is better? How to organize a solution in Visual Studio?</p>
</li>
</ul>
<p>I am not going to say that I have a single answer to these questions as the real answer is "it depends". But I come to writing code from a different angle and that lets me answer those questions naturally. That angle is a manifestation of broader ideas related to harmony, entropy, stability, probability, degree of freedom, human perception, and philosophy that are the essence of many interesting productivity metrics in code and even in your life.</p>
<p>Stay tuned!</p>
]]></content:encoded></item></channel></rss>