<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dirk Groot</title>
    <description>The latest articles on DEV Community by Dirk Groot (@dirkgroot).</description>
    <link>https://dev.to/dirkgroot</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F902071%2Ff4777a1b-bbdf-4be5-abdd-d061fdda1596.png</url>
      <title>DEV Community: Dirk Groot</title>
      <link>https://dev.to/dirkgroot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dirkgroot"/>
    <language>en</language>
    <item>
      <title>Designing with Types #03: Safety and Intent</title>
      <dc:creator>Dirk Groot</dc:creator>
      <pubDate>Wed, 09 Jul 2025 11:33:55 +0000</pubDate>
      <link>https://dev.to/dirkgroot/designing-with-types-03-safety-and-intent-5c1o</link>
      <guid>https://dev.to/dirkgroot/designing-with-types-03-safety-and-intent-5c1o</guid>
      <description>&lt;p&gt;&lt;em&gt;In the previous installment of this series, we concluded that we can write code that is &lt;strong&gt;safe&lt;/strong&gt; and that &lt;strong&gt;reveals intent&lt;/strong&gt; by making illegal states unrepresentable and by encapsulating state changes. Today, we'll try to come up with proper definitions of safety and revealing intent. Also, we'll take a look at more examples of applying these principles in practice.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety
&lt;/h2&gt;

&lt;p&gt;What does &lt;strong&gt;safety&lt;/strong&gt; mean in the context of software development in general and designing types in particular? Here's my attempt at a "formal" definition:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We consider an API to be &lt;strong&gt;safe&lt;/strong&gt; if and only if &lt;strong&gt;everything that's public&lt;/strong&gt; can be used &lt;strong&gt;freely&lt;/strong&gt;, without the risk of introducing an illegal state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With this definition I'm trying to stay as language agnostic as possible. Reason for this is that the concept of safety can be applied (to some extent) to every programming language and every paradigm.&lt;/p&gt;

&lt;p&gt;In this series, we're focusing mainly on strong-typed, object-oriented languages like Kotlin and Java. In those languages &lt;strong&gt;everything that's public&lt;/strong&gt; is either a public class, a public method, a public field or (in Kotlin) a public top-level function. To be more specific, this definition applies to everything that causes a state change. Things like read-only fields or pure functions are always safe according to this definition, because those things don't modify anything.&lt;/p&gt;

&lt;p&gt;So what does this mean in practice for things that do modify something? Let's look at each case individually.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public mutable fields&lt;/strong&gt; can only be freely mutable if there exist &lt;strong&gt;zero&lt;/strong&gt; situations where a possible value of such a field can be considered invalid. If this is not the case, a field cannot be publicly mutable according to this definition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public methods and top-level functions&lt;/strong&gt; that change state may &lt;strong&gt;never&lt;/strong&gt; change that state to something invalid. According to this definition, such methods or functions must either:

&lt;ul&gt;
&lt;li&gt;change the state to something valid;&lt;/li&gt;
&lt;li&gt;throw an exception;&lt;/li&gt;
&lt;li&gt;do nothing;&lt;/li&gt;
&lt;li&gt;or return a value that indicates a failure.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Revealing intent
&lt;/h2&gt;

&lt;p&gt;Code is written once, but it's read many times. Therefore we want the code to be easy to understand. For this article series, I'll use a pretty narrow definition of revealing intent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code that reveals intent is &lt;strong&gt;explicit&lt;/strong&gt; about what it &lt;strong&gt;does&lt;/strong&gt;, what it &lt;strong&gt;expects&lt;/strong&gt; and what it &lt;strong&gt;results&lt;/strong&gt; in.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't think this definition needs much of an explanation. It doesn't just apply to code. Take a typical &lt;a href="https://en.wikipedia.org/wiki/Use_case#Examples" rel="noopener noreferrer"&gt;use case template&lt;/a&gt; for example. Such a template will typically have room for us to document the goal, the preconditions and the postconditions. In order to properly understand a piece of functionality, this is what we typically need to know, regardless of whether we're reading code or a use case.&lt;/p&gt;

&lt;p&gt;This series is about designing with types, so here we'll be focusing on revealing intent using types. As we'll see, types are a very powerful way of revealing intent. This is because types allow us to give important concepts a proper*&lt;em&gt;name&lt;/em&gt;* and because by defining types, we can leverage the &lt;strong&gt;power of the compiler&lt;/strong&gt; to enforce intended usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;With the principles from the &lt;a href="//../02-pitfalls-and-practices"&gt;previous article&lt;/a&gt; in mind, let's look at some practical examples. Every example will start by reviewing a piece of code, evaluating whether it's safe and if it reveals intent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Properties
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Code review
&lt;/h4&gt;

&lt;p&gt;Let's look at this &lt;code&gt;Customer&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;emailAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no context in this example, but it's not hard to see that this code probably isn't safe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;id&lt;/code&gt; field is publicly mutable, but it's generally not recommended to change the primary key of an entity.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;id&lt;/code&gt; field is a plain &lt;code&gt;Long&lt;/code&gt;, but negative values are probably not allowed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;emailAddress&lt;/code&gt; fields are plain &lt;code&gt;String&lt;/code&gt;s, but it's highly unlikely that every possible &lt;code&gt;String&lt;/code&gt; is a valid value for these fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's assume that in this case, it's safe that &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;emailAddress&lt;/code&gt; are publicly mutable.&lt;/p&gt;

&lt;p&gt;The code does reveal intent to some degree, but it's the bare minimum. It uses helpful names for the properties, but the property types don't reveal any intent at all. The constraints for the &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;emailAddress&lt;/code&gt; are not obvious and it's not clear where we can find those constraints.&lt;/p&gt;

&lt;h4&gt;
  
  
  Refactoring
&lt;/h4&gt;

&lt;p&gt;The first and most obvious thing we can do to improve the safety of this code, is to make &lt;code&gt;id&lt;/code&gt; immutable. By doing this, we eliminate one way to introduce an illegal state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;emailAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can make &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;emailAddress&lt;/code&gt; safe by introducing &lt;a href="//../02-pitfalls-and-practices#value-object-pattern"&gt;Value Objects&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CustomerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;emailAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;CustomerName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isValidEmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// let's pretend we have this function available somewhere 😉&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like in the previous article, we can see the power of the Value Object pattern. With one refactoring, we made two improvements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The code is now safe. There is simply no way we can write compiling code that introduces an illegal state. The code will either not compile or we'll get an exception for trying to introduce an illegal state. We cannot accidentally swap &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;emailAddress&lt;/code&gt;, because that code won't compile:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name@example.com"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;emailAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CustomerName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John Johnsson"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;We cannot give &lt;code&gt;id&lt;/code&gt; an invalid value, because that will throw an exception:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The code reveals intent much more clearly. The constructor arguments of &lt;code&gt;Customer&lt;/code&gt; all have a type with a proper name, which makes it obvious that acceptable values for those arguments belong to a certain domain. It's also easy to find the constraints for &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;emailAddress&lt;/code&gt;. We simply use our IDE to navigate to the type definitions of the corresponding Value Objects and we'll have all the information we need.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Simple business rules
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Code review
&lt;/h4&gt;

&lt;p&gt;Here's an &lt;code&gt;Order&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;paymentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PaymentID&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a service that makes sure that orders that have not been payed cannot be completed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;completeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paymentId&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;COMPLETED&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Order&lt;/code&gt; class uses Value Objects and &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;customerId&lt;/code&gt; are immutable, so it's pretty safe, except that&lt;code&gt;status&lt;/code&gt; is publicly mutable. We can easily create an order with status &lt;code&gt;OrderStatus.COMPLETED&lt;/code&gt; while it has not been payed. We just circumvent &lt;code&gt;OrderService&lt;/code&gt; and call the &lt;code&gt;Order&lt;/code&gt; constructor directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This code compiles and throws no exceptions&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;COMPLETED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above communicates intent via &lt;code&gt;OrderService&lt;/code&gt;, but &lt;code&gt;Order&lt;/code&gt; and &lt;code&gt;OrderService&lt;/code&gt; are different classes which are usually defined in different source files, which could belong to different packages. It would be clearer if the data and the business rule were close together.&lt;/p&gt;

&lt;h4&gt;
  
  
  Refactoring
&lt;/h4&gt;

&lt;p&gt;We can improve this by introducing more encapsulation. We do this by merging &lt;code&gt;OrderService&lt;/code&gt; and &lt;code&gt;Order&lt;/code&gt;, so we can make the setters for &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;paymentId&lt;/code&gt; private:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PENDING&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;paymentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PaymentID&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentId&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;COMPLETED&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;IllegalStateException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cannot complete an order that has not been payed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this refactoring we made multiple improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The code is now safe, because all state changes changes are encapsulated inside the &lt;code&gt;Order&lt;/code&gt; class. This doesn't compile, because of the private setter for &lt;code&gt;status&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;COMPLETED&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- Compiler error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;And this raises an exception:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- BOOM!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Intent is clearer, because the data and the business rule are defined in one class instead of two.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Intent is also more clear, because the &lt;code&gt;Order&lt;/code&gt; constructor contains only fields that are relevant for creating an order in the initial state. All state changes are done using methods like &lt;code&gt;complete&lt;/code&gt; (I omitted other methods for brevity).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Statuses
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Code review
&lt;/h4&gt;

&lt;p&gt;Coming back to the &lt;code&gt;Order&lt;/code&gt; class, let's zoom in on the order status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PENDING&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What can we say about such a small piece of code? Well, first of all it looks pretty safe, because the &lt;code&gt;status&lt;/code&gt; field has a private setter. What else? Let's look at how we would instantiate this class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've lost some information compared to the class definition. From this constructor invocation alone, it's not obvious what the status of the newly created order is. This is a limitation of constructors in general, because we can't give constructors a descriptive name.&lt;/p&gt;

&lt;h4&gt;
  
  
  Refactoring
&lt;/h4&gt;

&lt;p&gt;We can improve the communication of intent by introducing a factory method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;createPending&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can instantiate a new &lt;code&gt;Order&lt;/code&gt; using the &lt;code&gt;createPending&lt;/code&gt; factory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createPending&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is clearly an improvement. By using the factory we are explicit about the state of a newly created &lt;code&gt;Order&lt;/code&gt;. Unfortunately, there's a downside: We now have a public constructor that accepts any &lt;code&gt;OrderStatus&lt;/code&gt;. We have potentially introduced the same safety issue we addressed in the previous example.&lt;/p&gt;

&lt;p&gt;Fortunately, the solution is simple. We'll just make the constructor &lt;code&gt;private&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;createPending&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the design is safe again. There's only one way to create a new &lt;code&gt;Order&lt;/code&gt;, which is via the &lt;code&gt;createPending&lt;/code&gt; factory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We identified two requirements and two design principles to help us write code that is robust and maintainable. The requirements are:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We should design API's so that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;they are always &lt;strong&gt;safe&lt;/strong&gt; to use;&lt;/li&gt;
&lt;li&gt;they &lt;strong&gt;reveal intent&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can achieve this by using the design principles we discussed in the &lt;a href="//../02-pitfalls-and-practices#principles"&gt;previous article&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Illegal states should be unrepresentable.&lt;/li&gt;
&lt;li&gt;State changes should be encapsulated.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;We've seen various examples of how to put this into practice. Until now, the code examples we looked at were rather simple and straightforward. In the next installment we'll conclude this series by looking at a few examples that are less straightforward.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Designing with Types #02: Pitfalls and Practices</title>
      <dc:creator>Dirk Groot</dc:creator>
      <pubDate>Mon, 30 Jun 2025 09:32:30 +0000</pubDate>
      <link>https://dev.to/dirkgroot/designing-with-types-02-pitfalls-and-practices-1f99</link>
      <guid>https://dev.to/dirkgroot/designing-with-types-02-pitfalls-and-practices-1f99</guid>
      <description>&lt;p&gt;&lt;em&gt;In this installment of my Designing with Types series, we'll look at how some typical backend code is set up. We'll identify some common pitfalls and identify best practices to avoid these pitfalls.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Regardless of what architecture style we use, an application almost always consists of three basic tiers or layers: &lt;em&gt;Presentation&lt;/em&gt;, &lt;em&gt;Business&lt;/em&gt; and &lt;em&gt;Infrastructure&lt;/em&gt;. Here's a diagram that shows two architecture styles using these three layers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxeu54x8owrz0mgo24uox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxeu54x8owrz0mgo24uox.png" alt="Architecture styles" width="750" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)" rel="noopener noreferrer"&gt;Hexagonal Architecture&lt;/a&gt;, the &lt;em&gt;Business&lt;/em&gt; layer defines ports. The &lt;em&gt;Presentation&lt;/em&gt; and &lt;em&gt;Infrastructure&lt;/em&gt; layer contain adapters that interact with the web framework and the persistence framework. The &lt;a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle" rel="noopener noreferrer"&gt;Dependency Inversion Principle&lt;/a&gt; (DIP) is used to make the &lt;em&gt;Business&lt;/em&gt; layer easy to test, because infrastructure-dependent code can easily be replaced by &lt;a href="https://en.wikipedia.org/wiki/Test_double" rel="noopener noreferrer"&gt;test doubles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the classic &lt;a href="https://en.wikipedia.org/wiki/Multitier_architecture" rel="noopener noreferrer"&gt;N-Tier Architecture&lt;/a&gt;, the responsibilities of the layers are the same, only the dependency rules are different from the Hexagonal Architecture.&lt;/p&gt;

&lt;p&gt;Let's look at some examples of code we often encounter in these three layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure
&lt;/h3&gt;

&lt;p&gt;As mentioned in the diagram above, this is the layer where database communication usually happens. It's also commonly known as the &lt;em&gt;Data Access&lt;/em&gt; or &lt;em&gt;Persistence&lt;/em&gt; layer. I'm calling it &lt;em&gt;Infrastructure&lt;/em&gt; because it can be used for more than just persistence.&lt;/p&gt;

&lt;p&gt;When used for data access, this layer usually consists of database entities and repositories. Here's an example in Kotlin, using JPA annotations for object-relational mapping (ORM) and Spring Data's &lt;code&gt;CrudRepository&lt;/code&gt; to generate a repository implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"todoList"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;CascadeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"todolist_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@Repository&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TodoListRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CrudRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nd"&gt;@Repository&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TodoItemRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CrudRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findByTodoListId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Business
&lt;/h3&gt;

&lt;p&gt;This layer contains the business logic of the application. Its responsibility is to make sure that business rules are enforced in the application. It usually consists of services that perform a certain task, using repositories and entities from the &lt;em&gt;Infrastructure&lt;/em&gt; layer.&lt;/p&gt;

&lt;p&gt;Here's a simple service that can create and remove todo lists (again, in Kotlin):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoListService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoListRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;createTodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Name cannot be blank"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;todoList&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;removeTodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoListToRemove&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoListToRemove&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;TodoListNotRemovableException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoListToRemove&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a common pattern in these services. They check some business rules and if everything is okay, an action is performed. In this example, the business rules are that a todo list must have a name and that a todo list may only be deleted if all its todo items are done.&lt;/p&gt;

&lt;p&gt;Here's an example that creates a new todo item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoItemService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;?):&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dueDate&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;InvalidDueDateException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todoListRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;orElseThrow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;TodoListNotFoundException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;todoListRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;todoItem&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, besides input validation, we also ensure that we don't try to add a todo item to a list that does not exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Presentation
&lt;/h3&gt;

&lt;p&gt;In the variants mentioned earlier, the &lt;em&gt;Presentation&lt;/em&gt; layer contains presentation and application logic. Alternatively, presentation and application logic can be assigned to separate layers. It contains functionality that is used by end users and uses the &lt;em&gt;Business&lt;/em&gt; layer to validate and execute actions that users request. In a backend application that exposes a REST API (for example for a frontend or external systems), this layer contains REST controllers.&lt;/p&gt;

&lt;p&gt;Here's an example of a REST controller, using annotations from Spring Web:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/v1/todo/list"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoListResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoListService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoListService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoListRestModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todoListService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CREATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TodoListRestModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;Business&lt;/em&gt; layer is used to create and store the new todo item. The controller makes sure that a HTTP response is returned, containing the newly created todo item.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code review
&lt;/h2&gt;

&lt;p&gt;These code examples are pretty simple and straightforward. That's because the problem domain and the associated business rules are simple. You could argue that this code is perfectly fine for such a simple application, and I would agree.&lt;/p&gt;

&lt;p&gt;Still, I'd like to review this code with one important question in mind: &lt;strong&gt;Does it scale?&lt;/strong&gt; Features will be added and existing features will be expanded. Systems grow bigger and more complicated, and so does the code. Ideally, we want our code to be designed in such a way that it stays maintainable while the system grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplication
&lt;/h3&gt;

&lt;p&gt;While not directly obvious in the code above, we can see that we're setting ourselves up for code duplication. For example, let's expand our &lt;code&gt;TodoListService&lt;/code&gt; with the ability to rename an existing todo list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;createTodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Name cannot be blank"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;todoList&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;renameTodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Name cannot be blank"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have two functions that accept a name for a todo list. Obviously, a &lt;code&gt;String&lt;/code&gt; can be blank, which we don't want, so every time we use a &lt;code&gt;String&lt;/code&gt; for accepting the name of a todo list, we need to check if that &lt;code&gt;String&lt;/code&gt; contains a valid name.&lt;/p&gt;

&lt;p&gt;This problem arises when parameter types accept more values than the domain we're implementing allows for. If that's the case, we need input validation to make sure our function is not used in an invalid way. This is an anti-pattern, called &lt;a href="https://refactoring.guru/smells/primitive-obsession" rel="noopener noreferrer"&gt;Primitive Obsession&lt;/a&gt;. Be aware that this isn't limited to the usage of primitives. In general, when we use overly permissive parameter types, we likely need input validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Public mutable state
&lt;/h3&gt;

&lt;p&gt;Some of the state of entities in the &lt;em&gt;Infrastructure&lt;/em&gt; layer can be changed by everyone. Take &lt;code&gt;TodoItem&lt;/code&gt; for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"todolist_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fields &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;dueDate&lt;/code&gt; and &lt;code&gt;done&lt;/code&gt; have a public setter. This means it's very easy to write code that violates business rules that are normally enforced by the &lt;em&gt;Business&lt;/em&gt; layer. For example, we can easily introduce code that changes a todo item to have an empty &lt;code&gt;title&lt;/code&gt; or creates a new todo item with a &lt;code&gt;dueDate&lt;/code&gt; in the past.&lt;/p&gt;

&lt;p&gt;This is an anti-pattern known as &lt;a href="https://refactoring.guru/smells/inappropriate-intimacy" rel="noopener noreferrer"&gt;Inappropriate Intimacy&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Object_orgy" rel="noopener noreferrer"&gt;Object Orgy&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of encapsulation
&lt;/h3&gt;

&lt;p&gt;The issue of public mutable state is a consequence of another design issue: Data and business logic are separated between different classes. Data is modeled as entities, services make sure that the data conforms to the business rules. The entities shown here are basically Data Transfer Objects (DTO's). They tell the Object-Relational Mapper (ORM) what tables exist, which columns these tables consist of, et cetera.&lt;/p&gt;

&lt;p&gt;These DTO's are used thoughout the entire code base. While the &lt;em&gt;Business&lt;/em&gt; layer should be enforcing business rules, it's very easy to violate business rules by changing DTO's in a REST controller, for example.&lt;/p&gt;

&lt;p&gt;We're using an object-oriented (OO) language, but by designing our code like this we don't benefit from one of the main strengths of OO, which is that data and the associated behaviour are &lt;strong&gt;encapsulated&lt;/strong&gt; in one class. This makes our code more error-prone than it needs to be.&lt;/p&gt;

&lt;h3&gt;
  
  
  There's a bug!
&lt;/h3&gt;

&lt;p&gt;Maybe you already spotted it, there's a bug in &lt;code&gt;TodoItemService&lt;/code&gt;. Can you find it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;?):&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dueDate&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;InvalidDueDateException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todoListRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;orElseThrow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;TodoListNotFoundException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;todoListRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;todoItem&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've shown this code in several talks I've given, asking the participants to spot the bug. From my limited "testing" it seems that the bug is surprisingly hard to find just by looking at the code.&lt;/p&gt;

&lt;p&gt;Now, where's the bug? It's in this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;todoItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; that is passed to the constructor is the &lt;code&gt;id&lt;/code&gt; of the containing &lt;code&gt;TodoList&lt;/code&gt;! Let's assume the database table for todo items has a proper primary key or a unique constraint. If that's the case, this code will fail as soon as we try to add a second &lt;code&gt;TodoItem&lt;/code&gt; to a &lt;code&gt;TodoList&lt;/code&gt;, because a duplicate key will be inserted.&lt;/p&gt;

&lt;p&gt;This is another example of Primitive Obsession. While &lt;code&gt;UUID&lt;/code&gt; isn't a primitive type in the strict sense of the word, it &lt;em&gt;is&lt;/em&gt; a type that doesn't tell us anything about what kind of ID it actually represents. When we see just a &lt;code&gt;UUID&lt;/code&gt;, it can be hard to determine if it's the ID of a todo list, a todo item, or something entirely unrelated to the domain.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fun fact&lt;/em&gt;: This is a bug that I accidentally introduced when I was preparing this code for a talk. I decided to include it in the talk as an example of how Primitive Obsession can easily lead to bugs that can be hard to find.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smells
&lt;/h2&gt;

&lt;p&gt;To summarize, in these code examples we have identified a couple of code smells:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primitive Obsession&lt;/strong&gt; - By using primitives or other overly permissive types, we need to duplicate input validation. Also, it's very easy to introduce bugs when multiple domain concepts are implemented using the same primitive type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inappropriate Intimacy&lt;/strong&gt; - Unrestricted write access to the state of an object can easily lead to violation of business rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of encapsulation&lt;/strong&gt; - Data and behaviour are separated into different classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Principles
&lt;/h2&gt;

&lt;p&gt;One way we can address the issues we found is by &lt;strong&gt;designing better types&lt;/strong&gt;. In this chapter I'd like to provide some high-level principles and practices to help in designing better types. In future installments, we'll explore these more in-depth.&lt;/p&gt;

&lt;p&gt;I'd like to mention two principles that I think are fundamental to designing with types. The principles are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Illegal states should be unrepresentable&lt;/li&gt;
&lt;li&gt;State changes should be encapsulated&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Illegal states should be unrepresentable &lt;sup id="fnref1"&gt;1&lt;/sup&gt;
&lt;/h3&gt;

&lt;p&gt;When writing code, the very first feedback we get about our code is from the compiler. This is the shortest possible feedback loop we can have.&lt;/p&gt;

&lt;p&gt;For example, when we try to assign a string to an integer variable, the compiler will immediately complain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Compiler error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the idea behind this principle is that in order to make our feedback loop as short as possible, we &lt;strong&gt;prefer compile-time validation over runtime validation&lt;/strong&gt;. We do this by designing types in such a way that it is &lt;strong&gt;impossible&lt;/strong&gt; to write &lt;strong&gt;compiling code&lt;/strong&gt; that introduces an illegal state.&lt;/p&gt;

&lt;h3&gt;
  
  
  State changes should be encapsulated
&lt;/h3&gt;

&lt;p&gt;Whenever we're using an object, we should be confident that the state of that object is valid. It's hard to be confident about this when the state of an object is freely mutable by everyone. This is why we use &lt;strong&gt;encapsulation&lt;/strong&gt; in OO.&lt;/p&gt;

&lt;p&gt;So this principle means that we should make each class &lt;strong&gt;exclusively&lt;/strong&gt; responsible for enforcing &lt;strong&gt;its own&lt;/strong&gt; invariants. This implies that each class should have &lt;strong&gt;exclusive control&lt;/strong&gt; over its own state changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principles in practice
&lt;/h2&gt;

&lt;p&gt;So how do we put these principles into practice? Let's look at two simple examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Value Object pattern
&lt;/h3&gt;

&lt;p&gt;This is such a simple design pattern, but it's oh so powerful! A value object represents one single value. This can be a complex value consisting of multiple fields (i.e. amount and currency for money).&lt;/p&gt;

&lt;p&gt;A value object must conform to the following rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is immutable&lt;/li&gt;
&lt;li&gt;It is self-validating&lt;/li&gt;
&lt;li&gt;The identity of the object is the value itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at two examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Name cannot be blank"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Name must have exactly one line"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Description cannot be blank"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have two value objects, &lt;code&gt;Name&lt;/code&gt; and &lt;code&gt;Description&lt;/code&gt;. Both classes encapsulate an immutable value. Both classes make sure that no invalid value is accepted. This is arguably the simplest possible example of encapsulation, but there are profound consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Functions that accept a &lt;code&gt;Name&lt;/code&gt; and/or a &lt;code&gt;Description&lt;/code&gt; as a parameter don't need input validation for these parameters, because that's what the constructors have already done. We can safely assume that the arguments are valid.&lt;/li&gt;
&lt;li&gt;We write fewer tests, because if we don't need input validation, we don't need to unit tests input validation.&lt;/li&gt;
&lt;li&gt;We can't confuse parameters. The following code does not compile, because we cannot pass a &lt;code&gt;Description&lt;/code&gt; when a &lt;code&gt;Name&lt;/code&gt; is expected.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoListID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Compiler error&lt;/span&gt;
      &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TodoListID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Description\nwith multiple lines"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So here we see both principles in practice. We make illegal states unrepresentable because we cannot confuse values with different domains. The values in the value objects are encapsulated and guaranteed to be valid, which simplifies our code by eliminating the need for duplicated input validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encapsulation using sum types
&lt;/h3&gt;

&lt;p&gt;Here is a slightly more advanced example using a sum type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoItemID&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoItemID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newDescription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newDescription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;markAsDone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoItemID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we model &lt;code&gt;TodoItem&lt;/code&gt; as a value object. The states (&lt;code&gt;todo&lt;/code&gt; and &lt;code&gt;done&lt;/code&gt;) of a todo item are modeled as implementations of the &lt;code&gt;TodoItem&lt;/code&gt; interface. The signatures of these types reveal that the description of a todo item in the &lt;code&gt;todo&lt;/code&gt; state can be changed. Todo items that are &lt;code&gt;done&lt;/code&gt; cannot be changed at all&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;This is another way we can make illegal states unrepresentable using types. In fact, we take it a step further because thanks to the sum type, we also make illegal state &lt;strong&gt;changes&lt;/strong&gt; unrepresentable. The following code does not compile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TodoItemID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Do the laundry"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Done&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Do the dishes"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Compiler error&lt;/span&gt;
        &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Todo&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sum type forces us to check the state of the todo item, before attempting to do anything with it. If we don't, the code simply won't compile. Code that tries to change the description of a &lt;code&gt;done&lt;/code&gt; todo item also doesn't compile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A lot of the code I've seen and written during my career suffers to some degree from the problems mentioned in this article. As said, in simple, small systems there's little harm in having some Primitive Obsession or lack of encapsulation. In such cases adding a lot of types can feel like overengineering. This is fine, as long as you are aware that the antipatterns can become problematic when the system grows.&lt;/p&gt;

&lt;p&gt;By designing better types, we can make our code &lt;strong&gt;safer&lt;/strong&gt; to use and easier to understand by explicitly &lt;strong&gt;revealing intent&lt;/strong&gt;. In this article we've seen two simple examples of this.&lt;/p&gt;

&lt;p&gt;Next up, we'll talk some more about &lt;strong&gt;safety&lt;/strong&gt; and &lt;strong&gt;revealing intent&lt;/strong&gt; and we'll look at more examples of using types to our advantage.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;This phrase was coined by &lt;a href="https://blog.janestreet.com/effective-ml-revisited/" rel="noopener noreferrer"&gt;Yaron Minsky&lt;/a&gt;. &lt;a href="https://fsharpforfunandprofit.com/posts/designing-with-types-making-illegal-states-unrepresentable/" rel="noopener noreferrer"&gt;Scott Wlaschin&lt;/a&gt; wrote a very nice article about this, as part of his article series on designing with types using F#. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;For the Kotlin-savvy among you: Yes, I know about &lt;code&gt;copy&lt;/code&gt; 😄. We'll address that in a future installment of this series. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>design</category>
      <category>typesafety</category>
      <category>java</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Designing with Types #01: Introduction</title>
      <dc:creator>Dirk Groot</dc:creator>
      <pubDate>Wed, 21 May 2025 11:44:48 +0000</pubDate>
      <link>https://dev.to/dirkgroot/designing-with-types-01-introduction-5ece</link>
      <guid>https://dev.to/dirkgroot/designing-with-types-01-introduction-5ece</guid>
      <description>&lt;p&gt;&lt;em&gt;Ever had that sinking feeling when a bug sneaks into production, despite all your testing efforts? Yeah, we've all been there! While languages like Java, C#, and Kotlin come with powerful type systems, many developers aren't using them to their full potential.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In this series, we'll explore how we can use types to catch bugs before they even have a chance to become bugs. We'll look at practical ways to make our designs safer and easier to understand.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is the first part of a series of articles I'm writing about designing with types. In the first two articles we'll set the stage. In subsequent articles we'll explore some practical ways of using types make our code safer and easier to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback loops
&lt;/h2&gt;

&lt;p&gt;Before we dive in, let's consider why this is relevant. While developing software, we are constantly faced with feedback loops. The diagram below shows a simplified version of a typical development process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyj5eaxuq39qwh2miws5q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyj5eaxuq39qwh2miws5q.png" alt="Simplified development process" width="304" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This diagram shows three types of feedback we could get after we make a change. The build pipeline could fail, a code reviewer could give valuable feedback, or someone could find a bug while manually testing the change.&lt;/p&gt;

&lt;p&gt;From this small example, we can easily see that we're more productive when we have short feedback loops. Solving the bugs we inevitably introduce takes longer when more steps are between making the change and receiving feedback. If receiving feedback takes a long time, we'll go do something else in the meantime. This causes context switching which, as we know, is a big productivity killer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding bugs
&lt;/h3&gt;

&lt;p&gt;Development processes like the one shown above typically have multiple ways of finding bugs after they have been introduced. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code review&lt;/li&gt;
&lt;li&gt;Automated tests&lt;/li&gt;
&lt;li&gt;Manual / exploratory tests&lt;/li&gt;
&lt;li&gt;Acceptance test&lt;/li&gt;
&lt;li&gt;Collect end user feedback&lt;/li&gt;
&lt;li&gt;etc...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to do stuff like this. If you're doing all this, good job!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fleddhivd5kbi6l0t0uaf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fleddhivd5kbi6l0t0uaf.gif" alt="Good job" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Preventing bugs
&lt;/h3&gt;

&lt;p&gt;There are also multiple ways to prevent bugs from occurring in the first place, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backlog refinement&lt;/li&gt;
&lt;li&gt;Developing in small, safe steps&lt;/li&gt;
&lt;li&gt;Test-Driven Development&lt;/li&gt;
&lt;li&gt;🎇 &lt;strong&gt;Design&lt;/strong&gt; 🎇&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Design&lt;/strong&gt; is what this series is about. We'll explore several ways of using carefully designed types to &lt;strong&gt;make illegal states unrepresentable&lt;/strong&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. Code that introduces an illegal state should not compile.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farbgpgzagk62wgai2mpk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farbgpgzagk62wgai2mpk.gif" alt="Mind blown" width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Strategy
&lt;/h3&gt;

&lt;p&gt;As shown before, we typically spend a lot of time and effort on finding bugs in our software. This is a good thing. Mistakes will always be made and cannot be 100% prevented, not even by the techniques we'll be exploring in this series. However, from what I've seen "in the wild", I do think we tend to miss a lot of opportunities to design our software in ways that &lt;strong&gt;prevent&lt;/strong&gt; bugs from being introduced.&lt;/p&gt;

&lt;p&gt;The overall strategy we'll be exploring is to prevent bugs by carefully designing a &lt;strong&gt;domain model&lt;/strong&gt;. This domain model implements business rules and should be considered an &lt;strong&gt;API&lt;/strong&gt; that is used by our application logic. The domain model should consist of carefully designed types that make it impossible to build application logic that introduces illegal states in our application. Ideally, mistakes in our application logic are found by the compiler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design goals
&lt;/h3&gt;

&lt;p&gt;In short, the design goals for our domain model are to create an API that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is &lt;strong&gt;safe&lt;/strong&gt; to use&lt;/li&gt;
&lt;li&gt;reveals &lt;strong&gt;intent&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Safety
&lt;/h4&gt;

&lt;p&gt;One common cause of bugs is misusing the API of the domain model. Some examples are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assigning invalid values to properties&lt;/li&gt;
&lt;li&gt;Changing a property of an entity while not allowed&lt;/li&gt;
&lt;li&gt;Changing the "workflow" status of an entity while not allowed&lt;/li&gt;
&lt;li&gt;Calling functions while not allowed&lt;/li&gt;
&lt;li&gt;Having functions without proper input validation and calling them with invalid parameter values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this can and &lt;strong&gt;will&lt;/strong&gt; happen, when the API's we design allow for such mistakes to be made.&lt;/p&gt;

&lt;p&gt;So what do I mean when I say we should design APIs that are safe to use? It means we should aim to design API's that can be used without having to wonder whether they are safe to use in a particular situation. To achieve this, we should prefer compile time validation over runtime validation. When you use an API in an invalid way, the code should not compile.&lt;/p&gt;

&lt;h4&gt;
  
  
  Revealing intent
&lt;/h4&gt;

&lt;p&gt;Problems like the ones mentioned above also tend to happen when developers misunderstand the intended purpose of APIs. An obvious reason for this is lack of proper naming. For example, &lt;code&gt;completeOrder&lt;/code&gt; reveals a lot more intent than &lt;code&gt;setStatus&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another, more subtle, naming problem is that we tend to just not name things. We'll be looking at several examples of this in subsequent articles. As a preparatory exercise, you can look at the code you're working on and see if you can find concepts that are implemented, but not explicitly named.&lt;/p&gt;

&lt;p&gt;As you will see, safety and revealing intent often go hand in hand. When we improve the safety of your code, we will likely also make the code easier to understand and vice versa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next up
&lt;/h2&gt;

&lt;p&gt;In the next article we'll do some more setting the stage by reviewing some commonly seen code. In subsequent articles we'll explore ways of improving the safety and readability of our code.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;This phrase was coined by &lt;a href="https://blog.janestreet.com/effective-ml-revisited/" rel="noopener noreferrer"&gt;Yaron Minsky&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>design</category>
      <category>typesafety</category>
      <category>java</category>
    </item>
  </channel>
</rss>
