<?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: Vadym Yaroshchuk</title>
    <description>The latest articles on DEV Community by Vadym Yaroshchuk (@y9vad9).</description>
    <link>https://dev.to/y9vad9</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%2F1132619%2F7de12cb5-0dee-4828-b65a-9a259770ccdf.jpeg</url>
      <title>DEV Community: Vadym Yaroshchuk</title>
      <link>https://dev.to/y9vad9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/y9vad9"/>
    <language>en</language>
    <item>
      <title>UI shouldn't think about validation</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Fri, 27 Feb 2026 14:14:40 +0000</pubDate>
      <link>https://dev.to/y9vad9/ui-shouldnt-think-about-validation-269c</link>
      <guid>https://dev.to/y9vad9/ui-shouldnt-think-about-validation-269c</guid>
      <description>&lt;p&gt;I recently worked on abstracting the presentation layer from the UI layer. Ideally, the UI layer should be as dumb as possible when it comes to any non-UI logic. However, the presentation layer often suffers from a fundamental architectural flaw: it tries to decide &lt;em&gt;what&lt;/em&gt; the UI should do.&lt;/p&gt;

&lt;p&gt;This happens either directly (passing a specific error &lt;code&gt;String&lt;/code&gt; to display) or indirectly (passing a string resource &lt;code&gt;Int&lt;/code&gt; identifier). The latter creates a false sense of decoupling — the presentation layer is still dictating the exact screen output.&lt;/p&gt;

&lt;p&gt;This approach breaks the Separation of Concerns (SoC). The solution is to provide enough contextual information for the UI to decide for itself how to display the error, without leaking presentation or domain logic.&lt;/p&gt;

&lt;p&gt;My specific problem area was form input validation. I needed to show UI formatting errors (e.g., length limits) while keeping input validation strictly separated from domain invariants — a distinction many developers miss.&lt;/p&gt;

&lt;p&gt;The initial solution in my head was simple: why not just provide enums?&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;TaskCreateState&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NameIssue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&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;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DescriptionIssue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;  
    &lt;span class="kd"&gt;val&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;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DueDateIssue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;  
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TagsIssue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;  
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;capturedTags&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;String&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;emptyList&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="nc"&gt;TaskComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NameIssue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;BLANK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TOO_SHORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TOO_LONG&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DescriptionIssue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;BLANK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TOO_SHORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TOO_LONG&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DueDateIssue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;FORMAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;INEXISTING_DATETIME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;IN_PAST&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TagsIssue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;TOO_MANY&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;hasAnyIssues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&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;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;tags&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;hasAnyIssues&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where input is:&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;@ConsistentCopyVisibility&lt;/span&gt;  
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;constructor&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;rawString&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;val&lt;/span&gt; &lt;span class="py"&gt;issues&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;I&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;emptyList&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;isValidated&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="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;InputValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;  
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&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;return&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;issues&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawString&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  
            &lt;span class="n"&gt;isValidated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&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="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
    &lt;span class="n"&gt;rawString&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="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&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;-&amp;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;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;  
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
    &lt;span class="n"&gt;rawString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rawString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;issues&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;emptyList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;  
    &lt;span class="n"&gt;isValidated&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="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
&lt;span class="p"&gt;)&lt;/span&gt;  

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
    &lt;span class="n"&gt;rawString&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="k"&gt;vararg&lt;/span&gt; &lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&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;-&amp;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;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;  
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
    &lt;span class="n"&gt;rawString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rawString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;issues&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;  
    &lt;span class="n"&gt;isValidated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;  
&lt;span class="p"&gt;)&lt;/span&gt;  

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="py"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;hasAnyIssue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;  
    &lt;span class="k"&gt;get&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isValidated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  

&lt;span class="k"&gt;public&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;hasAnyIssues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;  
    &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&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="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasAnyIssue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;InputValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&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;public&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawString&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;I&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;p&gt;It looks clean, but iterating on this revealed a problem: if an enum simply says &lt;code&gt;TOO_LONG&lt;/code&gt;, the UI still has to calculate the input size or fetch the max length from the domain to show a meaningful error message. It's not as dumb as I want it to be.&lt;/p&gt;

&lt;p&gt;Enums weren't the solution. Instead, I migrated to &lt;code&gt;sealed&lt;/code&gt; structures to represent validation errors. These data-rich issues give the UI enough context to render the error without forcing it to think or calculate:&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;TaskNameValidator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;InputValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TaskNameIssue&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;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawString&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TaskNameIssue&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TaskNameIssue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Blank&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TaskName&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="n"&gt;rawString&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="nc"&gt;TaskName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CreationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TooShort&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
                &lt;span class="nc"&gt;TaskNameIssue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TooShort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
                    &lt;span class="n"&gt;minLength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TaskName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MIN_LENGTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                    &lt;span class="n"&gt;currentLength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rawString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&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="nc"&gt;TaskName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CreationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TooLong&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
                &lt;span class="nc"&gt;TaskNameIssue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TooLong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
                    &lt;span class="n"&gt;maxLength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TaskName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
                    &lt;span class="n"&gt;currentLength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rawString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&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="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;TaskName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CreationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;emptyList&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="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TaskNameIssue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Blank&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TaskNameIssue&lt;/span&gt;  
    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TooShort&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;minLength&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;currentLength&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="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TaskNameIssue&lt;/span&gt;  
    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TooLong&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;maxLength&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;currentLength&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="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TaskNameIssue&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 UI receives every detail it needs. I eliminated the "decision context hunt" in the UI layer, as it no longer needs to know the presentation layer's implementation details or domain constraints.&lt;/p&gt;

&lt;p&gt;Another benefit of this approach is validator composition. I can use common validators across different screens, or compose them with screen-specific custom validators when business logic varies. Some might argue this involves code duplication, but strict SoC (both at the layer level and locally) is always worth the trade-off. Also, I don't think it's a big bottleneck in the era of AI (but, honestly, even without it, it doesn't take that much time).&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>architecture</category>
      <category>android</category>
    </item>
    <item>
      <title>Failures we don't model correctly</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Mon, 29 Dec 2025 11:15:16 +0000</pubDate>
      <link>https://dev.to/y9vad9/failures-we-dont-model-correctly-2hbo</link>
      <guid>https://dev.to/y9vad9/failures-we-dont-model-correctly-2hbo</guid>
      <description>&lt;p&gt;Guards, validation, error handling — we all do it. We throw exceptions, return null, wrap values into Result, or trust the caller to "do the right thing". Most of the time, we don't even think about it — we follow familiar patterns and move on.&lt;/p&gt;

&lt;p&gt;At the same time, there's a constant tension:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Exceptions are bad"&lt;/li&gt;
&lt;li&gt;"This should be exception-free"&lt;/li&gt;
&lt;li&gt;"We must handle all errors explicitly"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what does that actually mean?&lt;/p&gt;

&lt;p&gt;Not every failure is an error. Not every exception is exceptional. And not every unsafe operation deserves to be wrapped just to feel safer. What we're really dealing with is something more fundamental: violations of contracts between parts of our program.&lt;/p&gt;

&lt;p&gt;A function assumes something about its input. A caller assumes something about the outcome. IO assumes the world behaves. Sooner or later, those assumptions break.&lt;/p&gt;

&lt;p&gt;Kotlin gives us many ways to express that "something": throwing, returning null, exposing unsafe operations, wrapping results, or forcing callers to acknowledge failure through types. None of these are universally right or wrong — but each of them communicates responsibility in a different way.&lt;/p&gt;

&lt;p&gt;This article isn't about banning exceptions or worshipping types. It's about understanding what contract you're defining, who owns its violation, and how explicit your API should be.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Contract?
&lt;/h2&gt;

&lt;p&gt;Let's start with defining what do we mean by contract:&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;contract&lt;/strong&gt; is an agreement between a function and its caller that defines what inputs are allowed, what result is guaranteed if those inputs are valid, and what happens when those expectations are violated. It describes the assumptions a function makes, the promises it gives in return, and how failure is expressed when those assumptions do not hold.&lt;/p&gt;

&lt;p&gt;We can divide contract into three main points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Preconditions&lt;/strong&gt;: What conditions caller must uphold before an invocation. For example, &lt;code&gt;List&amp;lt;E&amp;gt;.first()&lt;/code&gt; from standard library expects you to check or be sure that &lt;code&gt;List&amp;lt;E&amp;gt;&lt;/code&gt; contains at least one element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postconditions&lt;/strong&gt;: What callee guarantees in return. Coming back to the previous example, &lt;code&gt;List&amp;lt;E&amp;gt;.first()&lt;/code&gt; guarantees that on non-empty list it will return the first element in an array.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure semantics&lt;/strong&gt;: What happens if contract is violated (function throws an exception, function returns null / type-safe result or just terminates the whole program).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Important thing to understand is that contract is not about validation like putting &lt;code&gt;require(...)&lt;/code&gt; or &lt;code&gt;check(...)&lt;/code&gt; in your code, rather the definition &lt;strong&gt;what is allowed, what is promised, and who is to blame when it goes wrong&lt;/strong&gt;. You may sometimes not even have any validation (what is better to avoid; &lt;a href="https://en.wikipedia.org/wiki/Fail-fast_system" rel="noopener noreferrer"&gt;fail-fast&lt;/a&gt; is still a thing), but it doesn't make 'contract' as a thing magically disappear.&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of contract violation
&lt;/h3&gt;

&lt;p&gt;As this article most and foremost about handling contract violation, let's talk about different types of contract violations.&lt;/p&gt;

&lt;h4&gt;
  
  
  User Input
&lt;/h4&gt;

&lt;p&gt;User input is the most obvious and most talked-about source of contract violations — and for a good reason. Users are not part of your program. They don't know your invariants, don't respect your constraints, and will happily provide input that violates every assumption your code could possibly make.&lt;/p&gt;

&lt;p&gt;In this case, contract violations are &lt;strong&gt;expected&lt;/strong&gt;, &lt;strong&gt;frequent&lt;/strong&gt;, and &lt;strong&gt;non-exceptional&lt;/strong&gt;. An empty field, an invalid email, a negative number where only positive values make sense — none of this is surprising. It's part of the normal control flow.&lt;br&gt;
That's why user input is usually the place where we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validate eagerly,&lt;/li&gt;
&lt;li&gt;report errors explicitly,&lt;/li&gt;
&lt;li&gt;and avoid throwing whenever possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, throwing an exception often means &lt;em&gt;you lost control&lt;/em&gt;. The contract was violated, but the violation was predictable, and your API should reflect that reality. Returning a typed error, a validation result, or a failure value is usually the honest thing to do.&lt;/p&gt;

&lt;p&gt;The important part is ownership: &lt;strong&gt;the caller owns the violation&lt;/strong&gt;. The user broke the contract, not the system.&lt;/p&gt;
&lt;h4&gt;
  
  
  Programmer Error
&lt;/h4&gt;

&lt;p&gt;Programmer errors live in a different reality. These are internal mistakes (logic bugs) that usually lead to exceptions. Unlike user input, which is expected to be invalid at times, programmer errors indicate something went wrong &lt;strong&gt;inside your program&lt;/strong&gt;, and you typically &lt;strong&gt;don't handle them silently&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose you have a function that sets &lt;strong&gt;the width of a view&lt;/strong&gt;, and it throws if the width is negative:&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;setWidthPx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;widthPx&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="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;widthPx&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Width must be non-negative"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layoutParams&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layoutParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&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="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;widthPx&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, imagine a caller that calculates a width but &lt;strong&gt;forgets to handle an edge case&lt;/strong&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;val&lt;/span&gt; &lt;span class="py"&gt;parentWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;measuredWidth&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;padding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;desiredWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parentWidth&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;  &lt;span class="c1"&gt;// oops, parentWidth is smaller than padding * 2&lt;/span&gt;
&lt;span class="nf"&gt;setWidthPx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;desiredWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 💥 throws: Width must be non-negative&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function is correct, the caller is "normal", but the contract is violated. This is &lt;strong&gt;a real programmer error&lt;/strong&gt;, and it's not something your program should try to silently recover from — except by not making such mistakes in the first place.&lt;/p&gt;

&lt;h4&gt;
  
  
  Environmental failure
&lt;/h4&gt;

&lt;p&gt;Environmental failures are a completely different beast. These come from things &lt;strong&gt;you don't control&lt;/strong&gt;, like the file system, network, or hardware. Unlike programmer errors, no amount of perfect internal logic guarantees success. Even with flawless code, these operations &lt;strong&gt;can still fail&lt;/strong&gt; — and that's why they need explicit handling.&lt;/p&gt;

&lt;p&gt;And even so, that &lt;strong&gt;doesn't mean we should overcomplicate our code&lt;/strong&gt; or pretend these failures don't exist. The goal isn't to wrap everything in layers of types or defensive checks — it's to &lt;strong&gt;handle the inevitable explicitly, clearly, and in the right place&lt;/strong&gt; with right pace.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to handle contract violations?
&lt;/h2&gt;

&lt;p&gt;Now as we discussed types of contract violation, it's time to discuss the ways how we can at best mitigate risks that may break our code.&lt;/p&gt;

&lt;h3&gt;
  
  
  How is it handled in code we use?
&lt;/h3&gt;

&lt;p&gt;Let's start with examples we see on day-to-day basis.&lt;/p&gt;

&lt;h4&gt;
  
  
  Standard library
&lt;/h4&gt;

&lt;p&gt;kotlin-stdlib is something many developers take inspiration from when building their own APIs. And no wonder — it introduced a set of patterns that quietly shaped how we write Kotlin today.&lt;/p&gt;

&lt;p&gt;Let's start with functions that &lt;strong&gt;throw exceptions&lt;/strong&gt; on contract violation:&lt;/p&gt;

&lt;p&gt;Firstly, let's start from those that throws exceptions in case of contract violation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/get-or-throw.html" rel="noopener noreferrer"&gt;kotlin.Result&amp;lt;T&amp;gt;.getOrThrow()&lt;/a&gt; throws encapsulated &lt;code&gt;Exception&lt;/code&gt; in case if the result was unsuccessful. You're expected to be absolutely sure about result by checking &lt;code&gt;isSuccess()&lt;/code&gt; before calling.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.text/to-int.html" rel="noopener noreferrer"&gt;String.toInt()&lt;/a&gt; throws &lt;code&gt;NumberFormatException&lt;/code&gt; in case if string wasn't a valid number. You're expected to check the string beforehand, be sure about the input or use another variant of function that we're going to discuss below.&lt;/li&gt;
&lt;li&gt;Iterable&amp;lt;Int&amp;gt;.&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/max.html" rel="noopener noreferrer"&gt;max&lt;/a&gt;/&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/min.html" rel="noopener noreferrer"&gt;min&lt;/a&gt;/&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/first.html" rel="noopener noreferrer"&gt;first&lt;/a&gt;/&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/last.html" rel="noopener noreferrer"&gt;last&lt;/a&gt;() that throws &lt;code&gt;NoSuchElementException&lt;/code&gt; in case if given iterable didn't have any elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these APIs rely on &lt;strong&gt;your knowledge of the state you pass in&lt;/strong&gt;. If that knowledge turns out to be wrong, they throw. Loudly and immediately.&lt;/p&gt;

&lt;p&gt;But throwing isn't the only option. For most of these functions, the standard library also provides OrNull variants, explicitly signaling that failure is a &lt;em&gt;possible and expected outcome&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-result/get-or-null.html" rel="noopener noreferrer"&gt;Result&amp;lt;T&amp;gt;.getOrNull()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.text/to-int-or-null.html" rel="noopener noreferrer"&gt;String.toIntOrNull()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/max-or-null.html" rel="noopener noreferrer"&gt;Iterable&amp;lt;Int&amp;gt;.maxOrNull()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, the contract is different: &lt;em&gt;you don't know for sure&lt;/em&gt;, so the API forces you to deal with the absence of a value.&lt;/p&gt;

&lt;p&gt;A slightly less talked-about pattern is &lt;code&gt;OrElse&lt;/code&gt;, which pushes that responsibility even further to the call site:&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;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"c"&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;value&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;elementAtOrElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;missing at $index&amp;gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of throwing or returning null, the function asks you to &lt;strong&gt;define the fallback explicitly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These three patterns — OrThrow, OrNull, and OrElse — form the backbone of the standard library's approach to contract violations. Kotlin doesn't force you into a single "correct" strategy. Instead, it gives you multiple ways to express &lt;strong&gt;how confident you are&lt;/strong&gt;, &lt;strong&gt;who owns the failure&lt;/strong&gt;, and &lt;strong&gt;how explicit you want your API to be&lt;/strong&gt;. &lt;/p&gt;

&lt;h4&gt;
  
  
  kotlinx.coroutines
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;kotlinx.coroutines&lt;/code&gt; are not much different and it's also no wonder – they're made by Kotlin team as well.&lt;/p&gt;

&lt;p&gt;We can see the same patterns, as well as some other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/get-completion-exception-or-null.html" rel="noopener noreferrer"&gt;Deferred&amp;lt;T&amp;gt;.getCompletionExceptionOrNull(): T?&lt;/a&gt; returns null if completion was not exceptional.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/get-completed.html" rel="noopener noreferrer"&gt;Deffered.getCompleted()&lt;/a&gt; throws &lt;code&gt;IllegalStateException&lt;/code&gt; if &lt;code&gt;Deffered&amp;lt;T&amp;gt;&lt;/code&gt; wasn't completed at the moment of call.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/try-send.html" rel="noopener noreferrer"&gt;SendChannel.trySend(value: T)&lt;/a&gt;: &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel-result/" rel="noopener noreferrer"&gt;ChannelResult&amp;lt;T&amp;gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-mutable-shared-flow/try-emit.html" rel="noopener noreferrer"&gt;MutableSharedFlow.trySend(value: T)&lt;/a&gt;: &lt;a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-boolean/" rel="noopener noreferrer"&gt;Boolean&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we can see, there's another pattern that prepends &lt;code&gt;try&lt;/code&gt; to its regular counterpart — for example, &lt;code&gt;trySend&lt;/code&gt;. In &lt;code&gt;kotlinx.coroutines&lt;/code&gt;, this pattern exists to turn an exception-based contract into an explicit, value-based one.&lt;/p&gt;

&lt;p&gt;The original function signals failure by throwing. The &lt;code&gt;try*&lt;/code&gt; variant preserves the same operation, but returns a meaningful result instead, allowing the caller to handle failure without relying on exceptions or try/catch.&lt;/p&gt;




&lt;p&gt;These are the main patterns you'll find across Kotlin's official libraries. The interesting part starts when you have to choose one.&lt;/p&gt;

&lt;p&gt;That choice is rarely about personal taste or "exceptions vs types". It's about what kind of failure you're modeling, who owns it, and what the caller is expected to know or guarantee.&lt;/p&gt;

&lt;p&gt;Is a failure part of normal control flow, or does it indicate a broken assumption?&lt;br&gt;
Is the caller expected to recover, or is this a point where the program should stop pretending everything is fine?&lt;br&gt;
Does this operation cross a system boundary, or does it stay entirely inside trusted code?&lt;/p&gt;

&lt;p&gt;Answering these questions usually makes the pattern obvious: throwing, &lt;code&gt;OrNull&lt;/code&gt;, &lt;code&gt;OrElse&lt;/code&gt;, or &lt;code&gt;try*&lt;/code&gt; stops being a stylistic choice and becomes part of the contract you're defining.&lt;/p&gt;
&lt;h3&gt;
  
  
  Reality
&lt;/h3&gt;

&lt;p&gt;The real question isn't which patterns exist, but where you draw the line.&lt;/p&gt;

&lt;p&gt;In practice, most codebases don't fail everywhere equally. They fail at boundaries.&lt;/p&gt;
&lt;h4&gt;
  
  
  User Input
&lt;/h4&gt;

&lt;p&gt;User input is one such boundary. We expect invalid data, so throwing is usually the wrong signal. An "exception" there doesn't communicate a bug — it just means the user typed something weird. That's why APIs at this edge naturally gravitate toward type-safe results: they make failure explicit, expected, and local.&lt;/p&gt;

&lt;p&gt;At this boundary, we should shift our focus from exception-first to safe-first:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserName&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;rawString&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="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;create&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;FactoryResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;FactoryResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InvalidLength&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;FactoryResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserName&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;FactoryResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;InvalidLength&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FactoryResult&lt;/span&gt;
        &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Success&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FactoryResult&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;Which.. directly contradicts our previously established pattern. But is it bad?&lt;/p&gt;

&lt;p&gt;Patterns like throwing exception by default and then having &lt;code&gt;OrNull&lt;/code&gt; or &lt;code&gt;try*&lt;/code&gt; are good — &lt;em&gt;when they apply&lt;/em&gt;. The Kotlin standard library is a general-purpose library and designed without our context in mind. By breaking the pattern here we risk nothing: type-system guards us.&lt;/p&gt;

&lt;p&gt;But does this mean that all input should be treated as equally unreliable everywhere and we should discourage patterns that were previously discussed? No.&lt;/p&gt;

&lt;p&gt;A simple counterexample is the database. When data comes from the database, invalid values usually signal a bug rather than a typical user mistake. Since it was supposed to be validated beforehand.&lt;/p&gt;

&lt;p&gt;That's why we often introduce an unsafe variant that intentionally &lt;em&gt;bypasses&lt;/em&gt; unsafely our type-safe result:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserName&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;rawString&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="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="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;createOrThrow&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;UserName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;50&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;IllegalArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&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="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;It still carries a familiar Kotlin Standard Library flavor, closely aligning with functions like &lt;code&gt;Result&amp;lt;T&amp;gt;.getOrThrow()&lt;/code&gt;, which we call only when we are confident the result &lt;em&gt;must&lt;/em&gt; be successful — and anything else would indicate a bug.&lt;/p&gt;

&lt;p&gt;We don't make &lt;code&gt;create&lt;/code&gt; throw, nor try* return a type-safe variant by default — &lt;strong&gt;we aren't a generic library like Kotlin Standard Library or kotlinx.coroutines&lt;/strong&gt; that doesn't know its target usage. We know what we expect, and in our case — every extra variant is an &lt;em&gt;extra choice&lt;/em&gt; — and by definition, every extra choice is an opportunity to misuse it. &lt;code&gt;create&lt;/code&gt; is the primary function: it's what appears first in code completion, it's the first one developers reach for, and it sets the expectation of safe handling. &lt;/p&gt;

&lt;h4&gt;
  
  
  Internal Input
&lt;/h4&gt;

&lt;p&gt;Even though most of the impact on our system comes from users, it doesn't mean we need to apply safe-first patterns everywhere or blindly wrap every system call with &lt;code&gt;OrThrow&lt;/code&gt;. That would be overkill.&lt;/p&gt;

&lt;p&gt;For data whose lifecycle is &lt;strong&gt;entirely owned by the system&lt;/strong&gt;, it's perfectly fine to throw immediately on invalid values. If a contract is violated here, it's a bug — and we want to know about it as soon as possible. We don't need extra layers of safety for something we fully control. 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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConfirmationAttempts&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;rawInt&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="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;rawInt&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Confirmation attempts cannot be negative."&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;&lt;code&gt;ConfirmationAttempts&lt;/code&gt; it's quite logical to know that confirmation attempts cannot be negative — its contract is well-established just by naming. You &lt;em&gt;could&lt;/em&gt; technically wrap it in &lt;code&gt;createOrThrow&lt;/code&gt; if you want, but I usually don't — because if every system-owned value is wrapped this way, the "attention signal" that &lt;code&gt;OrThrow&lt;/code&gt; conveys gets lost — especially if everything that throws has such signal. It becomes background noise: effectively, it's the same as just putting a require in the init block, and no one notices it anymore.&lt;/p&gt;

&lt;p&gt;This doesn't make the previously discussed patterns from standard library and &lt;code&gt;kotlinx.coroutines&lt;/code&gt; obsolete. We use it for the same reason as these libraries do — when we're not sure at what boundary is it going to be used — is it a user input? Or is it a trusted boundary where mistakes are not supposed to happen? &lt;/p&gt;

&lt;p&gt;The principle is simple: &lt;code&gt;OrThrow&lt;/code&gt; is a way to consume result unsafely, not a default for marking throwable code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Uncontrolled Input
&lt;/h4&gt;

&lt;p&gt;Not all data comes neatly packaged as "user input" or "system-owned". Sometimes, we deal with sources that are neither fully trusted nor entirely under our control — external APIs or third-party services. These represent a &lt;strong&gt;gray area&lt;/strong&gt;, where contracts exist but you can't guarantee on your side that everything will go according to the plan.&lt;/p&gt;

&lt;p&gt;We basically treat it as a user input, but as we're rarely care what exactly went wrong (except logging, but propagated exception is more than enough), I usually introduce &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; in such cases:&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;UserProfileRepository&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;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ConcurrentHashMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="c1"&gt;// should be better caching, just an example&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;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserProfileDao&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;networkClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserProfileApi&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;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Logger&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="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&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="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. In-memory cache is trusted system state&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cached&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;userId&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;cached&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="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Database I/O is an Environmental Failure boundary&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dbEntity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;suspendRunCatching&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;getOrElse&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;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure&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="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;dbEntity&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="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Mapping is OUTSIDE the wrapper. If this fails, it's a Programmer Error.&lt;/span&gt;
            &lt;span class="c1"&gt;// And we don't want it to be swallowed.&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dbEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapToDomain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
            &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Network I/O is the most common Environmental Failure boundary&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;suspendRunCatching&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;networkClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;getOrElse&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;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure&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="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Again, mapping is outside to ensure we don't hide bugs&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapToDomain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Database Write (Best-effort Side-effect)&lt;/span&gt;
        &lt;span class="c1"&gt;// Persistence is a secondary concern. We wrap and log it so &lt;/span&gt;
        &lt;span class="c1"&gt;// that a disk/DB failure doesn't deprive the user of a &lt;/span&gt;
        &lt;span class="c1"&gt;// successfully fetched result.&lt;/span&gt;
        &lt;span class="nf"&gt;suspendRunCatching&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;onFailure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to persist user profile to DB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&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;It is crucial to notice that we don't wrap the entire function in a single &lt;code&gt;suspendRunCatching&lt;/code&gt; (a custom-made alternative function to &lt;code&gt;runCatching&lt;/code&gt; that doesn't swallow &lt;code&gt;CancellationException&lt;/code&gt;, preventing breakage of structured concurrency). Instead, we surgically wrap only the specific I/O boundaries where failure is an environmental reality.&lt;/p&gt;

&lt;p&gt;The mapping logic (&lt;code&gt;mapToDomain&lt;/code&gt;) is intentionally left outside. We don't expect our internal mapping to fail; if it does, it is a Programmer Error, not a runtime failure. By keeping it outside the wrapper, we ensure the app crashes immediately, allowing us to catch the bug rather than silencing it inside a &lt;code&gt;Result.failure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In addition, you might have seen how we handled the database insertion failure. We express that this error is not that critical; we can consider it an exception that does not signal an exceptional state, rather it is a degraded state of a non-critical side-effect. Since the primary contract (delivering the user profile) has already been fulfilled by the network fetch and mapping — a failure in the persistence layer is secondary. It is a best-effort operation where the transient state of the local disk should not be allowed to break the successful delivery of the primary result to the caller.&lt;/p&gt;

&lt;p&gt;At the end of the day, you may also introduce the same pattern we apply for user input with sealed result — it depends on how much is it important to you to handle &lt;em&gt;specifics&lt;/em&gt; of the failure. But logic of programmer errors remains.&lt;/p&gt;

&lt;h3&gt;
  
  
  False Safety
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Catch.. less
&lt;/h4&gt;

&lt;p&gt;Coming back to the example from the previous section — more specifically, the database — can we really say that &lt;em&gt;every&lt;/em&gt; error thrown by an insertion or any other database operation is an environmental failure and not a programmer error?&lt;/p&gt;

&lt;p&gt;As we established earlier with &lt;code&gt;mapToDomain&lt;/code&gt;, we explicitly didn’t want to wrap everything into &lt;code&gt;suspendRunCatching&lt;/code&gt;. The reason is simple: we don’t want to ignore errors that are not part of uncontrolled output.&lt;/p&gt;

&lt;p&gt;A database can throw in very different scenarios:&lt;/p&gt;

&lt;p&gt;For example, database may throw in different scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environmental failures&lt;/strong&gt; — temporary network issues, connection pool exhaustion, database restarts, timeouts. These are rare, but they &lt;em&gt;do&lt;/em&gt; happen, and they're largely outside of your control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Programmer errors caused by invalid schema or assumptions&lt;/strong&gt; — missing indexes, violated constraints, incompatible column types, mismatched migrations, incorrect SQL. These indicate a broken contract inside your system, not an unstable environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treating both categories the same way is where false safety starts creeping in.&lt;/p&gt;

&lt;p&gt;Yes, we want our users not to see crashes caused by things they don't control — and often things &lt;em&gt;we&lt;/em&gt; don't control either. But that doesn't mean we should silence failures blindly. Tools like detekt or ktlint warn you for a reason: being explicit about what you &lt;em&gt;intentionally&lt;/em&gt; ignore is part of writing honest code.&lt;/p&gt;

&lt;p&gt;The question is not &lt;em&gt;"should this throw?"&lt;/em&gt;, but rather &lt;em&gt;"what exactly am I willing to silence here?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Consider this simple function I recently implemented:&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;public&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;reified&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;R2dbcTransaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEnumTypeIgnoring&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;enumName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;simpleName&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;lowercase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enum must have a name"&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;enumValues&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enumValues&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;joinToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"'${it.name}'"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CREATE TYPE $enumName AS ENUM ($enumValues)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// postgresql does not support CREATE TYPE IF NOT EXISTS, so we want to ignore such errors;&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;At first glance, this looks reasonable. But what's the flaw here? &lt;/p&gt;

&lt;p&gt;We're catching &lt;strong&gt;far too broadly&lt;/strong&gt;. By swallowing &lt;code&gt;Exception&lt;/code&gt;, we're not just ignoring the "type already exists" case — we're also ignoring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL syntax errors&lt;/li&gt;
&lt;li&gt;permission issues&lt;/li&gt;
&lt;li&gt;broken connections&lt;/li&gt;
&lt;li&gt;misconfigured transactions&lt;/li&gt;
&lt;li&gt;or even bugs introduced by future refactoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A first improvement might look like this:&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CREATE TYPE $enumName AS ENUM ($enumValues)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;R2dbcException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// postgresql does not support CREATE TYPE IF NOT EXISTS, so we want to ignore such errors;&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 better, but still insufficient. We're now ignoring &lt;em&gt;all&lt;/em&gt; database-level failures — including ones that absolutely should surface.&lt;/p&gt;

&lt;p&gt;We can narrow it down further:&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CREATE TYPE $enumName AS ENUM ($enumValues)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;R2dbcException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// In PostgreSQL, SQLSTATE 42710 corresponds to duplicate_object.&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqlState&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"42710"&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="n"&gt;e&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 intent is explicit. We are not saying &lt;em&gt;"database errors don't matter"&lt;/em&gt;. We're saying &lt;em&gt;"this very specific failure is expected and acceptable; everything else is not"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And it doesn't apply only to such "helper" functions — it applies everywhere.&lt;/p&gt;

&lt;h4&gt;
  
  
  Failure has destination
&lt;/h4&gt;

&lt;p&gt;And while you usually don't want to leak exceptions like previously "broken connection" (especially it) outside your database layer (which is obviously might be neither ours or user's fault), this doesn't mean that every error should be caught everywhere they might occur.&lt;/p&gt;

&lt;p&gt;Some failures are &lt;strong&gt;expected to occur&lt;/strong&gt;, but that does not mean they should be handled everywhere they arise.&lt;br&gt;&lt;br&gt;
A broken connection, a transaction failure, or a driver error are part of the environment your code runs in. They are neither programmer errors nor conditions to be silently absorbed.&lt;/p&gt;

&lt;p&gt;These failures must be allowed to &lt;strong&gt;propagate until they reach the boundary that owns the decision of what to do next&lt;/strong&gt;. Catching them earlier doesn't make the system safer — it only hides the fact that something went wrong and shifts responsibility to the wrong place.&lt;/p&gt;

&lt;p&gt;And that's why, in the &lt;code&gt;createEnumTypeIgnoring&lt;/code&gt; example, we &lt;em&gt;didn't&lt;/em&gt; wrap the entire function in a broad &lt;code&gt;try/catch&lt;/code&gt; — on its own it means and does nothing. There, uncontrolled failures are allowed — and expected — to propagate. The function only silences the &lt;strong&gt;specific, understood case&lt;/strong&gt; that is part of its contract (duplicate type), and lets everything else escape.&lt;/p&gt;

&lt;p&gt;This is intentional. Error handling is not about preventing exceptions from being thrown; it's about &lt;strong&gt;choosing the boundary where they should be handled&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Catching an exception too early flattens context. Catching it too late leaks infrastructure details. The right place is usually a boundary that understands both sides: it knows &lt;em&gt;what operation was attempted&lt;/em&gt; and &lt;em&gt;what the caller can reasonably do next&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That's also why we don't also "propagate a logger" everywhere. Logging, like error handling, belongs to a boundary that has enough context to decide whether a failure is expected noise, a degraded state, or a real bug.&lt;/p&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some errors should throw.&lt;/li&gt;
&lt;li&gt;Some errors should travel.&lt;/li&gt;
&lt;li&gt;Very few errors should be caught "just in case".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The discipline is not in avoiding exceptions, but in &lt;strong&gt;letting them move until they reach the boundary that can give them meaning&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;By the way, do you still remember &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/get-completion-exception-or-null.html" rel="noopener noreferrer"&gt;&lt;code&gt;Deferred&amp;lt;T&amp;gt;.getCompletionExceptionOrNull(): T?&lt;/code&gt;&lt;/a&gt;? If you assumed it &lt;em&gt;returns the completion exception or &lt;code&gt;null&lt;/code&gt; otherwise&lt;/em&gt; — you were wrong. I was too. Until it &lt;strong&gt;threw&lt;/strong&gt; an exception in real code.&lt;/p&gt;

&lt;p&gt;Only after hitting a bug do you usually end up in the docs, where it says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Returns &lt;em&gt;completion exception&lt;/em&gt; result if this deferred was &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/is-cancelled.html" rel="noopener noreferrer"&gt;cancelled&lt;/a&gt; and has &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/is-completed.html" rel="noopener noreferrer"&gt;completed&lt;/a&gt;, &lt;code&gt;null&lt;/code&gt; if it had completed normally, or throws &lt;a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-illegal-state-exception/index.html" rel="noopener noreferrer"&gt;IllegalStateException&lt;/a&gt; if this deferred value has not &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/is-completed.html" rel="noopener noreferrer"&gt;completed&lt;/a&gt; yet.&lt;/p&gt;

&lt;p&gt;This function is designed to be used from &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/invoke-on-completion.html" rel="noopener noreferrer"&gt;invokeOnCompletion&lt;/a&gt; handlers, when there is an absolute certainty that the value is already complete. See also &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/get-completed.html" rel="noopener noreferrer"&gt;getCompleted&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: This is an experimental api.&lt;/strong&gt; This function may be removed or renamed in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This isn’t a misuse — it’s a poorly shaped API. The name, return type, and common conventions strongly suggest a &lt;em&gt;safe query&lt;/em&gt;, yet the function hides a control-flow trap behind it.&lt;/p&gt;

&lt;p&gt;The lesson isn’t "read the docs more carefully". The lesson is: &lt;strong&gt;don’t design APIs that violate established expectations&lt;/strong&gt; — especially around errors.&lt;/p&gt;

&lt;p&gt;The lesson for the &lt;code&gt;kotlinx.coroutines&lt;/code&gt; maintainers is a hard one: which is more critical — labeling a function &lt;code&gt;OrNull&lt;/code&gt; just because the return type is nullable (like we wouldn't know it from compiler..?), or warning the developer of a hidden &lt;code&gt;IllegalStateException&lt;/code&gt;? By focusing on the &lt;code&gt;null&lt;/code&gt;, the API obscures the danger. In an honest system, it has a few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it should not throw,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OrNull&lt;/code&gt; marker should be replaced with &lt;code&gt;OrThrow&lt;/code&gt;. It would make sense if it would have any counterpart&lt;/li&gt;
&lt;li&gt;at the very least, it shouldn't lie about its safety by having &lt;code&gt;OrNull&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;OrThrow&lt;/code&gt;, &lt;code&gt;OrElse&lt;/code&gt;, and &lt;code&gt;try*&lt;/code&gt; are not about how failures are produced, but about how failure states are consumed.&lt;/p&gt;

&lt;p&gt;Each variant represents a different way for the caller to deal with a violated contract. Which one is appropriate depends on the boundary you are operating at.&lt;/p&gt;

&lt;p&gt;For trusted boundaries — such as system-owned code or data coming from a database that was already validated — exception-first is often more than acceptable choice. At these points, failure usually indicates a bug, not a recoverable situation, and throwing communicates that clearly.&lt;/p&gt;

&lt;p&gt;For untrusted or ambiguous boundaries — like user input or external systems we don't control — safe-first APIs are more honest. Focusing on returning a type-safe-first result makes failure explicit and forces the caller to acknowledge it where input is unexpected. Unsafe variants like &lt;code&gt;OrThrow&lt;/code&gt; may still exist, but they should be secondary and intentionally opt-in.&lt;/p&gt;

&lt;p&gt;Most importantly, &lt;code&gt;OrThrow&lt;/code&gt; should not be used as a marker that "this function can throw". Its purpose is to offer an alternative way to consume a failure state, not only to annotate danger. When overused, it loses its signaling value and becomes noise.&lt;/p&gt;

&lt;p&gt;These patterns are tools, not rules. They work best when chosen deliberately, based on the contract you are defining, the boundary you are crossing, and who owns the failure. Use them thoughtfully — not out of habit, but out of intent.&lt;/p&gt;

&lt;p&gt;Finally, remember &lt;strong&gt;the cost of false safety&lt;/strong&gt;: blindly silencing errors or over-protecting every operation may hide real issues. Handle failures deliberately, at the boundaries you understand, and propagate exceptions where they belong — not everywhere, not nowhere.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>codequality</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Semantic Typing We Ignore</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Wed, 24 Dec 2025 12:41:06 +0000</pubDate>
      <link>https://dev.to/y9vad9/semantic-typing-we-ignore-i0d</link>
      <guid>https://dev.to/y9vad9/semantic-typing-we-ignore-i0d</guid>
      <description>&lt;p&gt;In Kotlin, we constantly narrow our types. We prefer &lt;code&gt;String&lt;/code&gt; over &lt;code&gt;Any&lt;/code&gt; for text, &lt;code&gt;Int&lt;/code&gt; over &lt;code&gt;Any&lt;/code&gt; or even &lt;code&gt;Number&lt;/code&gt; for integers. This feels obvious — almost trivial. Done, right?&lt;/p&gt;

&lt;p&gt;So what is this article actually about?&lt;/p&gt;

&lt;p&gt;While we subconsciously avoid &lt;code&gt;Any&lt;/code&gt; in most situations — for good reasons, or sometimes simply because "why would I put &lt;code&gt;Any&lt;/code&gt; here instead of &lt;code&gt;Int&lt;/code&gt;?" — we often don't apply the same thinking when modeling our own software: its types, its behavior, and the relationships between them.&lt;/p&gt;

&lt;p&gt;This article explores the practice of &lt;strong&gt;semantic typing&lt;/strong&gt; — what it means, why you might want it, when it improves your code, and when it becomes a hindrance. We'll look at how semantic typing shows up in real Kotlin projects, both in application code and library design, and what tends to work well — or not.&lt;/p&gt;

&lt;p&gt;Even if you're not following Domain-Driven Design, these ideas still apply. You already narrow types every day; this article is about doing it deliberately and thoughtfully.&lt;/p&gt;

&lt;p&gt;We'll distill these lessons into practical rules to help you decide &lt;strong&gt;when to narrow types semantically&lt;/strong&gt; — guided by reason, not intuition or habit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is semantic typing?
&lt;/h2&gt;

&lt;p&gt;Let's start by defining what we're actually talking about, why it matters, and why it's not something you can just ignore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic typing&lt;/strong&gt; is the practice of creating semantically meaningful subsets of general-purpose types to express intent and prevent accidental misuse. It's about shifting from structural types ("this is a string") to behavioral or semantic types ("this is a email").&lt;/p&gt;

&lt;p&gt;The idea of semantic typing isn't unique to Kotlin — it has long been a common practice in languages like Java, where developers define lightweight wrapper classes (e.g. &lt;code&gt;UserId&lt;/code&gt;, &lt;code&gt;Email&lt;/code&gt;) around primitives to encode domain meaning and prevent accidental misuse.&lt;/p&gt;

&lt;p&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailAddress&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;raw&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="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's worth mentioning that semantic typing isn't limited to wrapping primitives; it applies equally to wrapping any type — whether a simple Int or a complex &lt;code&gt;Map&amp;lt;K, V&amp;gt;&lt;/code&gt;. The core idea is to give semantic meaning and stronger type safety by distinguishing values beyond their raw structure.&lt;/p&gt;

&lt;p&gt;Why should you care? This is usually where skepticism appears. Whenever this approach comes up, the reactions often sounds like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do I gain in return for this extra code? Isn't this just more boilerplate?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are completely valid questions — it makes you invest more time into thinking how to model your code and overall takes more time to write it. I'm all for following ideas of &lt;a href="https://en.wikipedia.org/wiki/KISS_principle" rel="noopener noreferrer"&gt;KISS&lt;/a&gt; and avoiding wrong abstractions problem.. and now onto a big BUT! 🌚&lt;/p&gt;

&lt;p&gt;What you get in return is actually not an abstraction for its own sake — it's clarity. Let's look at what this means in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Compile-time safety
&lt;/h3&gt;

&lt;p&gt;Let's create an 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="cm"&gt;/**
 * @param value The width in density-independent pixels. 
 */&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setWidth&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="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&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;At first glance, this looks fine. But it's easy to accidentally swap the measurement unit when calling 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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;containerSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="c1"&gt;// random int or just raw pixels value&lt;/span&gt;
&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// dp was expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code successfully compiles and even may seem valid, but there's lying a big bug that you can't validate inside a function.&lt;/p&gt;

&lt;p&gt;By narrowing types, we make invalid calls impossible at compile time. For example, we can introduce semantic type &lt;code&gt;Dp&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="nd"&gt;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;raw&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @param value The width in density-independent pixels. 
 */&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setWidth&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="nc"&gt;Dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&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 when the &lt;code&gt;value&lt;/code&gt; argument is passed, we make sure that the call-site is well-aware of measurement unit we expect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;I'm not the only one who's occasionally too lazy to check or write documentation, right? But don't blame me — like a wise man once said (Robert C. Martin, in Clean Code):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The proper use of comments is to compensate for our failure to express ourself in code. Note that I used the word failure. I meant it. Comments are always failures. We must have them because we cannot always figure out how to express ourselves without them, but their use is not a cause for celebration. So when you find yourself in a position where you need to write a comment, think it through and see whether there isn’t some way to turn the tables and express yourself in code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Semantic typing doesn't just let you wrap a value in a class — it lets you &lt;strong&gt;document the type itself&lt;/strong&gt;. You convey exactly what kind of data it expects, without repeating that information in every function that uses it. Self-documenting code, done right.&lt;/p&gt;

&lt;p&gt;Coming back to our 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="cm"&gt;/**
 * @param value The width in density-independent pixels. 
 */&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setWidth&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="nc"&gt;Dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&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;You might find the documentation a little funny — as if you could just pass something else instead of &lt;code&gt;Dp&lt;/code&gt;. But besides making that comment almost obsolete, we also eliminate another problem: &lt;strong&gt;documentation duplication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think about it: if we have &lt;code&gt;setWidth&lt;/code&gt;, we probably also have &lt;code&gt;setHeight&lt;/code&gt;, &lt;code&gt;setSpacing&lt;/code&gt;, and similar functions. Without semantic typing, the same documentation gets copied everywhere — or worse, it's incomplete, missing or outdated entirely somewhere, because somebody was lazy or simply forgot. Then anyone reading the code has to guess the expected input based on other parts of the code they might not even be calling. With a narrowed type, that guesswork disappears — you just reuse the type where it's semantically appropriate.&lt;/p&gt;

&lt;p&gt;But there's more. Beyond "wrapping" data, you need to consider &lt;strong&gt;identity and semantics&lt;/strong&gt;. You're not just slapping a random name on it, like the one GitHub suggested for your repo, you're giving it real meaning. A type should stand alone, without requiring extra context, so you don't end up with something like this:&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;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp&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="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// vs&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&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;Where you're trying to express the unit of measurement through parameter naming. And documenting function instead don't do better job either. Who forces you or me to check the parameter name? What if after a long day of work I falsely assumed that it accepts raw pixels?&lt;/p&gt;

&lt;p&gt;Even in the second example, you could still mess up the units, effectively making it another &lt;code&gt;Int&lt;/code&gt;, but just wrapped and with a little less flaws (we guarded it from being just a random &lt;code&gt;Int&lt;/code&gt; to a random &lt;code&gt;Size&lt;/code&gt;). And the old &lt;code&gt;Int&lt;/code&gt; version? Totally generic — it tells us something and nothing at the same time. semantic typing should and forces the code to &lt;strong&gt;say what it means&lt;/strong&gt;, loud and clear. That what makes it powerful and self-documentable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validation
&lt;/h3&gt;

&lt;p&gt;Another benefit of introducing semantic typing — is &lt;strong&gt;validation&lt;/strong&gt;. We're circling back to self-documentation, but this time the focus is on &lt;strong&gt;reuse&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;In our previous examples, every function had to validate density-independent pixels individually to fail fast (hopefully we were doing it, right?) and avoid hidden bugs:&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;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp&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="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;dp&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"dp cannot be negative"&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;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp&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="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;dp&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"dp cannot be negative"&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;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can move that logic into the type itself, making it a true subset type, just as we defined earlier:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;raw&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="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;dp&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"dp cannot be negative"&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;Notice how this eliminates repeated validation, guarantees correctness everywhere, and clearly documents the intended constraints.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
You can, of course, introduce more robust validation to be more type-safe depending on where and how it's used; Since this article is most and foremost about Semantic Typing rather than Validation, you can read about validation separately &lt;a href="https://dev.to/y9vad9/failures-we-dont-model-correctly-2hbo"&gt;here&lt;/a&gt;. 😄&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We could have actually merged &lt;strong&gt;Documentation&lt;/strong&gt; and &lt;strong&gt;Validation&lt;/strong&gt; into one section — but I kept them separate deliberately. Why? Because some validation challenges are more subtle, and using a semantic type highlights them perfectly.&lt;/p&gt;

&lt;p&gt;Take a real-world example we hinted at in the introduction:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailAddress&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;raw&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="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Email validation has always been a headache. Different parts of a system may enforce &lt;strong&gt;different rules&lt;/strong&gt;, often without anyone noticing. One function might just check that the string contains @. Another might use a simplified StackOverflow regex. A third might try to implement the full RFC standard.&lt;/p&gt;

&lt;p&gt;The result? Three functions that &lt;strong&gt;semantically expect the same input&lt;/strong&gt; may behave differently: one accepts a string, others reject it. Bugs like this are subtle, hard to catch, and annoying to debug.&lt;/p&gt;

&lt;p&gt;By narrowing it to a semantic type, you &lt;strong&gt;centralize the constraint&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any &lt;code&gt;EmailAddress&lt;/code&gt; instance is guaranteed to be valid according to your rules. And they remain the same.&lt;/li&gt;
&lt;li&gt;Consumers of the type &lt;strong&gt;don't need to repeat validation&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The compiler enforces that only valid data flows through your system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Validation + self-documentation + compile-time safety: this is the power of semantic typing in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it?
&lt;/h2&gt;

&lt;p&gt;With the examples in mind we may proceed to create a rule when we should actually use semantic typing and when most likely not.&lt;/p&gt;

&lt;p&gt;Depending on your project or application-level code requirements, it may differ, but not too much. We definitely don't want to over-engineer!&lt;/p&gt;

&lt;h3&gt;
  
  
  Application code
&lt;/h3&gt;

&lt;p&gt;Let's start with what Kotlin focuses on first — application-level code. Usually, in our projects we apply architecture patterns such as Clean Architecture, Domain-Driven Design, and sometimes even Hexagonal Architecture. All of them share a common layer — the domain — which derives in some form from DDD (check my article if you're not familiar with it: &lt;a href="https://dev.to/y9vad9/digging-deep-to-find-the-right-balance-between-ddd-clean-and-hexagonal-architectures-4dnn"&gt;Digging Deep to Find the Right Balance Between DDD, Clean and Hexagonal Architectures&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In the context of domain layers, we typically enforce business rules, constraints, and overall core business logic, isolated from infrastructure or application concerns. Since domains are intended to reflect the language of domain experts, it's usually beneficial to introduce &lt;strong&gt;semantic typing&lt;/strong&gt;, and more specifically, &lt;strong&gt;Value Objects&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's take an example — a User aggregate:&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;User&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;Int&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;email&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;val&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;val&lt;/span&gt; &lt;span class="py"&gt;bio&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;To enforce business rules and constraints, you could go down a couple of paths. Let's start with the &lt;strong&gt;opposite of semantic typing&lt;/strong&gt;, just to see what can go wrong:&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;User&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;Int&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;email&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;val&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;val&lt;/span&gt; &lt;span class="py"&gt;bio&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="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;id&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailRegex&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="n"&gt;length&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;50&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;bio&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;bio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;500&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 not an invalid approach, but let's reason about what can go wrong or feel suboptimal here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplication&lt;/strong&gt; is one obvious problem. It's common to see multiple representations of the same entity (in this case, &lt;code&gt;User&lt;/code&gt;) with the same properties, which forces you to duplicate validation:&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;PublicUser&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;Int&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;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;val&lt;/span&gt; &lt;span class="py"&gt;bio&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;id&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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="n"&gt;length&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;50&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;bio&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;bio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;500&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, User contains full information for the owner, while &lt;code&gt;PublicUser&lt;/code&gt; is returned to everyone else without the email. Aside from feeling unease of duplication, the rules and constraints tend to change over time, making this decentralized approach fragile and prone to being forgotten.&lt;/p&gt;

&lt;p&gt;The solution? Introduce &lt;strong&gt;Value Objects&lt;/strong&gt; with semantic typing. Each property becomes a type that &lt;strong&gt;encodes its constraints&lt;/strong&gt;, centralizing validation and making your domain model self-documenting:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserId&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;rawInt&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="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;raw&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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="nd"&gt;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Email&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;rawString&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="err"&gt; &lt;/span&gt; &lt;span class="err"&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;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailRegex&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="nd"&gt;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserName&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;rawString&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="err"&gt; &lt;/span&gt; &lt;span class="err"&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;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;50&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="nd"&gt;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bio&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;rawString&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="err"&gt; &lt;/span&gt; &lt;span class="err"&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;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;500&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;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;val&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;UserName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bio&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 approach, &lt;strong&gt;validation is centralized&lt;/strong&gt;, duplication disappears, and your domain objects become &lt;strong&gt;self-explanatory and safer by design&lt;/strong&gt;. Each property now carries semantic meaning, and any changes to rules need to be made only in the Value Object itself, not scattered across multiple classes.&lt;/p&gt;

&lt;p&gt;Let's also consider a more complex structure instead of just wrapping primitives. Imagine we have &lt;strong&gt;two bounded contexts&lt;/strong&gt; (features):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One is responsible for the &lt;strong&gt;shopping cart&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;The other is responsible for &lt;strong&gt;payments&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both features share the same underlying data — selected products — but the &lt;strong&gt;business rules differ&lt;/strong&gt;. For the shopping cart, it's valid to have an empty cart while the user is still selecting products. For the payment feature, however, it's crucial that the cart is &lt;strong&gt;not empty&lt;/strong&gt; — the user must arrive with at least one selected product:&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;PaymentCart&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;userId&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;products&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;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;userId&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// must not be empty for payment&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;Meanwhile, you might have something like this instead:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductSelection&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;raw&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;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// can be something more robust with factory pattern and type-safe result for outer layer, as it's part of user input&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;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// enforces non-empty list&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;PaymentCart&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;userId&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ProductSelection&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 type itself guarantees the constraint: &lt;strong&gt;you cannot construct a ProductSelection with an empty list&lt;/strong&gt;, eliminating the risk of forgetting validation if we have, for example, more than one aggregate, like &lt;code&gt;PaymentCart&lt;/code&gt; and &lt;code&gt;Bill&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We can also give ProductSelection additional behavior. For instance, if certain campaigns restrict delivery costs depending on the products purchased:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductSelection&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;raw&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;Product&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;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;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotEmpty&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;specialProducts&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;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* some filtering logic */&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;PaymentCart&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;userId&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ProductSelection&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;deliveryCosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Money&lt;/span&gt;
        &lt;span class="k"&gt;get&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;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;specialProducts&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;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ZERO&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cm"&gt;/* normal cost */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While you could technically implement this logic inside the aggregate itself, &lt;strong&gt;localizing responsibilities in the Value Object (semantic type) simplifies the code&lt;/strong&gt;, makes the API more explicit, and keeps the aggregate focused on core domain logic. And why should it apply only to code in the domain layer?&lt;/p&gt;

&lt;h3&gt;
  
  
  Library code
&lt;/h3&gt;

&lt;p&gt;Even though library code is often an additional component of any application — meaning it doesn't always follow the strict rules, conventions, or approaches typical of application code (aside from generic best practices) — I would still strongly recommend using the same approach to narrow your types almost everywhere.&lt;/p&gt;

&lt;p&gt;Application code is usually written with the goal of minimizing upfront cost. This means that while it can be worthwhile to apply strict modeling in specific parts (like the domain layer) to reduce future testing and maintenance overhead, maintaining the same strict model everywhere often seems unnecessary or excessive. However, in my opinion, libraries don't fall under this "cost-saving" rationale.&lt;/p&gt;

&lt;p&gt;Consider the following 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;@Serializable&lt;/span&gt;
&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&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;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcVersion&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;JsonRpcRequestId&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcVersion&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;JsonRpcRequestId&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;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&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="nc"&gt;JsonRpcResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Error&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;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcVersion&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;JsonRpcRequestId&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;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcResponseError&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="nd"&gt;@Serializable&lt;/span&gt;
&lt;span class="nd"&gt;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcVersion&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;string&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="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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;V2_0&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2.0"&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="nd"&gt;@Serializable&lt;/span&gt;
&lt;span class="nd"&gt;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JsonRpcRequestId&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;jsonElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JsonElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;jsonElement&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;JsonNull&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;jsonElement&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;JsonPrimitive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="s"&gt;"JSON-RPC ID must be a primitive or null"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; 
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;jsonElement&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;JsonPrimitive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;jsonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isString&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;jsonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intOrNull&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;jsonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;longOrNull&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;jsonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doubleOrNull&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="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="s"&gt;"JSON-RPC ID must be a string or number"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; 
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;isString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonElement&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;JsonPrimitive&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;jsonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isString&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// ... other&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;asString&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="k"&gt;get&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;isString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonElement&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;JsonPrimitive&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// ... other&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ... other classes in the same way&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, we put semantic types almost everywhere! Let's reason about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;JsonRpcRequestId&lt;/code&gt; is associated with the one that should be presented in the initial request, meaning that it beneficiary for both library code (less error-prone due to human factor). In addition it represents a real concept derived from the specification.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JsonRpcVersion&lt;/code&gt; is also reused, but it's not that common to be used and overall the reasoning is different — it first of all represents the concept and makes type self-explanatory by referencing to the JsonRpc Spec. The code that validate it now knows that they accept &lt;code&gt;JsonRpcVersion&lt;/code&gt; and not something else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end, I would say that for most cases it's most likely that you need to define semantic types to draw clear boundaries for yours and others sake, but with exceptions about which we are going to talk about later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-world examples
&lt;/h3&gt;

&lt;p&gt;Now let's hop onto real-world examples to see where we apply this technique, to reason about them and extend our context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Standard library
&lt;/h3&gt;

&lt;p&gt;Take &lt;code&gt;Thread.sleep()&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="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// five what? milliseconds? seconds? who knows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;measurement unit is not explicit&lt;/strong&gt;. You're forced to guess and to know it, and manually convert if you:&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="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// now it's five minutes, maybe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some developers might wrap it in a &lt;code&gt;java.time.Duration&lt;/code&gt; and convert to milliseconds in place, while others just write raw numbers and multiply it. Nothing &lt;strong&gt;forces&lt;/strong&gt; consistency. Every developer writes what they feel is right, and the code becomes unconventional, almost random. One mistake in conversion, and the behavior silently breaks. And code complexity? It goes rough.&lt;/p&gt;

&lt;p&gt;Semantic typing solves this problem elegantly. For example, with Kotlin's Duration type and well-designed API:&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;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;

&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minutes&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;Much easier to read, isn't it?&lt;/p&gt;

&lt;h3&gt;
  
  
  Android Views vs Jetpack Compose
&lt;/h3&gt;

&lt;p&gt;In classic Android Views, setting sizes often looks like this:&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;view&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ImageView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layoutParams&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ViewGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LayoutParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// what are these numbers? px or dp?&lt;/span&gt;

&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layoutParams&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ViewGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LayoutParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nc"&gt;ViewGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LayoutParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MATCH_PARENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nc"&gt;ViewGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LayoutParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WRAP_CONTENT&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first problem is that, at first glance and without reading documentation, you &lt;strong&gt;cannot reason about the input parameters&lt;/strong&gt; of &lt;code&gt;layoutParams&lt;/code&gt; — is that 200 in pixels? dp? Who knows? Sure, in the Android Framework most people are aware of it, thanks to rich documentation and common ecosystem knowledge. But if you're building your own library or application, you &lt;strong&gt;don't get the same safety net&lt;/strong&gt;. Your code probably isn't well-documented, and people rarely memorize internal details. This leads to false assumptions, subtle bugs, and frustrating debugging sessions. Also, remember what once a wise man said?&lt;/p&gt;

&lt;p&gt;The second problem is that, just by looking at the &lt;code&gt;ImageView&lt;/code&gt; class, &lt;strong&gt;you have no idea that these constants exist&lt;/strong&gt; or that they belong specifically to &lt;code&gt;ViewGroup.LayoutParams&lt;/code&gt;. Even though &lt;code&gt;ImageView&lt;/code&gt; don't even extend that class, you're expected to know the "magic" behind &lt;code&gt;MATCH_PARENT&lt;/code&gt; and &lt;code&gt;WRAP_CONTENT&lt;/code&gt;. For any newcomer, this is a nightmare — code that's technically correct but completely opaque.&lt;/p&gt;

&lt;p&gt;Now look at Jetpack Compose:&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="nc"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxWidth&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="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compose solves the same problem through &lt;strong&gt;semantic typing&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Dp&lt;/code&gt; wraps raw numbers, preventing accidental misuse.&lt;/li&gt;
&lt;li&gt;Intrinsic sizes like &lt;code&gt;fillMaxWidth()&lt;/code&gt; are &lt;strong&gt;typed functions&lt;/strong&gt;, not magic numbers.&lt;/li&gt;
&lt;li&gt;The compiler enforces correctness — no more guessing about units or constants.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is striking: Compose &lt;strong&gt;pushes correctness into the type system&lt;/strong&gt;, making code &lt;strong&gt;self-documenting, safer, and easier to reason about&lt;/strong&gt;, while classic Views rely on conventions and mental checks.&lt;/p&gt;

&lt;p&gt;This is a perfect real-world illustration of why &lt;strong&gt;semantic typing matters&lt;/strong&gt;: it reduces mental overhead, prevents misuse, and communicates intent directly through the types.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to use it?
&lt;/h2&gt;

&lt;p&gt;Think of semantic typing in context of value objects from DDD – as first and foremost real-world concepts. In most cases — whether you're building an application or a library — the types you define should represent things that exist or make sense &lt;strong&gt;in the world your software models&lt;/strong&gt;. Most values we work with &lt;em&gt;do&lt;/em&gt; correspond to such real-life concepts. However, it's easy to get carried away and start modeling &lt;em&gt;rules&lt;/em&gt; instead of &lt;em&gt;ideas&lt;/em&gt; — which leads to types that clutter your codebase rather than clarify it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modeling inputs
&lt;/h3&gt;

&lt;p&gt;Let's start with something simple.&lt;/p&gt;

&lt;p&gt;Take &lt;code&gt;PositiveNumber&lt;/code&gt;. At first glance, it might seem useful: it enforces a constraint and could be reused in many places. But &lt;strong&gt;is it a concept?&lt;/strong&gt; Not really.&lt;/p&gt;

&lt;p&gt;You may say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;But why bother? Didn't you say that it's an advantage of semantic typing — that I can centralize validation? Isn't that exactly what variable names are for? If I name it &lt;code&gt;price: PositiveNumber&lt;/code&gt; or &lt;code&gt;distance: PositiveNumber&lt;/code&gt;, doesn't that clarify everything?&lt;/strong&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It might — &lt;strong&gt;for a moment.&lt;/strong&gt; But naming alone isn't enough in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Naming doesn't travel.&lt;/strong&gt; Once the value leaves the scope where it was created, the meaning is often lost. You can't rely on every developer — across every file, layer, or feature — to maintain that clarity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;People reuse types for convenience.&lt;/strong&gt; If &lt;code&gt;PositiveNumber&lt;/code&gt; works, someone will use it for price, distance, item count, or whatever else they find that fits. And once a type is reused generically, its original intent disappears. You don't want to create such a "generic" trend, otherwise what's the point of introducing a semantic type? &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews and onboarding suffer.&lt;/strong&gt; When the same type is used for many things, it gets harder to reason about code, harder to trace bugs, and harder for newcomers to understand what a value &lt;em&gt;actually&lt;/em&gt; represents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bugs creep in silently.&lt;/strong&gt; You might never notice that someone accidentally passed a "length in kilometers" where "price in euros" was expected — because the type &lt;code&gt;PositiveNumber&lt;/code&gt; accepts both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the problem isn't that names are bad — it's that &lt;strong&gt;they're too easy to misunderstand, too easy to misuse, and too easy to forget.&lt;/strong&gt; If something &lt;em&gt;means&lt;/em&gt; price, make it a &lt;code&gt;Price&lt;/code&gt;. That meaning should survive refactors, boundaries, and time. Thus, the simplest reason to avoid it, is because &lt;strong&gt;it's too genetic&lt;/strong&gt;. In reality, &lt;strong&gt;Validation&lt;/strong&gt; is just a handy outcome and not the reason to introduce semantic typing.&lt;/p&gt;

&lt;p&gt;You may take a natural language as a reference and think whether you would express your thoughts to a stranger in the way how you phrase your semantic type. But, honestly, because it's too subjective, for me it was not a fit for a rule.&lt;/p&gt;

&lt;p&gt;Therefore we can come up with more intuitive and more explicit rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Type constraints should not drive your semantic type in any sense – in naming or in existence.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;But what if &lt;code&gt;PositiveNumber&lt;/code&gt; was in mathematical context?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It may seem that &lt;code&gt;PositiveNumber&lt;/code&gt; &lt;strong&gt;can still help&lt;/strong&gt; with preventing mistakes in our code, for example in context of mathematic operation that uses division:&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;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this looks neat — you're encoding the domain rule "no negatives allowed" right into the type in the context that "allow" such concept.&lt;/p&gt;

&lt;p&gt;But is it a valid concept within such domain? I would maybe say "yes", but would most definitely avoid it subconsciously. Why?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The meaning in such case depends on the operation, not the input&lt;/strong&gt;. Take this:&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;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;operation&lt;/strong&gt; (sqrt) is what &lt;em&gt;defines&lt;/em&gt; the constraint — that the input must be non-negative. The input itself, as a number, &lt;strong&gt;does not inherently carry that meaning&lt;/strong&gt;. &lt;strong&gt;Without the operation context&lt;/strong&gt;, the &lt;code&gt;PositiveNumber&lt;/code&gt; type &lt;strong&gt;is just a number with a constraint&lt;/strong&gt; — but what does it &lt;em&gt;really&lt;/em&gt; mean on its own?&lt;/p&gt;

&lt;p&gt;On top of that, many people casually associate "positive number" with "non-negative", forgetting that zero is &lt;strong&gt;not&lt;/strong&gt; positive nor negative, nor both at the same time. This subtle confusion can lead to seemingly logical code that is actually incorrect. Conceptually, look at these two operations:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PositiveNumber&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Value must be strictly positive"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &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;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;ln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine trying to call these functions:&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;zero&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// ❌ throws IllegalArgumentException&lt;/span&gt;
&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// ❌ fails, but mathematically sqrt(0) is valid&lt;/span&gt;
&lt;span class="nf"&gt;ln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PositiveNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// ✅ works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both functions appear to operate on "positive numbers", but the precise constraints differ: &lt;code&gt;sqrt&lt;/code&gt; accepts zero (non-negative), whereas ln requires strictly positive values. If you try to reuse a single &lt;code&gt;PositiveNumber&lt;/code&gt; type for both, one of the operations will either reject valid input or allow invalid input. &lt;strong&gt;Semantic typing must respect the operation context&lt;/strong&gt;, not just the nominal value.&lt;/p&gt;

&lt;p&gt;While the example is really specific, it should give you a great example of possible problems of misusing too general types, making it a reason not to introduce them.&lt;/p&gt;

&lt;p&gt;And original problem? You've pushed the constraint to the input, but in practice, it's the &lt;em&gt;operation&lt;/em&gt; that defines the constraint. You could instead say:&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;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// it would also be more than okay to have here just a guard&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;a&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;sqrtOrNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Double&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;a&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&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;ln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Double&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;a&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;lnOrNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Double&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;a&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;
    &lt;span class="k"&gt;return&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;Prefer duplication over a wrong abstraction for cases where you can't come up with a real-world concept.&lt;/p&gt;

&lt;p&gt;Thus I would make even more explicit rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Operation constraints should not drive your semantic type in any sense – in naming or in existence.&lt;/strong&gt; If you can't come up with reasonable name without mentioning constraint that would fit this rule – you most definitely don't need a "semantic type".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another thing to mention is that we may, for example, have a &lt;code&gt;Dp&lt;/code&gt; class that represents density-independent pixels and still have restrictions defined by operation (usage context):&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;setDividerHeight&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="nc"&gt;Dp&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;value&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="nc"&gt;Dp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ZERO&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;Introducing some kind of &lt;code&gt;DividerHeightDp&lt;/code&gt; with such condition is overkill and basically falls under &lt;code&gt;PositiveNumber&lt;/code&gt; problem.&lt;/p&gt;

&lt;p&gt;But you may say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But what if we allow &lt;code&gt;PositiveNumber&lt;/code&gt; just because it enforces a helpful constraint and I don't have any ambiguity in my code for it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's maybe great! But in reality where do we draw the line?&lt;/p&gt;

&lt;p&gt;Why stop there?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why not also have &lt;code&gt;NegativeNumber&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NonZeroNumber&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NonEmptyList&lt;/code&gt;, &lt;code&gt;OneElementList&lt;/code&gt;, &lt;code&gt;AtLeastThreeItemsList&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ShortString&lt;/code&gt;, &lt;code&gt;UppercaseString&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am absolutely sure that at least some of given options you would not like to see in your code, but without a clear rule it's easy to fall to some extent for that.&lt;/p&gt;

&lt;p&gt;And once you go down this path, &lt;strong&gt;any rule becomes a candidate for a semantic type&lt;/strong&gt; — and that's the problem. You're no longer modeling &lt;em&gt;concepts&lt;/em&gt; — you're modeling &lt;em&gt;conditions&lt;/em&gt;. And conditions are not identities.&lt;/p&gt;

&lt;p&gt;At that point, you're not designing a rich domain model. You're building a constraint-validation library shaped like your domain. And that's a completely different goal — one that introduces more complexity than clarity.&lt;/p&gt;

&lt;p&gt;Thus, let's come up with the reasonable rule without relying on any of ours "internal feelings":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If the semantic type cannot stand alone as a meaningful concept without its underlying type, it is likely a bad semantic type.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's also why introducing &lt;code&gt;DividerHeight(value: Dp)&lt;/code&gt; instead of &lt;code&gt;DividerHeightDp(value: Int)&lt;/code&gt; – is a bad idea. It will just be unwrapped every time, creating a lot of unnecessary boilerplate in our code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modeling outputs
&lt;/h3&gt;

&lt;p&gt;We've spent enough time talking about semantic typing for &lt;strong&gt;inputs&lt;/strong&gt; — types that wrap data &lt;em&gt;going into&lt;/em&gt; our domain. But what about the &lt;strong&gt;opposite direction&lt;/strong&gt;? Can semantic types also be useful for modeling &lt;strong&gt;outputs&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Let's consider the following 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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;textContents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FileTextContents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why it's bad? First of all it tells the source! But wait.. why is this a problem?&lt;/p&gt;

&lt;p&gt;Let's add similar variants to understand this problem more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FileTextContents&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HttpTextContents&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserInputTextContents&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;..where do we stop?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's perfectly fine to have the following:&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;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileSize&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;bytesAmount&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bytesAmount&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;kilobytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;..&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;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FileSize&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;..&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;This example isn't really about telling the source of data, but rather about a functional concept in its own right — &lt;strong&gt;file size&lt;/strong&gt;. The idea of FileSize &lt;strong&gt;represents an actual concept&lt;/strong&gt;: an amount of bytes named according to our domain. Whether it came from a file, a memory buffer, or a network stream — its &lt;em&gt;meaning&lt;/em&gt; remains the same. FileSize reflects a concept that stands on its own. &lt;/p&gt;

&lt;p&gt;Thus, naming it like &lt;code&gt;DownloadedFileSize&lt;/code&gt; makes no sense. Ask yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the &lt;em&gt;source&lt;/em&gt; of this string truly matter to the business logic?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Introducing something like this might feel "descriptive", but in reality, you're hardwiring context that doesn't belong to the core concept. You're taking a neutral, reusable concept — like "text" or "size" — and narrowing its scope unnecessarily. This creates fragmentation, confusion, and needless ceremony in the API of converting one into another for no reason. And I have a feeling that we don't like mapping one into another 🌚&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Name your concept narrowly enough to be understood at a glance, but without exaggeration.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Semantic typing, and value objects in particular, are powerful tools for pushing intent, validation and domain meaning into types. Use them when a concept is part of your domain language, when invariants must be centralized, or when an API boundary needs clearer intent. Avoid them when the constraint is defined by an operation rather than the concept itself, or when a type would be overly generic and thus lose meaning. Establish simple team rules so the benefits scale without creating unnecessary ceremony.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>architecture</category>
      <category>programming</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Package naming nobody cares about (but should)</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Mon, 15 Sep 2025 09:56:54 +0000</pubDate>
      <link>https://dev.to/y9vad9/package-naming-nobody-cares-about-but-should-3i5</link>
      <guid>https://dev.to/y9vad9/package-naming-nobody-cares-about-but-should-3i5</guid>
      <description>&lt;p&gt;Package naming and organization are fundamental aspects of writing maintainable code. How we choose to group files and modules impacts not only readability but also the ease of navigation and future development.&lt;/p&gt;

&lt;p&gt;In this article, we'll briefly explore how packages are used, trying to create some rules and give some reasoning about when it's a bad idea to create a separate package and when it's not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Package, Really?
&lt;/h2&gt;

&lt;p&gt;A package is one of the first concepts you encounter right after writing your basic "Hello World" program in Java or Kotlin. The simplest and yet misleading way to describe it is just as a folder structure used to organize code and prevent naming conflicts.&lt;/p&gt;

&lt;p&gt;And while it's partially true, it might give you the wrong perspective on when to actually use it. But let's stick to some kind of definition:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Package name&lt;/strong&gt; in Kotlin and Java is a &lt;strong&gt;namespace&lt;/strong&gt; used to organize a set of related types (classes, interfaces, enums, etc.) into a cohesive unit. It serves a dual purpose: providing logical structure to a codebase and preventing naming conflicts in large-scale applications or when integrating third-party libraries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Taking into account our previous "simple" explanation, it's true that packages help us avoid naming conflicts, since it's rare for class names to be 100% unique. &lt;/p&gt;

&lt;h2&gt;
  
  
  Packages as Folders
&lt;/h2&gt;

&lt;p&gt;Despite the benefits that packages provide, there's a significant downside: due to how packages are implemented in Java and Kotlin — and how IDEs handle them — they don't fully serve as true namespaces, even though they ideally should. For this reason, we tend to name our classes in a way that is expressive enough on its own, without needing to rely on the package name. For example:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UserAnalyticsReport&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OrderRepository&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserFileStorageService&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though these classes may be placed in distinct logical packages, like:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;com.example.user.UserAnalyticsReport&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;com.example.order.OrderRepository&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;com.example.user.UserFileStorageService&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;which theoretically could provide differentiation if we could write something like:&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;import&lt;/span&gt; &lt;span class="nn"&gt;com.example.user&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;report&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;AnalyticsReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately (and fortunately), we're not in C++ (which isn't necessarily a good or a bad thing). Since most people rely on IDEs' auto-importing and auto-completion features, they rarely deal directly with packages and therefore often don't put much thought into package structure.&lt;/p&gt;

&lt;p&gt;This approach can lead to a flawed mindset when designing your own package structure — you start thinking in terms of grouping files rather than defining meaningful namespaces and boundaries, leading to unclear responsibilities. The result is often a directory full of classes, organized by location rather than purpose.&lt;/p&gt;

&lt;p&gt;Even though it makes you feel good about things being "organized", it usually leads to a lot of problems. This is the wrong mental model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packages as Namespaces
&lt;/h2&gt;

&lt;p&gt;A better mental model is to think of packages as &lt;strong&gt;semantic boundaries&lt;/strong&gt; — they tell you what part of the system you're looking at and, ideally, what that code is responsible for.&lt;/p&gt;

&lt;p&gt;But let's be honest: most people treat packages as mere folders, often because they're intimidated by the number of files within a package. This "drawer" mentality keeps us from thinking deeper about &lt;strong&gt;why&lt;/strong&gt; we group code the way we do. I struggled with this mindset myself for a long time.&lt;/p&gt;

&lt;p&gt;When you start treating packages as true namespaces, several beneficial things happen:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The package itself becomes part of the documentation&lt;/strong&gt; — just by looking at where a class lives, you get a sense of what it does and what it's responsible for.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Related functionality naturally stays together&lt;/strong&gt;, while unrelated code stays apart. It also makes you think more about "what is it responsible for?"/"Who is the owner of this particular class/function"?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beyond that, many common software design headaches simply vanish. One of the biggest culprits in large codebases is the overuse or misapplication of generic "category" packages, such as &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;dto&lt;/code&gt;, or &lt;code&gt;entity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, consider a large project where multiple teams work on user-related features. You might see packages like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.example.user.model.profile  
com.example.user.profile.model  
com.example.user.utils.profile  
com.example.user.profile.utils  
com.example.user.dto.profile  
com.example.user.profile.dto  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(It's even more feasible in multi-modular projects, especially when multiple teams involved)!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here, the "profile" concept is scattered across multiple packages and subpackages, often with duplicated or reversed naming orders. Sometimes "profile" isn't even a standalone domain concept but just a part of the user aggregate — yet it's treated as its own package to reduce the number of files in &lt;code&gt;user.model&lt;/code&gt;. Teams unintentionally recreate the same logical groupings multiple times because they don't realize an equivalent package already exists somewhere else. This often results in inconsistent package naming based on personal preference rather than clear conventions, which makes onboarding new team members more difficult and slows down the process. Never to be asked again, “Why is it not here, but here?”.&lt;/p&gt;

&lt;p&gt;While this example is simplified, you will inevitably encounter this issue in real projects — especially if you maintain a library where backward compatibility across versions is critical. In such cases, inconsistent or unclear package structures can introduce long-term maintenance headaches and increase the risk of breaking changes.&lt;/p&gt;

&lt;p&gt;This situation quickly devolves into a maze where:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You spend more time guessing where something might live rather than understanding what it does.
&lt;/li&gt;
&lt;li&gt;Terminology becomes inconsistent across teams — some call certain classes "models", others call the same or similar concepts "entities" or "DTOs".
&lt;/li&gt;
&lt;li&gt;You get tangled in redundant or conflicting packages like &lt;code&gt;user.utils.profile&lt;/code&gt; vs &lt;code&gt;user.profile.utils&lt;/code&gt;, with no clear ownership or responsibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In such cases, generic package names like &lt;code&gt;utils&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, or &lt;code&gt;dto&lt;/code&gt; become meaningless labels. Instead of helping you find code based on its responsibility, they force you to rely purely on terminology — and since different people or teams often use these terms differently, this vocabulary can change or conflict over time. This makes understanding the codebase more dependent on mastering a shifting and ambiguous glossary rather than on intuitive architectural boundaries.&lt;/p&gt;

&lt;p&gt;By contrast, when packages represent &lt;strong&gt;responsibility&lt;/strong&gt; rather than just file categories or vague groupings, the codebase becomes more navigable, easier to understand, and more resilient to team or terminology changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  What deserves a namespace?
&lt;/h3&gt;

&lt;p&gt;While it's relatively easy to reason about not creating a &lt;code&gt;.model&lt;/code&gt;, &lt;code&gt;.util&lt;/code&gt;, &lt;code&gt;.impl&lt;/code&gt;, and so on, there is a big elephant in the room that remains unanswered — how to determine what deserves and what does not deserve a namespace?&lt;/p&gt;

&lt;p&gt;Let's create an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.example.user  
 ├─ User.kt  
 ├─ UserId.kt  
 ├─ UserFactory.kt  
 ├─ settings  
 │   ├─ UserSettings.kt  
 │   ├─ NotificationPreferences.kt  
 │   └─ PrivacyOptions.kt  
 ├─ profile  
 │   ├─ UserProfile.kt  
 │   ├─ ProfilePicture.kt  
 │   └─ Bio.kt  
 ├─ security  
 │   ├─ Password.kt  
 │   ├─ SecurityQuestions.kt  
 │   └─ TwoFactorAuth.kt  
 └─ utils  
     ├─ UserValidators.kt  
     └─ UserMappers.kt  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;For the sake of simplicity, we will avoid mentioning any layering in our structure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While collapsing all folders might make the structure look clean and minimal, it often turns into a long, meandering chain of subpackages with no clear purpose. Take &lt;code&gt;profile&lt;/code&gt; for example — it may appear as a cohesive, independent unit, but we should pause and ask ourselves:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does &lt;code&gt;.profile&lt;/code&gt; make any sense without a user concept?
&lt;/li&gt;
&lt;li&gt;Does the actual usage of entities within &lt;code&gt;.profile&lt;/code&gt; make sense outside the &lt;code&gt;user&lt;/code&gt; package (for example, is it only wrapped inside another class and managed by)? &lt;em&gt;(mappers don't count 😁)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Does this package provide a meaningful black box with its own API, used by more than just user? Or is it something that only makes sense inside User — for example, a UserProfile that never exists independently, is always unwrapped from User, and cannot be accessed without it?&lt;/li&gt;
&lt;li&gt;If I have to change something in this group of classes, will I usually change the others too?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If any of the answers are yes&lt;/strong&gt; → they most likely belong together in the same package.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;If all are no&lt;/strong&gt; → maybe it deserves its own package.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;.common&lt;/code&gt; / &lt;code&gt;.core&lt;/code&gt; packages
&lt;/h4&gt;

&lt;p&gt;Another edge case that I think is worth mentioning is the &lt;code&gt;.common&lt;/code&gt; / &lt;code&gt;.core&lt;/code&gt; packages, which I personally see frequently in codebases. While they seem handy at first, for the most part, their underlying meaning is literally "this is stuff that doesn't fit anywhere else". Thus does not really represent a &lt;strong&gt;cohesive concept&lt;/strong&gt; or a &lt;strong&gt;clear boundary&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The question is simple — why introduce &lt;code&gt;com.example.common&lt;/code&gt; if it should either be localized to the meaningful place or just be put inside &lt;code&gt;com.example&lt;/code&gt;? It's another example of "drawer" mentality, resulting in the situation where, over time, every team member will toss unrelated code there without any thought. And soon the package becomes a grab-bag of everything and nothing at the same time.&lt;/p&gt;

&lt;p&gt;As more and more unrelated code gets dumped there, it turns into the most depended-on part of the system, creating hidden coupling everywhere. That in turn makes refactoring dangerous, since moving anything out feels risky when you’re unsure who relies on it. Over time, the result is architectural erosion: instead of well-defined boundaries, the system gravitates around a bloated God-package at its center.&lt;/p&gt;
&lt;h4&gt;
  
  
  Bonus way of thinking
&lt;/h4&gt;

&lt;p&gt;As an additional way of thinking about it, you can consider an "Aggregate" from DDD as a mental model for identifying a coherent concept that deserves its own namespace. The idea is that a package should represent something that has meaning and utility on its own to the outside world.&lt;/p&gt;

&lt;p&gt;Value objects, domain entities, and events often don't qualify for separate packages because they exist solely to support the aggregate — they have no independent meaning outside of it. Splitting them into their own packages would violate the principle of exposing a clear API to the outside and would create artificial boundaries where nothing makes sense in isolation.&lt;/p&gt;

&lt;p&gt;Everything inside the aggregate is highly coupled, and that coupling is exactly why it belongs together in the same package: it communicates that these elements only have significance in the context of the aggregate.&lt;/p&gt;

&lt;p&gt;This approach ensures packages remain meaningful units of organization, rather than arbitrary folders that obscure the system's true structure. &lt;/p&gt;
&lt;h2&gt;
  
  
  Namespacing on macro level
&lt;/h2&gt;

&lt;p&gt;Until now, we’ve focused on packages at the micro level, like inside a domain. But what about &lt;strong&gt;layers themselves&lt;/strong&gt; — &lt;code&gt;domain&lt;/code&gt;, &lt;code&gt;infrastructure&lt;/code&gt;, &lt;code&gt;presentation&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;On this level, packages signal &lt;strong&gt;architectural boundaries&lt;/strong&gt; and obviously brings a lot of benefits. &lt;code&gt;domain&lt;/code&gt; holds core business logic, &lt;code&gt;presentation&lt;/code&gt; deals with APIs or UI, and &lt;code&gt;infrastructure&lt;/code&gt; wraps technical details like databases or messaging. Importantly, even, for example, infrastructure often contains &lt;strong&gt;self-sufficient logic and types&lt;/strong&gt; to access its logic (ideally), making it a cohesive unit in its own right. The key is that each layer still groups meaningful units rather than acting as a dumping ground. Done right, macro-level namespaces give developers a &lt;strong&gt;quick mental map&lt;/strong&gt; of the system and make dependencies on layers explicit.&lt;/p&gt;

&lt;p&gt;Thus, we can see it as a positive thing rather than a meaningless technical label — these boundaries are, for the most part, self-sufficient.&lt;/p&gt;
&lt;h2&gt;
  
  
  Naming a namespace (package)
&lt;/h2&gt;

&lt;p&gt;Now that we’ve covered when to create a package, let's talk about &lt;strong&gt;how to name it&lt;/strong&gt;. A package should be named after the &lt;strong&gt;concept it represents&lt;/strong&gt;, not the number of things inside it.&lt;/p&gt;

&lt;p&gt;For example, if your code is built around managing a single &lt;code&gt;Order&lt;/code&gt; — its validation, lifecycle, and operations — naming the package &lt;code&gt;orders&lt;/code&gt; is misleading. Plural implies a collection, which doesn't reflect the fact that the package is about the &lt;strong&gt;Order concept&lt;/strong&gt; itself. The correct choice is &lt;code&gt;order&lt;/code&gt;, which communicates that this namespace is about the domain concept, not a list of orders.&lt;/p&gt;

&lt;p&gt;Similarly, don’t name packages &lt;code&gt;errors&lt;/code&gt;, &lt;code&gt;events&lt;/code&gt;, or &lt;code&gt;notifications&lt;/code&gt; just because they contain multiple items of that type. The question to ask is: what &lt;strong&gt;concept or responsibility&lt;/strong&gt; does this package capture? Name it after that concept, not the quantity of instances it holds. This keeps your package names &lt;strong&gt;precise, meaningful, and aligned with the mental model of your system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Can a package name ever be plural? Sure. But in practice, it’s far less common than people assume. Most of the time, the package represents a single concept, not a collection, so as a default, we go with singular.&lt;/p&gt;
&lt;h2&gt;
  
  
  Make the correct focus
&lt;/h2&gt;

&lt;p&gt;It’s not enough to just create meaningful namespaces — the &lt;em&gt;focus&lt;/em&gt; of those namespaces matters just as much.&lt;/p&gt;

&lt;p&gt;Consider two structures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.example.domain   
 └─ user  
     └─ User.kt   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;vs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.example.user   
 └─ domain  
     └─ User.kt   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both look valid, but they communicate very different priorities. The first (&lt;code&gt;com.example.domain.user&lt;/code&gt;) puts the technical layer first and center. The second (&lt;code&gt;com.example.user.domain&lt;/code&gt;) keeps the spotlight on the concept — &lt;code&gt;user&lt;/code&gt; — with the layer being secondary.&lt;/p&gt;

&lt;p&gt;This difference becomes important as the system grows. Not every concept or feature will even have a &lt;code&gt;.domain&lt;/code&gt;, or a &lt;code&gt;.presentation&lt;/code&gt; (for example, authorization most likely does not have a business logic, therefore, does not deserve a &lt;code&gt;.domain&lt;/code&gt;), or whatever layer you expect. That means navigation quickly becomes awkward: you can’t tell what a feature &lt;em&gt;does&lt;/em&gt; or &lt;em&gt;contains&lt;/em&gt;, only that it might sit somewhere under a technical label. The result is uneven hierarchies and folders that feel bloated with layers, while the actual business concepts get buried.&lt;/p&gt;

&lt;p&gt;Modules show the same pattern. At a small scale, having &lt;code&gt;:domain:user&lt;/code&gt; and &lt;code&gt;:domain:task&lt;/code&gt; might look fine. But once you add &lt;code&gt;:application:auth&lt;/code&gt;, &lt;code&gt;:application:user&lt;/code&gt;, &lt;code&gt;:application:task&lt;/code&gt; (while &lt;code&gt;:domain:auth&lt;/code&gt; doesn’t exist) navigation becomes strange. You can’t immediately tell the actual capability of a feature or bounded context: does &lt;code&gt;auth&lt;/code&gt; even have domain logic, or is it just application code? The structure pushes technical boundaries first, while leaving the conceptual boundaries unclear.&lt;/p&gt;

&lt;p&gt;A concept-first approach — &lt;code&gt;com.example.user.domain&lt;/code&gt;, &lt;code&gt;com.example.order.infrastructure&lt;/code&gt; — avoids this problem. You always start with the thing the business cares about, and only then refine by the layer. &lt;/p&gt;

&lt;p&gt;Does it have a &lt;code&gt;domain&lt;/code&gt; or &lt;code&gt;infrastructure&lt;/code&gt; layer? That doesn’t matter — what matters is the concept itself, and only then how it’s implemented internally. &lt;strong&gt;This is especially important because the structure is likely to change over time.&lt;/strong&gt; And what do you do then?&lt;/p&gt;

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

&lt;p&gt;Let's summarise the main points, starting with the micro level:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First of all, my recommendation would be not to go by 'when not to create a package' path, but by 'when to create', meaning that we don't create a separate package by default. Unless we have a strong reason. &lt;/li&gt;
&lt;li&gt;It's justified to have a separate package if we want isolation, for example, when we talk about layered architecture. It's also justified when the package is actually an independent cohesive unit that has an independent meaning. &lt;/li&gt;
&lt;li&gt;We definitely don't create packages like &lt;code&gt;utils&lt;/code&gt;/&lt;code&gt;ext(ensions)&lt;/code&gt;/&lt;code&gt;helpers&lt;/code&gt;/ &lt;code&gt;impl&lt;/code&gt; and alike. &lt;/li&gt;
&lt;li&gt;We don't create packages that do not represent themselves in the general context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the macro level, it’s perfectly fine to have packages like &lt;code&gt;domain&lt;/code&gt; or &lt;code&gt;infrastructure&lt;/code&gt; that reflect the architectural layer you’re working in. In addition, set your priorities correctly — concept is first, then a layer you're operating on.&lt;/p&gt;

&lt;p&gt;Finally, &lt;strong&gt;use singular names by default&lt;/strong&gt;, unless the concept itself is inherently plural, like &lt;code&gt;news&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is example of the structure just for your reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.example
 ├─ user
 │   ├─ domain
 │   │   ├─ User.kt
 │   │   ├─ UserId.kt
 │   │   └─ UserName.kt
 │   ├─ application
 │   │   ├─ UserService.kt / SomeUseCase.kt
 │   │   └─ UserRepository.kt
 │   └─ infrastructure
 │       ├─ database
 │       │   └─ UserDataSource.kt
 │       └─ adapter
 │           └─ UserRepositoryImpl.kt
 │       └─ messaging
 │           └─ UserEventsPublisher.kt
 ├─ order
 │   ├─ domain
 │   └─ ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall, you may apply these rules to other languages outside JVM-ecosystem, such as TypeScript with its modules systems or anything else that has namespacing as a concept.&lt;/p&gt;




&lt;p&gt;More than 5 files within a directory ain't that scary, fellas!  &lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>architecture</category>
      <category>codequality</category>
      <category>java</category>
    </item>
    <item>
      <title>Finding the Right Balance Between DDD, Clean and Hexagonal Architectures</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Sat, 21 Sep 2024 18:16:04 +0000</pubDate>
      <link>https://dev.to/y9vad9/digging-deep-to-find-the-right-balance-between-ddd-clean-and-hexagonal-architectures-4dnn</link>
      <guid>https://dev.to/y9vad9/digging-deep-to-find-the-right-balance-between-ddd-clean-and-hexagonal-architectures-4dnn</guid>
      <description>&lt;p&gt;Choosing the right software architecture is challenging, especially when balancing theory and recommendations from the internet with practical implementation. In this article, I will share my journey and the architectural choices that have worked for me.&lt;/p&gt;

&lt;p&gt;Although the title might suggest I’m here to tell you exactly how to structure your application, that’s not my goal. Instead, I’ll focus on my personal experiences, choices, and the reasoning behind the approaches I’ve taken when building my apps. This doesn't mean you should structure things the same way, but since many of my friends have asked me about it, I thought I’d try to explain the architecture we use in &lt;a href="https://github.com/timemates/app" rel="noopener noreferrer"&gt;TimeMates&lt;/a&gt; (P.S: personal project that I make with my mates; upd: it's almost 2026 and it's still not finished :D).&lt;/p&gt;

&lt;h3&gt;
  
  
  Smart terminology
&lt;/h3&gt;

&lt;p&gt;You're probably already aware of certain terms like &lt;strong&gt;Clean Architecture&lt;/strong&gt;, &lt;strong&gt;DDD&lt;/strong&gt; (domain-driven design), or maybe even &lt;strong&gt;Hexagonal Architecture&lt;/strong&gt;. Perhaps you've read a lot of articles about it all. But as for me, I saw a few problems in most of them – too much theoretical information and little practical information. They might give you small and non-real examples where everything works perfectly, but it never worked for me and never gave me good answers, only increased boilerplate.&lt;/p&gt;

&lt;p&gt;Some of them are almost the same or include each other for the most part and do not conflict with each other in most cases, but many people stop at the specific approach not thinking that it's not the end of the world.&lt;/p&gt;

&lt;p&gt;We'll try to learn the most valuable information from different approaches I take inspiration from, apart from how I particularly build my apps at first. Then we'll come to, particularly my thoughts and implementation. Let's start from the same place most people do while developing Android apps:&lt;/p&gt;

&lt;h4&gt;
  
  
  Clean Architecture
&lt;/h4&gt;

&lt;p&gt;Clean architecture sounds pretty simple – you have specific layers that do only a specific job that should be done only at the specific layer (too much specific I know). Google recommends the following structure, though does not name it "clean" but "modern":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;presentation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;domain&lt;/em&gt; (optional in Google's opinion)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;data&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;presentation layer&lt;/strong&gt; is responsible for your UI, and ideally, its only role is to communicate between the user (who interacts with the UI) and the &lt;strong&gt;domain model&lt;/strong&gt;. The &lt;strong&gt;domain layer&lt;/strong&gt; handles business logic, while the &lt;strong&gt;data layer&lt;/strong&gt; deals with low-level operations like reading and writing to a database.&lt;/p&gt;

&lt;p&gt;Sounds simple, right? However, within this structure lies a big question: According to Google's recommended architecture for apps, why is the &lt;strong&gt;domain layer&lt;/strong&gt; optional? Where is the business logic supposed to go then?&lt;/p&gt;

&lt;p&gt;This idea comes from Google's stance that the domain layer can be skipped in certain cases. In simpler applications, you might find examples where the &lt;strong&gt;business logic&lt;/strong&gt; is placed in the &lt;strong&gt;ViewModel&lt;/strong&gt; (part of the presentation layer). So, what’s the problem with this approach?&lt;/p&gt;

&lt;p&gt;The issue lies in the &lt;strong&gt;MVVM/MVI/MVP&lt;/strong&gt; patterns and the role of the &lt;strong&gt;presentation layer&lt;/strong&gt;. The presentation layer should only handle the &lt;strong&gt;integration with platform details&lt;/strong&gt; and UI-related tasks. In this context, it’s crucial to keep the presentation layer — whether it's using MVVM or any other pattern — free of &lt;strong&gt;business logic&lt;/strong&gt;. The only logic it should contain is related to platform-specific requirements.&lt;/p&gt;

&lt;p&gt;Why? In &lt;strong&gt;Clean Architecture&lt;/strong&gt;, each layer has a specific responsibility to ensure separation of concerns and maintainable code. The &lt;strong&gt;presentation layer&lt;/strong&gt;'s job is to interact with the user through the UI and manage platform-related operations like rendering views or handling input. It’s not meant to contain business logic because that belongs to the &lt;strong&gt;domain layer&lt;/strong&gt;, where the core rules and decision-making are centralized.&lt;/p&gt;

&lt;p&gt;The concept is to separate platform-specific considerations in the presentation layer, making it possible to change or adjust the user interface or platform without impacting the business rules and other code. For instance, if you wanted to transition from an Android to an iOS app, you would only need to rework the UI, while preserving the domain logic, which is particularly beneficial in the context of Kotlin. 😋&lt;/p&gt;

&lt;p&gt;Most of the misunderstanding comes from not understanding what business logic is, where it should be located, and the nature of certain examples.&lt;/p&gt;

&lt;p&gt;So, to address other issues, let's talk more about the domain layer, specifically about DDD:&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain-driven Design
&lt;/h3&gt;

&lt;p&gt;Domain-driven design (DDD) revolves around structuring the application to reflect the core business domain. But simply – what code should be written and in what way?&lt;/p&gt;

&lt;p&gt;You surely already know about Repositories or UseCases and some of you might think that UseCases are part of it. But the most important part is not UseCases or Repositories (I overall consider them not a part of a domain), but domain objects around which your domain logic lives.&lt;/p&gt;

&lt;p&gt;Domain objects in the DDD realm are essential objects that reflect the business problem you're addressing. They are not just regular DTOs or POJOs as commonly used in many beginner projects. Instead, in DDD, domain objects encapsulate both data and behavior (that includes, for example, validation). They are designed to represent real-world concepts and processes, and they embody the rules and logic that govern those concepts. But what is the simple advice that comes from it?&lt;/p&gt;

&lt;p&gt;There are 3 types of domain objects within DDD, so let's talk about them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Value objects
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;value object&lt;/strong&gt; is an immutable domain concept that has &lt;strong&gt;no identity&lt;/strong&gt; of its own.&lt;/p&gt;

&lt;p&gt;It never exists independently — it only makes sense as part of something larger, usually an entity or an aggregate.&lt;/p&gt;

&lt;p&gt;Value objects can range from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a simple wrapper around a single value&lt;/li&gt;
&lt;li&gt;to a richer structure composed of multiple fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As long as they are immutable and identity-less, they qualify.&lt;/p&gt;

&lt;p&gt;Identity, identity.. but what does it truly mean? In practice, it means that two value objects are considered the same &lt;strong&gt;if their data is the same&lt;/strong&gt;. There is no external identifier to compare — only the values they contain.&lt;/p&gt;

&lt;p&gt;In Kotlin, this maps naturally to a &lt;code&gt;data class&lt;/code&gt;, where equality is derived from all properties:&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;Money&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Currency&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="k"&gt;operator&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;minus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;
    &lt;span class="c1"&gt;// other functionality&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;Money(10, EUR)&lt;/code&gt; is indistinguishable from another &lt;code&gt;Money(10, EUR)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is no concept of "which one" — only "what value".&lt;/p&gt;

&lt;p&gt;Another powerful use of value objects is &lt;strong&gt;semantic typing&lt;/strong&gt; — replacing raw primitives with meaningful domain types:&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;@JvmInline&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailAddress&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="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;rawString&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="k"&gt;public&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;public&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;LENGTH_RANGE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;IntRange&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;EMAIL_PATTERN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Regex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Regex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;buildString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\@"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"("&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;")+"&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="k"&gt;public&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;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="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EmailAddress&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;return&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;value&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="k"&gt;in&lt;/span&gt; &lt;span class="nc"&gt;LENGTH_RANGE&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&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;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EMAIL_PATTERN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;EmailAddress&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="p"&gt;}&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;You can read more about semantic typing in my article — &lt;a href="https://dev.to/y9vad9/semantic-typing-we-ignore-i0d"&gt;Semantic Typing We Ignore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In short, my general advice would be to avoid using raw types such as &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Long&lt;/code&gt;, etc. directly in your domain model (&lt;code&gt;Boolean&lt;/code&gt; is often the only reasonable exception).&lt;/p&gt;

&lt;p&gt;Instead, introduce semantic value objects that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;are self-documenting&lt;/li&gt;
&lt;li&gt;encapsulate validation&lt;/li&gt;
&lt;li&gt;localize domain logic&lt;/li&gt;
&lt;li&gt;centralize terminology&lt;/li&gt;
&lt;li&gt;decouple the domain from accidental representations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what if my business object has a stable identity? That's where domain entities comes into play.&lt;/p&gt;

&lt;h4&gt;
  
  
  Domain entities
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;domain entity&lt;/strong&gt; represents a business concept that &lt;strong&gt;has a stable identity over time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike value objects, an entity is &lt;strong&gt;not defined by its attributes&lt;/strong&gt;, but by &lt;em&gt;who it is&lt;/em&gt;. Its properties may change, but its identity must remain the same.&lt;/p&gt;

&lt;p&gt;An example of it can be, for example, &lt;code&gt;UserProfile&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;UserProfile&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;UserProfileId&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;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DisplayName&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;email&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;changeDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;newDisplayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DisplayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;UserProfile&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;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;displayName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newDisplayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&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;changeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;newEmail&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="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;UserProfile&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;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;displayName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newEmail&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;No matter whether we change the email or the display name, the user is still the same. The identity of the entity does not depend on its attributes — it is anchored in the id, which remains constant even as other properties evolve. This makes it clear that an entity is about &lt;em&gt;who it is&lt;/em&gt;, not &lt;em&gt;what it currently has&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Even though this version of &lt;code&gt;UserProfile&lt;/code&gt; is immutable, it still preserves its identity through the &lt;code&gt;id&lt;/code&gt;. Each "change" produces a new instance, representing the same entity at a different point in time. &lt;/p&gt;

&lt;p&gt;In Classic DDD entities are mutable, and many implementations rely on this for convenience. I prefer keeping my code immutable whenever possible, because it makes reasoning about state, testing, and concurrency much safer, while still respecting the core DDD principle that an entity is defined by its stable identity rather than its attributes.&lt;/p&gt;

&lt;p&gt;And onto our "composer" — Aggregate.&lt;/p&gt;

&lt;h4&gt;
  
  
  Aggregate
&lt;/h4&gt;

&lt;p&gt;In DDD, an &lt;strong&gt;aggregate&lt;/strong&gt; is a cluster of domain objects — usually entities and value objects — that are treated as a single consistency boundary. The aggregate ensures that the rules and invariants of the domain are upheld whenever its internal state changes.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;invariant&lt;/strong&gt; is a business rule that must always hold true for an entity or aggregate. While value objects can enforce rules for their own data (for example, an EmailAddress ensures it has a valid format), an aggregate can enforce &lt;strong&gt;higher-level rules&lt;/strong&gt; that involve multiple entities or value objects together. In other words, the data might be valid individually, but not consistent in the context of the aggregate.&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;User&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="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;UserId&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;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&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;isAdmin&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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="c1"&gt;// enforce invariant at creation&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;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;isAdmin&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="nc"&gt;UserCreationResult&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;isAdmin&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&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;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@business_email.com"&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;UserCreationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InvalidAdminEmail&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;UserCreationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;User&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;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isAdmin&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="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// command within the aggregate&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;promoteToAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newEmail&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="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;UserPromotionResult&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;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newEmail&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;updatedProfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&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;updatedProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&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;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@business_email.com"&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;UserPromotionResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InvalidAdminEmail&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;UserPromotionResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;User&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;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updatedProfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;isAdmin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UserCreationResult&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;Success&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserCreationResult&lt;/span&gt;
    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;InvalidAdminEmail&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserCreationResult&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UserPromotionResult&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;Success&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;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserPromotionResult&lt;/span&gt;
    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;InvalidAdminEmail&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserPromotionResult&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, all creation and state changes go through type-safe factories and commands, ensuring that invariants are never bypassed. It's very important that our domain maintains consistency and data stay valid throughout its entire lifecycle.&lt;/p&gt;

&lt;p&gt;The sealed result types make it explicit and self-documentable which outcomes are possible, allowing the compiler to enforce handling of both success and failure cases. Instantiation of &lt;code&gt;User&lt;/code&gt; directly is prevented by the &lt;code&gt;private constructor&lt;/code&gt;, so every instance must pass through the validation logic in &lt;code&gt;create&lt;/code&gt; or &lt;code&gt;promoteToAdmin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Aggregates themselves also have boundaries we need to respect. You can’t just embed one aggregate inside another, because each aggregate is responsible for its own invariants and consistency rules. For example, a Team aggregate can’t hold User aggregates directly; it only keeps references to their IDs.&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;Team&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="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;TeamId&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TeamName&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;memberIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// references to User aggregates by ID&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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;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;TeamId&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;TeamName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;memberIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Team&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// can be more robust validation&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;memberIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Team must have at least one member"&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;Team&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memberIds&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="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;addMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Team&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Team&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memberIds&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;userId&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;removeMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Team&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Team&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memberIds&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;userId&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 keeps the rules clear: Team enforces its own invariants, while User enforces its own, and they interact safely through IDs rather than mixing their internal states. It’s like giving each aggregate its own workspace — they can collaborate, but never step on each other’s toes.&lt;/p&gt;




&lt;p&gt;Apart from Aggregates, Domain entities and Value objects, sometimes you might see the domain layer's service classes. They're used for the logic that usually cannot be put on the aggregates, but still a kind of business one. 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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShippingService&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;calculatePrice&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="n"&gt;shippingAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ShippingAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;shippingOption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ShippingOption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&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;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;shippingAddress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&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;price&lt;/span&gt;
        &lt;span class="k"&gt;else&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;price&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;someFee&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 won't discuss the usefulness or effectiveness of domain-level services or aggregators at this point. Just keep it in mind until we come to the point where we'll combine these approaches into one piece.&lt;/p&gt;

&lt;p&gt;But it's pretty much everything – the implementation may vary from project to project, and the only thing I use as a rule for anything – is immutability whenever possible.&lt;/p&gt;

&lt;h4&gt;
  
  
  Problems
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Anemic Domain Entities
&lt;/h5&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;Anemic Domain Model&lt;/strong&gt; is a common anti-pattern in Domain-Driven Design (DDD) where the domain objects — entities and value objects — are reduced to passive data containers that lack behavior and only contain getters and setters (if it applies) for their properties. This model is considered "anemic" because it fails to encapsulate the business logic that is supposed to live within the domain itself. Instead, this logic is often pushed into separate service classes, which leads to several problems in the overall design.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To understand this problem more, what particularly is bad about Anemic Domain Entities? Let's review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Possible complexity while understanding what domain entity is capable of&lt;/strong&gt;: When logic is spread across controllers or UseCases, it's harder to track the responsibilities of the entity, slowing down understanding and debugging (also, take into account that apart from IDE it's hard to look up the business logic that you put on some kind of controllers or UseCases, it makes code review much harder).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encapsulation is broken&lt;/strong&gt;: Entities hold only data without behavior, pushing the business logic into services, making the structure harder to maintain. It means that you should align logic across the UseCases/Controllers/so on and make sure that business logic is actually changed to the right one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harder to test&lt;/strong&gt;: When behavior is scattered, testing individual features gets harder because logic isn’t grouped inside the entity itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repetition of logic&lt;/strong&gt;: Business rules often get repeated across services/usecases, leading to unnecessary repetition and higher maintenance costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example of a bad business entity:&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;TimerState&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TimerEvent&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;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;alive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&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;publishTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Paused&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;publishTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&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;alive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minutes&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="nc"&gt;TimerState&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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt;

        &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Paused&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;data class&lt;/span&gt; &lt;span class="nc"&gt;ConfirmationWaiting&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;publishTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&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;alive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&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="nc"&gt;TimerState&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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt;

        &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ConfirmationWaiting&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;data class&lt;/span&gt; &lt;span class="nc"&gt;Inactive&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;publishTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&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="nc"&gt;TimerState&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;alive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;INFINITE&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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt;

        &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Inactive&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;data class&lt;/span&gt; &lt;span class="nc"&gt;Running&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;publishTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&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;alive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&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="nc"&gt;TimerState&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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt;

        &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Running&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;data class&lt;/span&gt; &lt;span class="nc"&gt;Rest&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;publishTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&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;alive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&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="nc"&gt;TimerState&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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt;

        &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;&amp;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;These are simply containers with data about TimeMates states. The question is: how can we transform this anemic domain entity into a rich one?&lt;/p&gt;

&lt;p&gt;In this case, for states, I had a different controller that was handling all transitions and events:&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;TimersStateMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;timers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimersRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimerSessionRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateStorage&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TimerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimerState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimerEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimeProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;coroutineScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineScope&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="nc"&gt;StateMachine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TimerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimerEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimerState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;stateMachineController&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;initial&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;TimerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Inactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TimerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Inactive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;onEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;timerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;onTimeout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;timerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="c1"&gt;// ...&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;Besides its good look, it violates the DDD principles – domain object should represent not only data but also behavior. The entity should be like this:&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;TimerState&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TimerEvent&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;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;alive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&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;publishTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&lt;/span&gt;

    &lt;span class="c1"&gt;// Now business entity can react to events by itself;&lt;/span&gt;
    &lt;span class="c1"&gt;// This functions are 'aggregates' from DDD;&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimerEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimerSettings&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;TimerState&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimerSettings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;TimerState&lt;/span&gt;

    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Paused&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;publishTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&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;alive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minutes&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="nc"&gt;TimerState&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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt;

        &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimerEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimerSettings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;TimerState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;TimerEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Start&lt;/span&gt; &lt;span class="p"&gt;-&amp;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;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isConfirmationRequired&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nc"&gt;TimerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ConfirmationWaiting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publishTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nc"&gt;TimerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publishTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;workTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimerSettings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UnixTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;TimerState&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;Inactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentTime&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Sometimes some logic is put into UseCases and it might be less obvious than in this particular case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By looking at such an entity you understand faster what it does, how it reacts to the domain events, and other things that may happen.&lt;/p&gt;

&lt;p&gt;But, as for business objects, sometimes you may feel that it does not have any behavior that you may add/move to them. Here's my example of such an object:&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;User&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;UserId&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserName&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;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="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;UserDescription&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;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Avatar&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;Patch&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserName&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;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;UserDescription&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;val&lt;/span&gt; &lt;span class="py"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Avatar&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interesting note&lt;/strong&gt;: It's an actual code from my user's domain with such a problem. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The potential problem is that &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Patch&lt;/code&gt; are data containers without business logic. First of all, I use &lt;code&gt;Patch&lt;/code&gt; only at the UseCases, which means that it should be put where it's needed. Use this rule for everything – declaration without usage on the layer which defines it, means that you do something wrong.&lt;/p&gt;

&lt;p&gt;As for &lt;code&gt;User&lt;/code&gt;, there's no need to create aggregate functions – Kotlin's auto-generated copy method is more than enough as value-objects are already validated and there's no custom logic for that for the entire entity.&lt;/p&gt;

&lt;p&gt;To learn more about this problem, you may reference, for example, this &lt;a href="https://medium.com/@inzuael/anemic-domain-model-vs-rich-domain-model-78752b46098f" rel="noopener noreferrer"&gt;article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'd add that you should try to avoid anemic domain entities, but at the same time do not force yourself to – if there is nothing to aggregate, do not add aggregates. Don't invent a behavior if there's nothing to be added – &lt;a href="https://www.interaction-design.org/literature/topics/keep-it-simple-stupid" rel="noopener noreferrer"&gt;KISS&lt;/a&gt; still applies.&lt;/p&gt;

&lt;h5&gt;
  
  
  Ignoring Ubiquitous Language
&lt;/h5&gt;

&lt;p&gt;Ubiquitous Language, a key concept in DDD, is often ignored. The domain model and code should use the same language as the business stakeholders to reduce misunderstandings. Failing to align the code with the &lt;strong&gt;language of the domain experts&lt;/strong&gt; results in a disconnect between the business logic and the actual implementation.&lt;/p&gt;

&lt;p&gt;In simple terms, names should be easy to understand even for non-programmers. This is especially helpful for large projects involving multiple teams with varying knowledge, skills, and responsibilities.&lt;/p&gt;

&lt;p&gt;That's something small to follow, but very important. I'd add that the same concepts shouldn't have different names across different domains – it's confusing even within one team.&lt;/p&gt;




&lt;p&gt;Now, let's move on to another approach I'm using in my projects – Hexagonal Architecture:&lt;/p&gt;

&lt;h3&gt;
  
  
  Hexagonal Architecture
&lt;/h3&gt;

&lt;p&gt;Hexagonal Architecture, also known as Ports and Adapters, takes a different angle on structuring applications compared to traditional approaches. It's all about isolating the &lt;strong&gt;core domain logic&lt;/strong&gt; from external systems — so that the core business logic isn't dependent on frameworks, databases, or other infrastructure concerns. This approach promotes &lt;strong&gt;testability&lt;/strong&gt; and &lt;strong&gt;maintainability&lt;/strong&gt;, and it aligns well with &lt;strong&gt;DDD&lt;/strong&gt; in that the focus remains on the business logic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There're two types of &lt;strong&gt;Ports&lt;/strong&gt; – Inbound and Outbound.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inbound ports&lt;/strong&gt; are about defining the operations that the outside world can perform on the core domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbound ports&lt;/strong&gt; are about defining the services that the domain needs from the outside world.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;The difference between DDD and Hexagonal architecture in isolation strategy is conceptually the same, but the second one takes it to the next level. Hexagonal Architecture defines how you should communicate with your domain model.&lt;/p&gt;

&lt;p&gt;So, for example, if you need to access an external service or feature to do something in your domain, you do the following:&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;interface&lt;/span&gt; &lt;span class="nc"&gt;GetCurrentUserPort&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&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;class&lt;/span&gt; &lt;span class="nc"&gt;TransferMoneyUseCase&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;balanceRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BalanceRepository&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;getCurrentUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;GetCurrentUserPort&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="o"&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;val&lt;/span&gt; &lt;span class="py"&gt;currentUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getCurrentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&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;availableAmount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balanceRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCurrentBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&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="c1"&gt;// ... transfer logic&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UseCases&lt;/strong&gt; are typically considered inbound ports since they represent operations or interactions initiated by the external world. However, the naming and implementation may vary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my projects, I prefer not to introduce another terminology and usually I just create an interface of Repository that I need from outside:&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;interface&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getCurrentUser&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other methods&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I consolidate everything into a single repository to avoid unnecessary class creation, providing a clearer abstraction for most people familiar with the concept of a repository.&lt;/p&gt;

&lt;p&gt;It may not always be the case that you need to call a repository from another feature or system. Sometimes, you may want to call a different business logic that handles what you need (what can be much better), known as UseCases. In this scenario, it's common to have a distinct interface from the first example.&lt;/p&gt;

&lt;p&gt;Here's the visualization:&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%2Fdu61takk1a8evfukzop4.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%2Fdu61takk1a8evfukzop4.png" alt="Visualisation" width="800" height="950"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: By the way, the other terminology for 'feature' is the &lt;a href="https://martinfowler.com/bliki/BoundedContext.html" rel="noopener noreferrer"&gt;'bounded context'&lt;/a&gt; from the DDD. They pretty much mean the same.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The example of defining and using ports that follows the schema above:&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;// FEATURE «A»&lt;/span&gt;

&lt;span class="c1"&gt;// Outbound port to get the user from another feature (bounded context)&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;GetUserPort&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;getUserById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;User&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;TransferMoneyUseCase&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;getUserPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;GetUserPort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TransferService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;transfer&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;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserId&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;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;USDAmount&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getUserPort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userId&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transferring ${request.amount} to ${user.name}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Insufficient balance for ${user.name}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Implementation of ports is done through &lt;em&gt;Adapters&lt;/em&gt; – they're basically just linkage that implements your interface to work with an external system. The naming of such layer may vary – from simple &lt;strong&gt;data&lt;/strong&gt; or &lt;strong&gt;integration&lt;/strong&gt; to straightforward &lt;strong&gt;adapters&lt;/strong&gt;. They are pretty interchangeable and up to specific project naming conventions. This layer usually implements other domains and uses other &lt;em&gt;Ports&lt;/em&gt; to achieve what it needs. &lt;/p&gt;

&lt;p&gt;Here's example of &lt;code&gt;GetUserPort&lt;/code&gt; 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="c1"&gt;// UserService is the Service from another feature (B)&lt;/span&gt;
&lt;span class="c1"&gt;// Adapters are usually in separate module because they're dependent on&lt;/span&gt;
&lt;span class="c1"&gt;// another domain, to avoid straight coupling.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetUserAdapter&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;getUserUseCase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;GetUserUseCase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;GetUserPort&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getUserById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&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;User&lt;/span&gt;&lt;span class="p"&gt;?&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;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUserById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&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;So, features are only coupled at the data/adapters level. The advantage of it is that your domain logic remains unchanged no matter what happens to the external system. That's another reason why the domain's ports actually shouldn't comply with everything that the external system wants – it's the Adapter's responsibility to deal with it. By that, I mean that, for example, the function signature might be different from the one that is used in the external system as long as it makes it possible to play around, of course.&lt;/p&gt;

&lt;p&gt;Another thing is that it's important to consider how to handle domain types. Features are rarely completely isolated from other types of features. For instance, if we have a business object called &lt;code&gt;User&lt;/code&gt; and a value object &lt;code&gt;UserId&lt;/code&gt;, we often need to reuse the user's ID to store information related to the user. This creates a need to find a way to reuse this type in different parts of the system.&lt;/p&gt;

&lt;p&gt;In an ideal Hexagonal architecture, different domains should exist independently. This means that each domain should have its specific definitions of the types they use. In simpler terms, it requires you to redeclare these types every time you need them.&lt;/p&gt;

&lt;p&gt;It creates a lot of duplication, boilerplate while converting each type between separate domains, problems with validation (especially if requirements change over time, you might overlook something), and just a great pain in any developer's life. &lt;/p&gt;

&lt;p&gt;The advice is that you shouldn't follow all of those rules as long as you don't see the benefit. Look for a happy medium while dealing with it, how I dealt with that we will discuss in the following part.&lt;/p&gt;

&lt;h4&gt;
  
  
  Problems
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Incorrect mental model
&lt;/h5&gt;

&lt;p&gt;As for mistakes I see the most – developers don't understand that the Domain &amp;amp; Application layers is not just about a physical division, but about the correct mental model. Let me explain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Mental model&lt;/strong&gt; is a conceptual representation of the work or structure of various parts of the system that interact with each other (simply, how the code is perceived by those who use it). It differs from a physical model in that a physical model involves &lt;strong&gt;physical&lt;/strong&gt; interaction - for example, calling a specific function or implementing a module, i.e. anything that is done by hand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A common issue in software design is allowing the domain/application layer to be aware of data storage or sourcing, which violates the separation of concerns principle. The domain's focus should remain on business logic, independent of data sources. However, you might encounter examples like &lt;code&gt;LocalUsersRepository&lt;/code&gt; or &lt;code&gt;RemoteUsersRepository&lt;/code&gt;, and corresponding use cases such as &lt;code&gt;GetCachedUserUseCase&lt;/code&gt; or &lt;code&gt;GetRemoteUserUseCase&lt;/code&gt; in application layer (in case if repositories put in domain, I usually put them in place where I use them — in application layer, but problem remains). While this may solve a specific problem, it violates the domain's mental model, which should remain agnostic to the source of the data.&lt;/p&gt;

&lt;p&gt;The same applies to the DAOs in the context of frameworks like &lt;code&gt;androidx.room&lt;/code&gt;. They're not only violating the rule of saying about the data source, but in addition, violate the rule of independence of any frameworks.&lt;/p&gt;

&lt;p&gt;Your repositories/usecases should stay away from the source of data, even though it might seem to be okay in situations when implementation is not directly in the domain/application layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  My implementation
&lt;/h3&gt;

&lt;p&gt;After finishing the explanation of the approaches that I use, I'd like to proceed to my actual implementation and how I dealt with reducing unnecessary boilerplate and abstractions.&lt;/p&gt;

&lt;p&gt;Let's start by defining the key ideas of each approach we discussed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clean Architecture&lt;/strong&gt;: divide your code into different layers by their responsibility (domain, data, presentation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain-driven Design&lt;/strong&gt;: The domain should contain only a business logic, all types should be consistent and valid across all of its lifecycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hexagonal Architecture&lt;/strong&gt;: Strict rules about accessing Domain and from Domain. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They perfectly match each other for the most part, which makes it a key to writing good code.&lt;/p&gt;

&lt;p&gt;The structure of TimeMates features (different domains) is following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;domain&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;data&lt;/strong&gt; (Implements everything that is related to storage or network management, including submodules with DataSources)

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;database&lt;/strong&gt; (integration with SQLDelight, auto-generated DataSources)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;network&lt;/strong&gt; (actually, I don't have it in TimeMates, because it's replaced with &lt;a href="https://github.com/timemates/sdk" rel="noopener noreferrer"&gt;TimeMates SDK&lt;/a&gt;, but if it is not, I would add it)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;dependencies&lt;/strong&gt; (Integration layer with Koin)&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;presentation&lt;/strong&gt; (UI with Compose and MVI)&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I like this structure so far, but you might want to distinguish the UI from ViewModels to be able to use different UI frameworks per platform, I am not planning to, so I leave it as it is. But if I face such a challenge in the future, it's not hard for me as I am not dependent on the Compose in the ViewModels.&lt;/p&gt;

&lt;p&gt;The main problem I experienced was the boilerplate that I had while implementing Hexagonal Architecture – I copied and pasted types that made me wonder 'Do I need it much'? So, I came up with the following rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I have common core types that are reused among different systems, it's a kind of combined domain of most needed types.&lt;/li&gt;
&lt;li&gt;The type can be only common if it's used among most of the domains, has a problem with duplication of the validation, and is not a complex structure (sometimes there are exceptions, but usually there are not a lot) at all.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What do I mean under 'complex structure'? Usually, your domain that demands another domain's type, don't actually need everything that's described in the given type. For example, you might want to share 'User' type, among with its value objects, but for the most part, other domains do not need all from User type and might want, for example, only name and the id. I am trying to avoid such situations and even if something is already in the core domain types, I would rather create distinct type with information that my certain domain need. But as for validation, I share almost all semantic value objects. &lt;/p&gt;

&lt;p&gt;You may extend this idea for bigger projects by creating not just common core types, but types for specific areas where some group of your subdomains (bounded contexts) works.&lt;/p&gt;

&lt;p&gt;To sum up, I am reusing value objects that have the same validation rules in one common module; I am trying not to make my common core types module too big with everything. There should be always a happy medium. &lt;/p&gt;

&lt;p&gt;In addition, in my projects I don't have 'Inbound Ports' as term in my projects. I replace them fully with UseCases:&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;GetTimersUseCase&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;timers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimersRepository&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;fsm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimersStateMachine&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="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Authorized&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TimersScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PageToken&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
        &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PageSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Result&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;infos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTimersInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageSize&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;ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;infos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TimersRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TimerInformation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;id&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;states&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&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;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fsm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCurrentState&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="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;infos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapIndexed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;states&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="n"&gt;index&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="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Result&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;Success&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;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Result&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: it's an example from the TimeMates Backend&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It does not violate Hexagonal Architecture or DDD which makes it a good way to define how the external world accesses your domain. It has the same meaning and behavior as an inbound port.&lt;/p&gt;

&lt;p&gt;As for outbound ports, I made the same as I provided earlier in examples.&lt;/p&gt;

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

&lt;p&gt;In my projects, I prefer to keep things practical. While theory and abstraction are useful, they can overcomplicate simple things. That’s why I combine the strengths of Clean Architecture, DDD, and Hexagonal Architecture without being overly strict about following them to the letter. Use critical thinking to determine what you actually need and why it benefits your project, rather than blindly following recommendations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus
&lt;/h3&gt;

&lt;p&gt;If you liked the article, I suggest you checking my other socials, where I share my thoughts, articles, and overall updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://t.me/vadymlog" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/y9vad9/" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/y9vad9/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kotlin</category>
      <category>architecture</category>
      <category>android</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Kotlin Multiplatform is now stable – What's the Impact?</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Wed, 01 Nov 2023 21:35:13 +0000</pubDate>
      <link>https://dev.to/y9vad9/kotlin-multiplatform-is-now-stable-whats-the-impact-4f18</link>
      <guid>https://dev.to/y9vad9/kotlin-multiplatform-is-now-stable-whats-the-impact-4f18</guid>
      <description>&lt;p&gt;In the latest Kotlin release, &lt;a href="https://blog.jetbrains.com/kotlin/2023/11/kotlin-1-9-20-released/" rel="noopener noreferrer"&gt;version 1.9.20&lt;/a&gt;, a significant milestone has been achieved with the stabilization of Kotlin Multiplatform technology. This marks a pivotal moment in Kotlin's development, as Multiplatform support has transitioned from its beta phase, which began in &lt;a href="https://kotlinlang.org/docs/whatsnew1720.html" rel="noopener noreferrer"&gt;version 1.7.20&lt;/a&gt;, to a stable and reliable feature in the Kotlin ecosystem.&lt;/p&gt;

&lt;p&gt;For those who doesn't know what is Kotlin Multiplatform, I will explain it in brief:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;❓ Explanation&lt;/strong&gt;&lt;br&gt;
Kotlin Multiplatform – is Kotlin technologies that leverages language ability to be compiled in different environments and languages, such as JVM (+Android), Web (via JavaScript or WebAssembly; in addition WASM can be used for other targets within its technology) and Native (iOS through Objective-C and Desktop using C++). Using it, you can write common and reusable code between different platforms only using Kotlin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Game changer
&lt;/h2&gt;

&lt;p&gt;Now that it's stable, let's highlight its advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easier to sell&lt;/strong&gt;: When technology reaches such state, it's much easier to push it towards your managers and overall business to use this technology that was not that reliable before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stable API&lt;/strong&gt;: Stable APIs guarantee consistent performance, easing developers' concerns about future compatibility and making it an appealing option for long-term projects.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Library Reliability:&lt;/strong&gt; Library authors benefit from stable Kotlin Multiplatform, ensuring their creations remain compatible and trustworthy, encouraging wider adoption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effortless Upgrades:&lt;/strong&gt; Smooth transitions between versions simplify the update process, providing businesses and developers a hassle-free experience.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Moreover, the stabilization of Kotlin Multiplatform opens up new opportunities for collaboration and innovation within the Kotlin community. With a stable foundation in place, developers can explore creative solutions and build versatile applications that cater to a wider audience. This shift from beta to stable empowers developers to pursue ambitious projects, knowing they have a reliable framework to support their endeavors.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;br&gt;
It's important to clarify that while Kotlin Multiplatform has reached overall stability, this doesn't necessarily extend to specific targets. For instance, the WebAssembly (WASM) target remains experimental, and some native targets might still be in the experimental phase. This means developers should exercise caution and check the stability status of individual targets before incorporating them into their projects. But for those that is using it for Android / iOS / Desktop &lt;strong&gt;it's already stable&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  My opinion
&lt;/h3&gt;

&lt;p&gt;I've been using this technology for 1.5 years, and the shift towards stability in Kotlin Multiplatform brings a sense of optimism about Kotlin's future. However, it's important to note that while the overall framework is stable, specific technologies like Compose/Multiplatform might not be production-ready yet. For instance, Compose's web and iOS targets are still experimental.&lt;/p&gt;

&lt;p&gt;However, for tasks that don't involve building common UI components, Kotlin Multiplatform is highly valuable. One of the most notable use cases is, for example, in network communication. Developers can create shared networking code using Kotlin Multiplatform, which can be utilized across platforms, both in Kotlin and native languages specific to each platform. This versatility makes it a powerful tool for enhancing productivity and code reusability in multi-platform projects.&lt;/p&gt;

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

&lt;p&gt;In summary, the stable release of Kotlin Multiplatform in version 1.9.20 signifies a pivotal moment in the evolution of Kotlin. Developers can now harness the power of Kotlin Multiplatform with confidence, knowing that it offers a reliable and efficient way to create cross-platform applications. This development not only simplifies the development process but also fosters a collaborative and innovative environment within the Kotlin community, paving the way for a future of diverse and dynamic cross-platform applications.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>multiplatform</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Finding the Right Balance in Gradle Dependency Strategy</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Wed, 25 Oct 2023 20:27:22 +0000</pubDate>
      <link>https://dev.to/y9vad9/finding-the-right-balance-in-gradle-dependency-strategy-4jdl</link>
      <guid>https://dev.to/y9vad9/finding-the-right-balance-in-gradle-dependency-strategy-4jdl</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In the dynamic field of software engineering, mastering dependency management is essential. With Gradle, developers have numerous strategies to declare dependencies, plugins, and versions. We will explore these methods and dissect the reasons for choosing each approach, delving into their merits and pitfalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;Before diving into methods, understanding the significance of well-structured dependencies is crucial. What potential issues can arise in the future, and what problems should proper structuring solve? Let's explore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating your dependencies
&lt;/h3&gt;

&lt;p&gt;In multi-module projects, updating dependencies like Kotlin versions or library versions can quickly become a daunting task. Imagine having to navigate through every &lt;code&gt;build.gradle.kts&lt;/code&gt; file, locating the plugins block, and modifying the version function for each dependency or plugin. The real challenge emerges when you're dealing with a significant number of modules. It's incredibly easy to overlook a specific dependency, leading to version conflicts or, in cases like Jetpack Compose, running into issues with unstable APIs tightly bound to specific Kotlin compiler versions (that can easily make your build broken with unexpected errors). You don't want to run into hours of debugging, because of simple mistakes, am I right?&lt;/p&gt;

&lt;h3&gt;
  
  
  Security vulnerabilities
&lt;/h3&gt;

&lt;p&gt;In the realm of software security, vulnerabilities in your code can pose significant risks. Many vulnerability scanners, however, struggle with dependencies that are partially defined in other files than &lt;code&gt;build.gradle.kts&lt;/code&gt; (for example, versions in &lt;code&gt;properties&lt;/code&gt; files). It's crucial to adopt practices that align with security systems, ensuring your code remains robust and safe.&lt;/p&gt;

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

&lt;p&gt;In smaller projects, managing dependencies manually might seem feasible. However, as the project and team expand, this approach quickly becomes a challenge. It's far more efficient to spot issues within a specific file(s) rather than sifting through numerous configuration files, attempting to identify potential problems.&lt;/p&gt;

&lt;p&gt;Centralizing dependency management enhances documentation. It allows specific decisions, like version choices, to be documented clearly. This centralized approach simplifies leaving notes or todos for reviews or impacting dependencies. Consistent formatting minimizes confusion and enhances readability, fostering a shared understanding of the project's dependencies. This creates a more efficient collaborative environment for the team.&lt;/p&gt;

&lt;p&gt;Lastly, a centralized approach offers consistency in the definition style. When everyone adheres to a standardized format and structure, it minimizes confusion and enhances readability. It fosters a cohesive understanding of the project's dependencies, making collaboration smoother and more effective.&lt;/p&gt;




&lt;p&gt;Overall, declaring your methods centralized makes your build configuration more clean, understandable and maintainable by newcomers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solutions
&lt;/h2&gt;

&lt;p&gt;Now, we've already discussed possible problems, so let's define our main goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple&lt;/li&gt;
&lt;li&gt;Secure&lt;/li&gt;
&lt;li&gt;Maintainable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we will come up with the solutions discussing their problems and advantages. Let's start from the simplest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Properties as versions container
&lt;/h3&gt;

&lt;p&gt;In the ever-evolving landscape of Gradle build management, the use of &lt;code&gt;.properties&lt;/code&gt; files as version containers remains a prevalent approach. This method centralizes dependency versions, ensuring project-wide consistency.&lt;/p&gt;

&lt;p&gt;For example, you can add your versions to &lt;code&gt;gradle.properties&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;kotlinVersion&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1.9.20-RC&lt;/span&gt;
&lt;span class="py"&gt;coroutinesVersion&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1.7.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And access it in your &lt;code&gt;build.gradle.kts&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="c1"&gt;// ... plugins, repositories&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;coroutinesVersion&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="c1"&gt;// it will auto-resolve property by name&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"&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;But, it has obvious disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lack of plugins support&lt;/strong&gt;: As &lt;code&gt;plugins&lt;/code&gt; evaluates before any other code in the &lt;code&gt;build.gradle.kts&lt;/code&gt; and has restrictions on operations inside, you can't use properties to provide versions to your build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate&lt;/strong&gt;: It adds a lot of boilerplate code (if we have more than one dependency that is obviously always a case) that clutters other logic and makes it less maintainable and understandable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the advantages is that you can override properties on the Gradle Build stage, but you'll probably never need it.&lt;/p&gt;

&lt;p&gt;In some cases, you can combine such an approach with another if you really need it, but for most projects, it's a bad idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies as constants
&lt;/h3&gt;

&lt;p&gt;As Gradle supports &lt;a href="https://docs.gradle.org/current/userguide/composite_builds.html" rel="noopener noreferrer"&gt;composite builds&lt;/a&gt; and &lt;a href=""&gt;&lt;code&gt;buildSrc&lt;/code&gt;&lt;/a&gt; convention module (note: it's also treated as composite built, but implicitly), you can simply create using Kotlin / Java / Groovy language singleton with constants that have dependencies &amp;amp; versions definition and use it directly in your builds in the next way:&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;object&lt;/span&gt; &lt;span class="nc"&gt;Deps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Libs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Kotlinx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;Coroutines&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines}"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Versions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;kotlin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.9.20-RC"&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;coroutines&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.7.3"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// let's take jvm plugin just for example&lt;/span&gt;
            &lt;span class="k"&gt;const&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="s"&gt;"org.jetbrains.kotlin.jvm"&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;In the case of &lt;code&gt;buildSrc&lt;/code&gt; it's always in your Gradle Build configurations classpath (context) and all code from &lt;code&gt;src/main/kotlin&lt;/code&gt; (or any other if applicable) is accessible in every &lt;code&gt;build.gradle.kts&lt;/code&gt; within a project. For composite builds, you should register Gradle plugin and define it in the desired module (we won't discuss it in this specific article, it's just for you to know). &lt;/p&gt;

&lt;p&gt;Even in this solution, we have multiple variants of how we can use such an approach in the end, but let's use the best one in my opinion. In your root gradle project &lt;code&gt;build.gradle.kts&lt;/code&gt; file, you can make next:&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="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// we should do this for each plugin we use in modules&lt;/span&gt;
    &lt;span class="c1"&gt;// it will affect classpath and make us free of specifying version&lt;/span&gt;
    &lt;span class="c1"&gt;// every time&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;version&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Versions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;❓ &lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;
In Gradle, the root module sets the tone for a multi-module project. Configurations, plugins, and dependencies defined in the root module automatically extend to all submodules. This ensures a uniform build environment across the project. The hierarchical structure simplifies management, allowing common settings at the root, with flexibility for customization in individual submodules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our modules we can use this plugin and our dependency in the following way:&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="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// now we don't need to specify version, it's already on a classpath&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ... repositories&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlinxCoroutines&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;What is the advantages? &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility in code&lt;/strong&gt;: In the case of composite builds, we can use it as usual code without any problems from other composite builds &lt;a href="https://github.com/y9vad9/kotlin-project-template/blob/2607858f0c604722c71f4d53187d5459f51eb945/build-logic/configuration/build.gradle.kts#L12" rel="noopener noreferrer"&gt;(my example)&lt;/a&gt; or even inside our code. As for version catalogs that we're going to discuss next, you can't use them as regular code in composite builds (important note: you can't use it inside &lt;code&gt;src/main/kotlin&lt;/code&gt;, but inside its configuration, you can) without &lt;a href="https://github.com/gradle/gradle/pull/15443/files#diff-5e341d2605a36e31ef4c643790effcdbbf1d4ca23483da374e04aeb3781d4e87R1025" rel="noopener noreferrer"&gt;hacks&lt;/a&gt; (also, it's possible only within right context).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better navigation and refactor&lt;/strong&gt;: Utilizing the same mechanism throughout your code improves IDE support, making navigation and refactoring more efficient. This built-in consistency enhances the overall development experience.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let's talk about the disadvantages of this method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lack of standardization&lt;/strong&gt;: As it's not recommended by Gradle and not that common practice in the community, it makes your build logic more complex and less understandable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Scans:&lt;/strong&gt; Almost all security scanners (except scanners that work as gradle plugins) don't support this method (for example, &lt;a href="https://github.com/dependabot/dependabot-core/issues/2280" rel="noopener noreferrer"&gt;related issue in Dependabot&lt;/a&gt;), removing entirely the effectiveness of security analysis in the project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-updating&lt;/strong&gt;: Such a method does not have any support in IDE auto-updating / migrating feature (and as was mentioned before, same for the Dependabot)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Certain issues with dependency management methods can indeed be addressed (or ignored as unimportant for certain projects), although not without their trade-offs.&lt;/p&gt;

&lt;p&gt;In summary, this method isn't the most maintainable or secure, &lt;a href="https://github.com/dependabot/dependabot-core/issues/2280" rel="noopener noreferrer"&gt;lacking integration with powerful tools like Dependabot&lt;/a&gt;. They are relatively straightforward but have such limitations. In the future it might be resolved, but for now, it's a big problem if you want to use Dependabot.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;br&gt;
This also applies to the other variants, for example, &lt;code&gt;dependencies.gradle&lt;/code&gt; files written in Groovy that provide constants for your build scripts – it's not checked by most of the scanners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡 Bonus&lt;/strong&gt;&lt;br&gt;
Regarding &lt;code&gt;buildSrc&lt;/code&gt;, it also poses challenges. Using it can hinder reusability, add complexity, and complicate gradual project migrations. It might even lead to classpath issues and performance problems in complex usage scenarios. Consequently, I avoid using &lt;code&gt;buildSrc&lt;/code&gt; in my projects due to these potential complications. For composite builds it's not always the case, but anyway makes your build configuration less understandable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Version catalogs
&lt;/h3&gt;

&lt;p&gt;Now let's discuss a relatively recent innovation in Gradle – Version catalogs. What is version catalogs?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;❓ Definition&lt;/strong&gt;&lt;br&gt;
Version catalog – it's a centralized file (in TOML format) in a Gradle project containing structured version information for libraries and plugins (usually located in &lt;code&gt;gradle/libs.versions.toml&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's an example of such a definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[versions]&lt;/span&gt;
&lt;span class="py"&gt;kotlin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.9.20-RC"&lt;/span&gt;
&lt;span class="py"&gt;coroutines&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.7.3"&lt;/span&gt;

&lt;span class="nn"&gt;[libraries]&lt;/span&gt;
&lt;span class="py"&gt;kotlinx-coroutines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"org.jetbrains.kotlinx:kotlinx-coroutines-core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"coroutines"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[plugins]&lt;/span&gt;
&lt;span class="py"&gt;kotlin-jvm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"org.jetbrains.kotlin.jvm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kotlin"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;build.gradle.kts&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="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// there's special function for version catalog definition&lt;/span&gt;
    &lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jvm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlinx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coroutines&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;br&gt;
You can create multiple TOML definition files, for details please refer to the &lt;a href="https://docs.gradle.org/current/userguide/platforms.html#ex-declaring-additional-catalogs" rel="noopener noreferrer"&gt;official manual&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why version catalogs?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Standardization and Familiarity:&lt;/strong&gt; Version catalogs are recommended by Gradle, which makes them widely understood by developers. Their structured approach simplifies dependency management, making it accessible to a broad audience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependabot Compatibility:&lt;/strong&gt; Version catalogs seamlessly integrate with tools like Dependabot, ensuring that your project stays up-to-date with the latest library versions. This compatibility streamlines the process of managing dependencies and addressing security vulnerabilities. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDE Support:&lt;/strong&gt; IDEs like IntelliJ IDEA provide built-in support for version catalogs. Developers can conveniently search for updates directly within the IDE, enhancing the development workflow and promoting efficient dependency management.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;br&gt;
There're multiple ways of defining version catalogs, but, for example, Dependabot supports only reading from a file (it's worth mentioning that it will cover most of the cases). But, it might be &lt;a href="https://github.com/dependabot/dependabot-core/issues/1164" rel="noopener noreferrer"&gt;resolved in future&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Limitation in Composite Builds:&lt;/strong&gt; Version catalogs struggle with integrating generated code in composite builds code. This constraint hampers the flexibility of using generated code as regular components, requiring additional effort and workarounds in composite build (and not only) scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring&lt;/strong&gt;: If you want to change names or path of your dependencies, you will need to change it in your build configurations by hands as there's no builtin support of it within IDE (at least, at the moment when this article was written).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Overall, I am choosing this method for all my new projects as it's already a standard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Classpath
&lt;/h3&gt;

&lt;p&gt;There're several ways how you can provide versions of your plugins by propagating it through classpath.&lt;/p&gt;

&lt;h4&gt;
  
  
  BuildScript
&lt;/h4&gt;

&lt;p&gt;In older versions of Gradle, specifying plugin versions directly in the &lt;code&gt;buildscript&lt;/code&gt; block was a common practice. It allowed developers to define the version of a plugin explicitly. Here's how it was typically done:&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="nf"&gt;buildscript&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 

    &lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nf"&gt;classpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20-RC"&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;In this approach, the version of the Kotlin Gradle plugin (&lt;code&gt;1.9.20-RC&lt;/code&gt; in this example) is declared in the &lt;code&gt;buildscript&lt;/code&gt; block. This version constraint ensures that the project uses the specified version of the plugin (but, it can be overwritten by submodules with special efforts).&lt;/p&gt;

&lt;p&gt;Once the plugin version is specified in the &lt;code&gt;buildscript&lt;/code&gt; block, applying the plugin becomes simplified. Developers can apply the plugin without explicitly specifying its version:&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="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jvm"&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 snippet, the &lt;code&gt;kotlin("jvm")&lt;/code&gt; plugin is applied without mentioning the version. Gradle automatically refers to the version specified in the &lt;code&gt;buildscript&lt;/code&gt; (to be more clear, buildscript evaluates before any other block in your script and adds specified dependencies in the classpath that is always used to resolve plugins and dependencies without a version specified; also it enforces specific version to be used as you cannot have the same plugin, but with different versions) block. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Versioning:&lt;/strong&gt; The version constraint is clearly defined in the build script, ensuring that the project uses a specific version of the plugin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Versions:&lt;/strong&gt; All modules in the project automatically use the specified version, promoting consistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-updating&lt;/strong&gt;: Intellij Idea (and Android Studio as well) supports auto-migration on such declarations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance Overhead:&lt;/strong&gt; Manually updating the version in the &lt;code&gt;buildscript&lt;/code&gt; block for every plugin can be tedious, especially in large projects with numerous plugins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less Concise Build Scripts:&lt;/strong&gt; The &lt;code&gt;buildscript&lt;/code&gt; block adds verbosity to the build script, potentially making it harder to read and maintain, especially as the number of plugins increases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security scanners compatibility&lt;/strong&gt;: Dependabot does not support vulnerability checks on such declarations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this method was widely used in the past, it's now considered less recommended due to the maintenance overhead and verbosity it introduces.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;❓ Explanation&lt;/strong&gt;&lt;br&gt;
Modern Gradle practices discourage specifying plugin versions directly in the &lt;code&gt;buildscript&lt;/code&gt; block for practical reasons. Instead, &lt;a href="https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies" rel="noopener noreferrer"&gt;it's recommended&lt;/a&gt; to centralize version management using the &lt;a href="https://docs.gradle.org/current/userguide/platforms.html" rel="noopener noreferrer"&gt;version catalogs&lt;/a&gt; or &lt;a href="https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_management" rel="noopener noreferrer"&gt;&lt;code&gt;pluginManagement&lt;/code&gt;&lt;/a&gt; (we will discuss it lower). Plugin management approach enables, for example, &lt;a href="https://docs.gradle.org/current/userguide/plugins.html#ex-plugin-resolution-strategy" rel="noopener noreferrer"&gt;dynamic version resolution&lt;/a&gt;, simplifying build scripts and preventing unnecessary clutter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;pluginManagement&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;As was discussed before, it's a modern way to declare your plugin's version, repositories and resolution rules. 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="nf"&gt;pluginManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;gradlePluginPortal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;google&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin.jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"1.9.20-RC"&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;Additionally, you can enforce specific versions using &lt;code&gt;resolutionStrategy&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="nf"&gt;pluginManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;resolutionStrategy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;eachPlugin&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;requested&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;namespace&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"com.example"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;useVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.2.3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Specify the desired version here&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;strong&gt;Pros of &lt;code&gt;pluginManagement&lt;/code&gt;:&lt;/strong&gt;
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lack of constraint syntax:&lt;/strong&gt; You can use versions from properties or any other supported source. You couldn't do the same with plugins, because &lt;a href="https://docs.gradle.org/current/userguide/plugins.html#sec:constrained_syntax" rel="noopener noreferrer"&gt;plugins block always evaluated separately&lt;/a&gt; from other part of the build script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Version Resolution:&lt;/strong&gt; You can replace versions or entire plugins source using &lt;a href="https://docs.gradle.org/current/userguide/plugins.html#ex-plugin-resolution-strategy" rel="noopener noreferrer"&gt;&lt;code&gt;resolutionStrategy&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;
  
  
  &lt;strong&gt;Cons of &lt;code&gt;pluginManagement&lt;/code&gt;:&lt;/strong&gt;
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Potential Complexity:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Con:&lt;/strong&gt; Overuse (or usage without real need) might lead to complex and harder-to-maintain scripts.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security scanners compatibility&lt;/strong&gt;: Dependabot does not support vulnerability checks on such declarations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Overall, this feature should be used only if you have a special need, consider using simpler variants if all you need is the version controlling.&lt;/p&gt;

&lt;h3&gt;
  
  
  BOM
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://docs.gradle.org/current/userguide/platforms.html#sub:bom_import" rel="noopener noreferrer"&gt;BOM (Bill of Materials)&lt;/a&gt; in Gradle is a central version management tool for multiple dependencies. It's a file that lists compatible versions of libraries and their dependencies. Importing a BOM ensures consistent versions, minimizing conflicts in complex projects. BOMs simplify version control, providing stability and coherence across the project.&lt;/p&gt;

&lt;p&gt;Here is the example of a BOM declaration:&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="nf"&gt;javaPlatform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;allowDependencies&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constraints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.slf4j:slf4j-api:2.0.9"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And how to use 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="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;enforcedPlatform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":some-bom"&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;How exactly it help with project organizing?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prevents Conflicts:&lt;/strong&gt; Ensures modules use compatible versions, avoiding conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publishable&lt;/strong&gt;: You can easily publish your BOM to the Maven, for example, for your library consumers; you can't do the same with version catalogs (with the same simplicity), properties, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scanners compatibility&lt;/strong&gt;: &lt;code&gt;.pom&lt;/code&gt; format that is used for BOMs that are published is easily recognized by most of the scanners (important note: that scan maven, Dependabot does not support it directly; but you always can use it all together with version catalogs, for example).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's especially useful in cases where your libraries are tightly bound to specific versions of other libraries, compiler APIs, etc.&lt;/p&gt;

&lt;p&gt;The main drawback is the possibility that you might not need this level of dependency declaration and might prefer simpler options like version catalogs.&lt;/p&gt;

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

&lt;p&gt;We have explored various methods of defining dependencies and managing plugins and their versions. It's crucial to emphasize that your choice should align with the specific requirements of your project. For instance, the &lt;code&gt;pluginManagement&lt;/code&gt; block can handle plugin versions, but it might unnecessarily complicate your build configuration without providing substantial benefits. Similarly, opting for constants in composite builds or &lt;code&gt;buildSrc&lt;/code&gt; should be a decision based on actual necessity, considering both advantages and potential drawbacks. Always assess the real needs of your project before making these choices.&lt;/p&gt;

</description>
      <category>gradle</category>
      <category>kotlin</category>
      <category>security</category>
      <category>android</category>
    </item>
    <item>
      <title>Gradle: from Newbie to Strong fundamentals</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Thu, 07 Sep 2023 18:32:37 +0000</pubDate>
      <link>https://dev.to/y9vad9/gradle-from-newbie-to-strong-fundamentals-mdf</link>
      <guid>https://dev.to/y9vad9/gradle-from-newbie-to-strong-fundamentals-mdf</guid>
      <description>&lt;p&gt;When developing on Kotlin, every beginner faces the problem of not understanding the appropriate tooling for working with the programming language. That's what this article was created for - to explain the work of Gradle for Kotlin and on Kotlin. Let's go!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❓ &lt;strong&gt;Definition&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Gradle&lt;/strong&gt; is a system for automating the assembly, including building and compilation. It’s designed for complex build flows, where the task is not only to execute the code, but to create custom build logic, handle multi-module projects, and integrate with continuous integration systems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But let's start with something simple - how to create our first project using Gradle?&lt;/p&gt;

&lt;h2&gt;
  
  
  Project
&lt;/h2&gt;

&lt;p&gt;Let's start with the basic concept that exists in the application building system - Project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❓ &lt;strong&gt;Definitions&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Project&lt;/strong&gt; is an independent unit of application organization as a set of dependent modules and rules for them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Module&lt;/strong&gt; is an independent unit of code organization that has a certain set of rules (how it is built, etc.). Exists for the same purpose as packages in Kotlin — to divide the code into logical blocks in order to improve the quality of the source code (reuse of code, both in one project and in others).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What rules am I talking about? In fact, everything is very simple - we describe how our project will be built (description of technical features), for which platform (for example, Android or iOS), in which language, and with which means (project dependencies).&lt;/p&gt;

&lt;h3&gt;
  
  
  Structure
&lt;/h3&gt;

&lt;p&gt;Let's create an example project structure:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fy9vad9%2Fy9vad9%2Fmain%2Farticles%2Fgradle-build-strong-foundation%2Fresources%2Fimages%2Fsimple-project-structure.svg" 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%2Fraw.githubusercontent.com%2Fy9vad9%2Fy9vad9%2Fmain%2Farticles%2Fgradle-build-strong-foundation%2Fresources%2Fimages%2Fsimple-project-structure.svg" alt="Simple project structure" width="1638" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;P.S: The names in the example have no special sense, they're just terminology from &lt;a href="https://wikipedia.org/wiki/Foobar" rel="noopener noreferrer"&gt;Foobar&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All modules are required to have &lt;code&gt;build.gradle.kts&lt;/code&gt; in order to work. It's important to note that modules cannot exist on their own and they only work if we include them to our project explicitly via &lt;code&gt;settings.gradle.kts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we have a kind of "chief supervisor", who determines which modules will be in our project and how they will work, and "local supervisors" who set rules only for the code subordinate to them (modules), but, it's worth noting that &lt;strong&gt;Project&lt;/strong&gt; has more priority than modules when it comes to rules (but it isn't something that we will cover in this particular article).&lt;/p&gt;

&lt;p&gt;What rules are there? In fact, there are many of them - it all depends on what you do, but the basic ones are, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project name, version, group (an identifier that is a kinda package from Kotlin)&lt;/li&gt;
&lt;li&gt;Programming language (Java / Scala / Groovy / Kotlin / etc)&lt;/li&gt;
&lt;li&gt;Platform (Only relevant for Kotlin, to be more clear, for Kotlin Multiplatform plugin)&lt;/li&gt;
&lt;li&gt;Dependencies (libraries or frameworks used in the code)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Modules
&lt;/h2&gt;

&lt;p&gt;Let's consider the modules: how and with what they are eaten. Let me remind you what a module is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❓ &lt;strong&gt;Definition&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Module&lt;/strong&gt; is an independent unit of code organization that has a certain set of formal rules that define the module's behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For an example, let's take &lt;code&gt;foo&lt;/code&gt; module:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fy9vad9%2Fy9vad9%2Fmain%2Farticles%2Fgradle-build-strong-foundation%2Fresources%2Fimages%2Ffoo-module-structure.svg" 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%2Fraw.githubusercontent.com%2Fy9vad9%2Fy9vad9%2Fmain%2Farticles%2Fgradle-build-strong-foundation%2Fresources%2Fimages%2Ffoo-module-structure.svg" alt="Module structure explanation" width="1128" height="237"&gt;&lt;/a&gt;&lt;br&gt;
To create a module, first of all, we create the directories of this module. After that, we create a file with the name &lt;code&gt;build.gradle.kts&lt;/code&gt; (or &lt;code&gt;build.gradle&lt;/code&gt; if you use Groovy as script language, no other way), where we will already prescribe what our module can do.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;build.gradle.kts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Our settings file has the following structure:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fy9vad9%2Fy9vad9%2Fmain%2Farticles%2Fgradle-build-strong-foundation%2Fresources%2Fimages%2Fbuild-gradle-schema.svg" 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%2Fraw.githubusercontent.com%2Fy9vad9%2Fy9vad9%2Fmain%2Farticles%2Fgradle-build-strong-foundation%2Fresources%2Fimages%2Fbuild-gradle-schema.svg" alt="build.gradle.kts scheme" width="1333" height="612"&gt;&lt;/a&gt;&lt;br&gt;
Don't be afraid! Even if it looks quite complicated. 😄&lt;/p&gt;

&lt;p&gt;The main component in any &lt;code&gt;build.gradle.kts&lt;/code&gt; is the &lt;strong&gt;Plugins&lt;/strong&gt; block. &lt;strong&gt;Dependencies&lt;/strong&gt; and &lt;strong&gt;Repositories&lt;/strong&gt; blocks are independent of Plugins block, but without it, they're like comedians telling jokes in an empty theater – they may have some great material, but there's no stage, no audience, and no laughter to be heard.&lt;/p&gt;

&lt;p&gt;Plugins that are applied to your module usually consume what you've specified in the &lt;strong&gt;Dependencies&lt;/strong&gt; block. So, dependencies without plugins that will use it are useless, that's why dependencies has a connection with plugins in our scheme.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repositories&lt;/strong&gt; are not dependent on plugins per se, but you always need them in place to apply any dependencies or plugins to your project. Therefore, without plugins or dependencies, it makes no sense to exist. So, using our previous analogy, it's akin to having a theater full of people without any comedians on stage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tasks&lt;/strong&gt; are also a fundamental thing in your Gradle configuration files. They're always provided by plugins that you're applying to your module. In an empty module without any plugin, you will not have any tasks. However, there are some basic tasks that are available on a project level: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tasks&lt;/code&gt; (returns list of available tasks across the project: name, on which tasks task is dependent, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dependencies&lt;/code&gt; (prints a report of the project's dependencies, showing which dependencies are used and their versions)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;help&lt;/code&gt; (returns list of available tasks across the project with a brief description).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;model&lt;/code&gt; (provides a detailed report of your project's structure, tasks, etc.; helping you understand and debug your Gradle build)&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Bonus&lt;/strong&gt;&lt;br&gt;
Tasks can be dependent on other tasks, it's especially useful when you're needed in the result of other task's execution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;

&lt;p&gt;Now, let's consider an example. For instance, let's create a Kotlin/JVM project with &lt;a href="https://github.com/Kotlin/kotlinx.coroutines" rel="noopener noreferrer"&gt;kotlinx.coroutines&lt;/a&gt; library as a dependency.&lt;/p&gt;

&lt;p&gt;Firstly, we need to create our project configuration file – &lt;code&gt;settings.gradle.kts&lt;/code&gt; in the root of our project:&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="n"&gt;rootProject&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;"our-first-project"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it work, you should run gradle sync inside your IDE:&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%2Frzxkcc98ben274ornz3m.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%2Frzxkcc98ben274ornz3m.png" alt="How to run gradle sync" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can either create a new folder for a new module or utilize the root folder as a module in the same way by simply adding a &lt;code&gt;build.gradle.kts&lt;/code&gt; file in the root directory (&lt;code&gt;our-first-project/build.gradle.kts&lt;/code&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❗️ &lt;strong&gt;Important&lt;/strong&gt;&lt;br&gt;
When we use root folder as a module we don't need to explicitly add it to our project configuration file, but for any new modules we should declare it by using the &lt;code&gt;include&lt;/code&gt; function – for example, &lt;code&gt;include(":foo")&lt;/code&gt; (for nested folders use &lt;code&gt;include(":foo:bar")&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's start with &lt;code&gt;plugins&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="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin.jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"1.9.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Bonus&lt;/strong&gt;&lt;br&gt;
We can simplify the declaration of Kotlin plugins (such as &lt;code&gt;jvm&lt;/code&gt;, &lt;code&gt;android&lt;/code&gt;, &lt;code&gt;js&lt;/code&gt;, &lt;code&gt;multiplatform&lt;/code&gt;) using &lt;code&gt;kotlin&lt;/code&gt; function: &lt;code&gt;id("org.jetbrains.kotlin.jvm")&lt;/code&gt; -&amp;gt; &lt;code&gt;kotlin("jvm")&lt;/code&gt;.&lt;br&gt;
It automatically appends &lt;code&gt;org.jetbrains.kotlin&lt;/code&gt; at the start.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, let's come to the Dependencies:&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="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"&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;Our Kotlin/JVM plugin provides a useful for us function – &lt;code&gt;implementation&lt;/code&gt;. Without it, we would have to write explicitly a configuration name (identifier for plugin) that will consume our dependencies. As you can remember, dependencies do not live on their own. So, to be more clear, &lt;strong&gt;Dependencies&lt;/strong&gt; block provides only the basic ability to add and consume added dependencies. We could add our dependency in the next way (but we still need a plugin that will consume 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="nf"&gt;dependencies&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;configurationName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"implementation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;configurationName&lt;/strong&gt; stands for dividing dependencies with different target plugins (plugins that are consuming our dependencies).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But, if we try to build our module, we will have the next problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Could not resolve all dependencies for configuration ':compileClasspath'. &amp;gt; Could not find org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To resolve this issue, we need to specify the repository from which we want to implement our dependency. Let's look at the 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="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// builtins:&lt;/span&gt;
    &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;mavenLocal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;google&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// or specify exact link to repository:&lt;/span&gt;
    &lt;span class="nf"&gt;maven&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://maven.y9vad9.com"&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;blockquote&gt;
&lt;p&gt;❓ &lt;strong&gt;Definition&lt;/strong&gt;&lt;br&gt;
&lt;u&gt;Maven repositories&lt;/u&gt; – are like online stores or libraries for code. They are collections of pre-built software libraries and dependencies that you can easily access and use in your projects. These repositories provide a centralized and organized way to share and distribute code.&lt;/p&gt;

&lt;p&gt;Also, it's important to notice, that Maven is &lt;em&gt;another build tool&lt;/em&gt; with built-in support from Gradle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But, for our case, we're only needed in &lt;code&gt;mavenCentral()&lt;/code&gt;. So, our resulting &lt;code&gt;build.gradle.kts&lt;/code&gt; is next:&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="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"1.9.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"&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;For such an example, we don't need to touch any tasks. But it would be good to mention that our Kotlin plugin provides the following tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;compileKotlin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compileJava&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, usually, you don't need to call these tasks directly, unless you're developing your own plugin that is dependent on results/outputs from these tasks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Bonus&lt;/strong&gt;&lt;br&gt;
You can run gradle tasks either by using command line or through IDE:&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%2Fqf9w3zy9sgrdru6rj68b.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%2Fqf9w3zy9sgrdru6rj68b.png" alt="How to run task" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an example I took &lt;code&gt;gradle build&lt;/code&gt; task.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will skip for now a full explanation of tasks and how they can be used. I will cover it in the next articles.&lt;/p&gt;

&lt;p&gt;Now, let's finally come to how and where can we write our code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source sets
&lt;/h3&gt;

&lt;p&gt;We figured out how to create a project and modules, but where should we write the code? In Gradle projects, there is a concept of different sets of source code (Source sets) - a kind of separation of code for different needs. For example, the following sets exist by default for our previous example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; – The name says ifself, it's main place where code should be placed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test&lt;/code&gt; – Used for code that are related to testing. It's dependent on &lt;code&gt;main&lt;/code&gt; source set and has all dependencies / code you have written in &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Bonus&lt;/strong&gt;&lt;br&gt;
But it's not always the case, for instance, &lt;a href="https://kotlinlang.org/docs/multiplatform.html" rel="noopener noreferrer"&gt;Kotlin/Multiplatform projects&lt;/a&gt; have dedicated source sets for every platform you're writing for (basically, the plugin creates a set of source sets for all the platforms we need). So, it's important to mention that it always depends on the plugins you're applying to the module and on your configuration. It's not a constant.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  main
&lt;/h4&gt;

&lt;p&gt;To start coding we need to create a folder for our programming language, for Kotlin it's &lt;code&gt;src/main/kotlin&lt;/code&gt; folder. So, from now on, we can simply create our 'Hello, World!' project. Let's create &lt;code&gt;Main.kt&lt;/code&gt; in our recently created folder:&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Kotlin!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run it using IDE, it will automatically handle the Gradle Building process.&lt;/p&gt;

&lt;h4&gt;
  
  
  test
&lt;/h4&gt;

&lt;p&gt;As I've previously told you, this source set is used for testing purposes. But it's important to mention that it has its own dependencies (but it also forks it from &lt;code&gt;main&lt;/code&gt; source-set). So, you can implement dependencies that will be available in this source-set. For example, let's implement &lt;code&gt;kotlin.test&lt;/code&gt; library:&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="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nf"&gt;testImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&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;You can refer to &lt;a href="https://kotlinlang.org/docs/jvm-test-using-junit.html" rel="noopener noreferrer"&gt;full tutorial&lt;/a&gt; in Kotlin documentation of how you can test your code using &lt;code&gt;kotlin.test&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-module projects
&lt;/h2&gt;

&lt;p&gt;The creation of different modules creates the need for their interaction with each other. Also, let's analyze the types of interaction, how you can't or shouldn't do it, and what's the point. Let's start!&lt;/p&gt;

&lt;p&gt;If you remember our initial project structure it has a few modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;foo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;qux&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's consider &lt;code&gt;foo&lt;/code&gt; as the main module where we have our entry point to application (&lt;code&gt;Main.kt&lt;/code&gt; file). Let's begin with creating a configuration for all three modules (it will be simple without any dependencies):&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="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;version&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.9.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;mavenCentral&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;To make it work, let's add our modules to &lt;code&gt;settings.gradle.kts&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="n"&gt;rootProject&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;"example"&lt;/span&gt;

&lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;":bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;":qux"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, let's make &lt;code&gt;foo&lt;/code&gt; dependent on &lt;code&gt;bar&lt;/code&gt; module:&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;// File: /foo/build.gradle.kts&lt;/span&gt;

&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":bar"&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;blockquote&gt;
&lt;p&gt;❗️ &lt;strong&gt;Important&lt;/strong&gt;&lt;br&gt;
To implement a module from your project, you should specify it using &lt;code&gt;project&lt;/code&gt; function. When implementing modules or specifying it somewhere else, we use special notation where &lt;code&gt;/&lt;/code&gt; is replaced with the &lt;code&gt;:&lt;/code&gt; symbol.&lt;/p&gt;

&lt;p&gt;And bonus: to implement the root module, just use &lt;code&gt;implementation(project(":"))&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From now, we can use any function or class from the &lt;code&gt;bar&lt;/code&gt; inside the &lt;code&gt;foo&lt;/code&gt; module (of course, if the visibility of these declarations allows it). For example, let's create a file in &lt;code&gt;foo&lt;/code&gt; module:&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;package&lt;/span&gt; &lt;span class="nn"&gt;com.my.project&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;printMeow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Meow!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can use it in &lt;code&gt;foo&lt;/code&gt; module:&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;import&lt;/span&gt; &lt;span class="nn"&gt;com.my.project.printMeow&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="nf"&gt;printMeow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But, can't use it from &lt;code&gt;qux&lt;/code&gt; module. Moreover, if we try to implement &lt;code&gt;foo&lt;/code&gt; module, &lt;code&gt;bar&lt;/code&gt; will still stay inaccessible. Module's dependencies are not exposed to other modules by default.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Bonus&lt;/strong&gt;&lt;br&gt;
We can share dependencies for modules that implement our particular module using &lt;a href="https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation" rel="noopener noreferrer"&gt;&lt;code&gt;api&lt;/code&gt;&lt;/a&gt; function instead of &lt;code&gt;implementation&lt;/code&gt;. In this way, for example, &lt;code&gt;qux&lt;/code&gt; module can access &lt;code&gt;bar&lt;/code&gt; module functions/classes/etc by implementing &lt;code&gt;foo&lt;/code&gt; without explicit dependence on the &lt;code&gt;bar&lt;/code&gt; module.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;Imagine that after the previous example, you need to get any class/function/etc. inside &lt;code&gt;bar&lt;/code&gt; from &lt;code&gt;foo&lt;/code&gt; module. If you will try to do this, you will have the next problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Circular dependency between the following tasks:
:bar:classes
\--- :bar:compileJava
     +--- :bar:compileKotlin
     |    \--- :foo:jar
     |         +--- :foo:classes
     |         |    \--- :foo:compileJava
     |         |         +--- :bar:jar
     |         |         |    +--- :bar:classes (*)
     |         |         |    +--- :bar:compileJava (*)
     |         |         |    \--- :bar:compileKotlin (*)
     |         |         \--- :foo:compileKotlin
     |         |              \--- :bar:jar (*)
     |         +--- :foo:compileJava (*)
     |         \--- :foo:compileKotlin (*)
     \--- :foo:jar (*)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is it all about? Everything is simple – you cannot create circular dependencies. &lt;/p&gt;

&lt;p&gt;Circular dependencies in Gradle are like a never-ending loop that makes your build process stuck because tasks keep waiting for each other to finish, which never happens. It's essential to avoid them to ensure your build runs smoothly. Furthermore, it's always about violating &lt;a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle" rel="noopener noreferrer"&gt;&lt;strong&gt;Dependency Inversion Principle&lt;/strong&gt;&lt;/a&gt; that is not good practice.&lt;/p&gt;

&lt;p&gt;You can refer to &lt;a href="https://discuss.gradle.org/t/gradle-support-for-cyclic-dependencies/14355" rel="noopener noreferrer"&gt;this discussion&lt;/a&gt; to read about it more.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👨🏻‍🏫 &lt;strong&gt;Bonus for experienced&lt;/strong&gt;&lt;br&gt;
Usually, if we talk about, for example, mobile applications, we use Three-Tier Architecture. So, it's a good idea to divide it into different modules to enforce architectural rules:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fy9vad9%2Fy9vad9%2Fmain%2Farticles%2Fgradle-build-strong-foundation%2Fresources%2Fimages%2Fproject-structure.svg" 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%2Fraw.githubusercontent.com%2Fy9vad9%2Fy9vad9%2Fmain%2Farticles%2Fgradle-build-strong-foundation%2Fresources%2Fimages%2Fproject-structure.svg" alt="Gradle project structure" width="1638" height="675"&gt;&lt;/a&gt;&lt;br&gt;
It makes, for example, our &lt;code&gt;domain&lt;/code&gt; layer not to be dependent on &lt;code&gt;data&lt;/code&gt; layer – it's literally impossible as there will be a circular dependency problem.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;It's not just another coffee brewing method; it's a powerful build automation tool that simplifies your software development process. With Gradle, you can manage dependencies, automate tasks, and keep your projects well-organized. It's like having a trusty assistant who takes care of the nitty-gritty, so you can focus on writing awesome code!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>gradle</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Kotlin Coroutines are not just about concurrency</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Fri, 01 Sep 2023 17:16:50 +0000</pubDate>
      <link>https://dev.to/y9vad9/coroutines-are-not-just-about-concurrency-4bfe</link>
      <guid>https://dev.to/y9vad9/coroutines-are-not-just-about-concurrency-4bfe</guid>
      <description>&lt;p&gt;Every time you hear about Kotlin Coroutines, you probably think about an easy, concise, and performant solution for handling asynchronous tasks like, for example, network requests. But is that their &lt;em&gt;only purpose&lt;/em&gt;? Let's consider the usages of Kotlin Coroutines beyond concurrency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kotlin Coroutines Primitives
&lt;/h2&gt;

&lt;p&gt;Let's start by understanding, how Coroutines underlying mechanism works. If we take a look at Kotlin Coroutines Primitives, it's only about a few classes and functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Continuation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CoroutineContext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;suspendCoroutine&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;createCoroutine&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;startCoroutine&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all that we have in our &lt;code&gt;kotlin-stdlib&lt;/code&gt;. But what are they used for? Let's dive deeper into how Kotlin Coroutines are designed to work.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Continuation&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Continuation is just an interface with two members: &lt;code&gt;context&lt;/code&gt; and &lt;code&gt;resumeWith&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="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Continuation&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nc"&gt;T&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;public&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;resumeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&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;p&gt;What does it do and what its purpose? &lt;code&gt;Continuation&lt;/code&gt; – is literally &lt;em&gt;Coroutine&lt;/em&gt; in primary. It's like a buffed callback with ability to propagate additional information and provide useful result of execution to coroutine.&lt;/p&gt;

&lt;p&gt;We haven't talked about &lt;code&gt;CoroutineContext&lt;/code&gt; yet, let's consider only &lt;code&gt;resumeWith&lt;/code&gt; for now.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;resumeWith&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Like any other callback, this is a function that is called when coroutine finishes its work. It uses &lt;code&gt;kotlin.Result&lt;/code&gt; to propagate any exceptions that occur inside coroutine in a safe way. &lt;/p&gt;

&lt;p&gt;So, we can literally create our concurrency logic using &lt;code&gt;Continuation&lt;/code&gt; in the next way:&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;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;executeNetworkRequest&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="nf"&gt;suspendCoroutine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;continuation&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;thread&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;continuation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resumeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;someBlockingRequest&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;blockquote&gt;
&lt;p&gt;&lt;code&gt;suspendCoroutine&lt;/code&gt; – is a bridge between kotlin coroutines code and non-coroutine-based code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or use existing asynchronous (pseudo-code):&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;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;executeNetworkRequest&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="nf"&gt;suspendCoroutine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;continuation&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;apiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="c1"&gt;// onSuccess &amp;amp; onFailure is a callback&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;continuation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;continuation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;resumeWithException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeAsync&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;blockquote&gt;
&lt;p&gt;&lt;code&gt;Continuation&amp;lt;in T&amp;gt;.resume(..)&lt;/code&gt; is the extension to avoid passing &lt;code&gt;kotlin.Result&lt;/code&gt; every time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, we can not only implement our concurrency logic but use existing and make it work with Kotlin Coroutines.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;startCoroutine&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Also, we can start suspend functions from non-suspend contexts using &lt;code&gt;startCoroutine&lt;/code&gt;. In Kotlin it's always used in the end if your &lt;code&gt;main&lt;/code&gt; function is &lt;code&gt;suspend&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;kotlinx.coroutines&lt;/code&gt; also uses it to run coroutines, but the mechanism there is much harder, of course.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlin.coroutines.*&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startCoroutine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;Continuation&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// we will talk about it lower&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;coroutineContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmptyCoroutineContext&lt;/span&gt;

            &lt;span class="c1"&gt;// called when coroutine finished its work&lt;/span&gt;
            &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;resumeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Unit&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;println&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSuccess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                        &lt;span class="s"&gt;"Successfully done"&lt;/span&gt; 
                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"Error happenned: ${result.exceptionOrNull()}"&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="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;But, of course, you can't just call suspend functions from &lt;code&gt;kotlinx.coroutines&lt;/code&gt; when executing coroutines in such way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;CoroutineContext&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now we came to another member of &lt;code&gt;Continuation&lt;/code&gt; – &lt;code&gt;CoroutineContext&lt;/code&gt;. What is it for?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CoroutineContext&lt;/code&gt; is a provider that propagates needed data to the coroutine. In the real world, it's usually about passing parameters across complex chain of coroutines.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To be more clear, CoroutineContext in &lt;code&gt;kotlinx.coroutines&lt;/code&gt; stands for &lt;a href="https://kotlinlang.org/docs/coroutines-basics.html#structured-concurrency" rel="noopener noreferrer"&gt;structured concurrency&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Simple example
&lt;/h4&gt;

&lt;p&gt;Let's create from our previous example for &lt;code&gt;startCoroutine&lt;/code&gt; code with the ability to retrieve values from &lt;code&gt;CoroutineContext&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="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlin.coroutines.*&lt;/span&gt;

&lt;span class="c1"&gt;// define our container for data we need inside coroutine&lt;/span&gt;
&lt;span class="c1"&gt;// it should always inherit 'CoroutineContext.Element'&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&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;someInt&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="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Element&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;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EXECUTION_CONTEXT_KEY&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// define type-safe key using which we will get our value&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;EXECUTION_CONTEXT_KEY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// define coroutine context that we will pass into Continuation&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;myCoroutineContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;CoroutineContext&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;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;EXECUTION_CONTEXT_KEY&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;E&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;):&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;?&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;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// .. we omit other functions for simplicity&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// here we retrieve value from coroutine context&lt;/span&gt;
    &lt;span class="c1"&gt;// coroutineContext is compiler intrinsic, can be called&lt;/span&gt;
    &lt;span class="c1"&gt;// only from suspend world&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;executionContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coroutineContext&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;EXECUTION_CONTEXT_KEY&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;executionContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;someInt&lt;/span&gt;&lt;span class="o"&gt;!!&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;main&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;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startCoroutine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;Continuation&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Unit&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;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myCoroutineContext&lt;/span&gt;

            &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;resumeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Unit&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;println&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSuccess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                        &lt;span class="s"&gt;"Successfully done"&lt;/span&gt; 
                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"Error happenned: ${result.exceptionOrNull()}"&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="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;CoroutineContext.Element&lt;/code&gt; – is the abstract that is used for storing elements inside &lt;code&gt;CoroutineContext&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CoroutineContext.Key&lt;/code&gt; – is the identifier of &lt;code&gt;CoroutineContext.Element&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can play around with this code &lt;a href="https://pl.kotl.in/0AZBcZ4MM" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Real-project example
&lt;/h4&gt;

&lt;p&gt;Let's imagine that we have our API service. Usually, we need to have some layer of authorization, so let's consider next example (as for such, I took gRPC):&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;// let's define an element that will persist in `CoroutineContext`&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;AuthorizationContext&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;accessHash&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;val&lt;/span&gt; &lt;span class="py"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AuthorizationProvider&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="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Element&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthorizationContext&lt;/span&gt;&lt;span class="p"&gt;&amp;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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// now we define our interceptor&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthorizationInterceptor&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;authorizationProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AuthorizationProvider&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="nc"&gt;CoroutineContextServerInterceptor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&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;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ACCESS_TOKEN_METADATA_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"access-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ASCII_STRING_MARSHALLER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;coroutineContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ServerCall&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&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;AuthorizationContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;accessHash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ACCESS_TOKEN_METADATA_KEY&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authorizationProvider&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CoroutineContext&lt;/code&gt; in &lt;code&gt;kotlinx.coroutines&lt;/code&gt; is literally just a &lt;code&gt;Map&amp;lt;K : CoroutineContext.Key, V : CoroutineContext.Element&amp;gt;&lt;/code&gt; (to be more accurate, it's &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html" rel="noopener noreferrer"&gt;ConcurrentHashMap&lt;/a&gt; on JVM, for example), same as it was in our example above. But, if we talk about &lt;code&gt;kotlinx.coroutines&lt;/code&gt;, it's propagated to all children coroutines within the desired coroutine (we didn't have such a mechanism).&lt;/p&gt;

&lt;p&gt;So, now we can get it in children coroutines:&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;suspend&lt;/span&gt; &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;provideAuthorization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&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;authContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coroutineContext&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;AuthorizationContext&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;authContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accessHash&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;StatusException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UNAUTHORIZED&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;userId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accessHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&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;blockquote&gt;
&lt;p&gt;Interesting fact: &lt;a href="https://github.com/JetBrains/kotlin/blob/7a7d392b3470b38d42f80c896b7270678d0f95c3/libraries/stdlib/src/kotlin/coroutines/Continuation.kt#L157" rel="noopener noreferrer"&gt;coroutineContext&lt;/a&gt; is the only property in Kotlin that has &lt;code&gt;suspend&lt;/code&gt; modifier. 👀&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For gRPC, we also need to register our Interceptor and write our RPCs. But the idea of this solution for gRPC is simple – decouple logic and simplify developer experience. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For Java, gRPC uses &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html" rel="noopener noreferrer"&gt;ThreadLocal&lt;/a&gt;, so we can also consider &lt;code&gt;CoroutineContext&lt;/code&gt; as an alternative to &lt;code&gt;ThreadLocal&lt;/code&gt;. We cannot use &lt;code&gt;ThreadLocal&lt;/code&gt; within coroutines, because usually coroutine is not linked to a specific thread (especially, when we talk about &lt;code&gt;withContext&lt;/code&gt;). Coroutines are more likely to be resumed on another thread (in addition, different coroutines can run on a single thread).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But, doesn't it mean that the only reason why coroutines exist – is concurrency? Let me explain.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Sequence&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One of the most common-place examples is the – &lt;code&gt;kotlin.sequences.Sequence&amp;lt;T&amp;gt;&lt;/code&gt;. In brief, it's a lazy collection that iterates only when you start consuming elements. You can read about them more &lt;a href="https://kotlinlang.org/docs/sequences.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you ever looked at &lt;code&gt;SequenceScope&lt;/code&gt; sources, it uses suspend functions under the hood:&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;@RestrictSuspension&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SequenceScope&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * Yields a value to the [Iterator] being built and suspends
     * until the next value is requested.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;yield&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="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// ... other&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;@RestrictSuspension&lt;/code&gt; disallows consumers from calling non-members suspend functions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, the idea is elements are consumed lazily. You can use them as regular collections and take advantage of lazy iteration.&lt;/p&gt;

&lt;p&gt;But how does it work under the hood? Let's take a look at implementation sources:&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;private&lt;/span&gt; &lt;span class="k"&gt;typealias&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;  

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;State_NotReady&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;State_ManyNotReady&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;State_ManyReady&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;  
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;State_Ready&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;  
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;State_Done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;  
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;State_Failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;  

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SequenceBuilderIterator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SequenceScope&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Iterator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nc"&gt;Continuation&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Unit&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;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;State_NotReady&lt;/span&gt;  
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;nextValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&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="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;nextIterator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Iterator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&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;null&lt;/span&gt;  
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;nextStep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Continuation&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Unit&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;null&lt;/span&gt;  

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;hasNext&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;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&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;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                &lt;span class="nc"&gt;State_NotReady&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;  
                &lt;span class="nc"&gt;State_ManyNotReady&lt;/span&gt; &lt;span class="p"&gt;-&amp;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;nextIterator&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasNext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;State_ManyReady&lt;/span&gt;  
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;  
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                    &lt;span class="n"&gt;nextIterator&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="nc"&gt;State_Done&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;  
                &lt;span class="nc"&gt;State_Ready&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;State_ManyReady&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;  
                &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nf"&gt;exceptionalState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
            &lt;span class="p"&gt;}&lt;/span&gt;  

            &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;State_Failed&lt;/span&gt;  
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;step&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nextStep&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;  
            &lt;span class="n"&gt;nextStep&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;  
            &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// IMPORTANT: it starts executing next yield &lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;T&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;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="nc"&gt;State_NotReady&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;State_ManyNotReady&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;nextNotReady&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
            &lt;span class="nc"&gt;State_ManyReady&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;State_ManyNotReady&lt;/span&gt;  
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;nextIterator&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
            &lt;span class="p"&gt;}&lt;/span&gt;  
            &lt;span class="nc"&gt;State_Ready&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;State_NotReady&lt;/span&gt;  
                &lt;span class="nd"&gt;@Suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UNCHECKED_CAST"&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nextValue&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;  
                &lt;span class="n"&gt;nextValue&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;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;  
            &lt;span class="p"&gt;}&lt;/span&gt;  
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nf"&gt;exceptionalState&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;nextNotReady&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;T&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="nf"&gt;hasNext&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;NoSuchElementException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;exceptionalState&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Throwable&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;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nc"&gt;State_Done&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;NoSuchElementException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
        &lt;span class="nc"&gt;State_Failed&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&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;"Iterator has failed."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&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;"Unexpected state of the iterator: $state"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  


    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;yield&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="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="n"&gt;nextValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;  
        &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;State_Ready&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;suspendCoroutineUninterceptedOrReturn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;  
            &lt;span class="n"&gt;nextStep&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;  
            &lt;span class="nc"&gt;COROUTINE_SUSPENDED&lt;/span&gt;  
        &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;    
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;resumeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOrThrow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// just rethrow exception if it is there  &lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;State_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;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoroutineContext&lt;/span&gt;  
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmptyCoroutineContext&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;COROUTINE_SUSPENDED&lt;/code&gt; is a special constant used internally by the Kotlin compiler to manage coroutine suspension and resumption. It's not something developers typically interact with directly, but rather, it serves as an internal signal within the coroutine machinery.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Looks a bit hard to read, isn't it? Let's go step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, we start with states. We have next states, let's talk about them in brief:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;State_NotReady&lt;/strong&gt;: The iterator is not ready to provide an item right now. It might be waiting for an operation or further processing to make an item available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State_ManyNotReady&lt;/strong&gt;: The iterator is prepared to provide multiple items, but they aren't ready immediately. It's waiting for a signal that items are ready for consumption (basically, waits for terminal operator).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State_ManyReady&lt;/strong&gt;: The iterator is ready to provide multiple items right now. It can immediately give the next item from the sequence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State_Ready&lt;/strong&gt;: The iterator has a single item ready to be provided. It's set to immediately give the item when asked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State_Done&lt;/strong&gt;: The iterator has no more items to provide. It has completed its job of producing elements from the sequence. We reach this state when we leave &lt;code&gt;SequenceBuilder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State_Failed&lt;/strong&gt;: Something unexpected happened, and the iterator encountered an issue. Usually, this should not happen.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hasNext&lt;/code&gt; based on state, returns a value or set of values when it's ready to consume. Moreover, it starts execution of sequence on every iteration inside &lt;code&gt;while&lt;/code&gt;. So, if there's &lt;code&gt;State_NotReady&lt;/code&gt;, it makes it ready by executing next yields.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;next&lt;/code&gt; function retrieves the next item from the iterator based on its current state (same to &lt;code&gt;hasNext&lt;/code&gt;). If next was called without &lt;code&gt;hasNext&lt;/code&gt;, you can reach &lt;code&gt;nextNotReady()&lt;/code&gt;. In other situations, it will simply return value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yield&lt;/code&gt; function just changes states of sequence iterator implementation. When new elements added it changes to &lt;code&gt;State_Ready&lt;/code&gt;. Using &lt;code&gt;suspendCoroutineUninterceptedOrReturn&lt;/code&gt; suspends the coroutine (execution) and resumes it later. It will be started when previous coroutine (suspend point) will be finished.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;To finish my explanation, let's just end with how could we make the same functionality just by using callbacks:&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;sequence&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sequence&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;yield&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="k"&gt;yield&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;
kotlin&lt;/p&gt;

&lt;p&gt;But, it looks a bit hard to read, isn't it? That's why coroutines are useful in this particular situation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the end, it doesn't look that complex, am I right?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;DeepRecursiveScope&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now, let's discuss another case for Kotlin Coroutines – &lt;code&gt;DeepRecursiveScope&lt;/code&gt;. As you probably know, usually when specific function call itself, we have a probability of running into &lt;code&gt;StackOverflowError&lt;/code&gt; as every call contributes it to our stack.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For the same purpose, for example, also exists &lt;code&gt;tailrec&lt;/code&gt; language construction. The difference is that &lt;code&gt;tailrec&lt;/code&gt; cannot have branching (conditional checks) with calls to other functions.&lt;/p&gt;

&lt;p&gt;You can read about that more &lt;a href="https://kotlinlang.org/docs/functions.html#tail-recursive-functions" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, &lt;code&gt;DeepRecursiveScope&lt;/code&gt; doesn't rely on traditional stack flow, but uses all the features Coroutines offer. To understand it more, let's consider classic example with fibonacci numbers:&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;fibonacci&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DeepRecursiveFunction&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Int&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;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;-&amp;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;x&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;x&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;-&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callRecursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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="nf"&gt;callRecursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// output: 144&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more complex example, you can refer to the &lt;a href="https://github.com/JetBrains/kotlin/blob/7a7d392b3470b38d42f80c896b7270678d0f95c3/libraries/stdlib/src/kotlin/util/DeepRecursive.kt#L40" rel="noopener noreferrer"&gt;kdoc&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We won't stop on exact implementation details of &lt;code&gt;DeepRecursiveScope&lt;/code&gt; (you can take a look at it &lt;a href="https://github.com/JetBrains/kotlin/blob/7a7d392b3470b38d42f80c896b7270678d0f95c3/libraries/stdlib/src/kotlin/util/DeepRecursive.kt#L131" rel="noopener noreferrer"&gt;here&lt;/a&gt;) as it has the same idea to &lt;code&gt;Sequence&lt;/code&gt; with additional behaviour to support mechanisms that is provided, but let's discuss how Kotlin Coroutines solves this particular problem. Furthermore, there's &lt;a href="https://elizarov.medium.com/deep-recursion-with-coroutines-7c53e15993e3" rel="noopener noreferrer"&gt;very good article about it&lt;/a&gt; from Roman Elizarov.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coroutines internally
&lt;/h3&gt;

&lt;p&gt;How exactly it solves the problem? As I mentioned before, Coroutines are inspired by &lt;a href="https://en.wikipedia.org/wiki/Continuation-passing_style" rel="noopener noreferrer"&gt;CPS (Continuation Passing Style)&lt;/a&gt;, but it's not exactly what Kotlin Compiler does to handle coroutines that efficiently.&lt;/p&gt;

&lt;p&gt;Kotlin Compiler uses a combination of optimizations to manage coroutines stack and execution efficiently. Let's check what exactly it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compiler transformations&lt;/strong&gt;: Kotlin Compiler generates State Machine, same to what we saw in the implementation details of Sequences. It doesn't get rid of all stack calls but reduces it enough to not run into &lt;code&gt;StackOverflowError&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heap-Allocation of Continuations&lt;/strong&gt;: In a traditional callback chain, each function call pushes data onto the call stack. If the chain is deep, this can consume a lot of stack space. In the coroutine approach, when a coroutine is suspended, its continuation is stored on the heap as an object. This continuation object holds the necessary information (stack, dispatcher, etc) to resume the coroutine's execution. This heap storage allows for a much greater capacity to handle deep call chains without risking stack overflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact mechanism of coroutines is next:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Serialization&lt;/strong&gt;: Suspended coroutine's stack state is saved in a heap-allocated continuation object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resumption&lt;/strong&gt;: When ready to resume, the framework sets up a native stack to mimic captured state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Copy&lt;/strong&gt;: Serialized stack state is copied from the continuation object to the native stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Configuration&lt;/strong&gt;: Execution context is configured to match original state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Program Counter&lt;/strong&gt;: Program counter is set to saved value for correct instruction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invocation&lt;/strong&gt;: Continuation code is invoked using CPS, resuming execution.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Stack restoring also helps us with resuming on different threads as they don't know anything about our coroutine's stack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, from now we can understand how coroutines internally work. Let's move on to other examples where Kotlin coroutines are used beyond concurrency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jetpack Compose
&lt;/h2&gt;

&lt;p&gt;If you ever worked with Compose and, for example, handling pointer events, you have probably noticed that there're some hacks used from Coroutines for listening to updates:&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;@RestrictsSuspension&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;---&lt;/span&gt;
&lt;span class="nd"&gt;@JvmDefaultWithCompatibility&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;AwaitPointerEventScope&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Density&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;awaitPointerEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PointerEventPass&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PointerEventPass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Main&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PointerEvent&lt;/span&gt;

    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;withTimeoutOrNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;timeMillis&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="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="nc"&gt;AwaitPointerEventScope&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;withTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;timeMillis&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="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="nc"&gt;AwaitPointerEventScope&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;block&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, as you see, scope that is used to handle pointer events is marked with &lt;code&gt;@RestrictsSuspension&lt;/code&gt;. If we come to the documentation provided, we'll see next:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;This is a restricted suspension scope. Code in this scope is
always called un-dispatched and may only suspend for calls 
to [awaitPointerEvent]. These functions resume synchronously and the caller may mutate the result
&lt;span class="gs"&gt;**before**&lt;/span&gt; the next await call to affect the next stage of the input processing pipeline.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;awaitPointerEvent&lt;/code&gt; is handled using kotlin primitives, without &lt;code&gt;kotlinx.coroutines&lt;/code&gt;. As in such situation we don't need any &lt;code&gt;kotlinx.coroutines&lt;/code&gt; logic (it's fairly just a callback that is called from Main-looper thread after user's action).&lt;/p&gt;

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

&lt;p&gt;In conclusion, this article has explored various facets of Kotlin Coroutines, emphasizing their versatility beyond traditional concurrency tasks. We've delved into the inner workings of coroutines primitives, discussed their use in Sequences and complex problem-solving scenarios like deep recursion, and examined real-world examples that showcase their broad applicability. The title, "Coroutines are not just about concurrency," aptly reflects the diverse capabilities that Kotlin coroutines offer in modern software development.&lt;/p&gt;

&lt;p&gt;Feel free to casually drop your expertise in the water cooler chat!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>coroutines</category>
      <category>android</category>
      <category>concurrency</category>
    </item>
    <item>
      <title>Extension-Oriented Design in Kotlin</title>
      <dc:creator>Vadym Yaroshchuk</dc:creator>
      <pubDate>Fri, 04 Aug 2023 15:50:47 +0000</pubDate>
      <link>https://dev.to/y9vad9/extension-oriented-design-3d41</link>
      <guid>https://dev.to/y9vad9/extension-oriented-design-3d41</guid>
      <description>&lt;p&gt;As programming evolved, so did the ways we structure code. Early programs were simple straight-line sequences of instructions, with reuse achieved mainly through copy-and-paste. Over time, this led to the introduction of abstractions such as subroutines, procedures, and functions, and later to higher-level approaches like object-oriented programming.&lt;/p&gt;

&lt;p&gt;Progress doesn’t stop, and each language comes with its own conventions that shape how code is written and read. Today, we’ll discuss Extension-Oriented Design in Kotlin — what it is, and why extensions are more than just a workaround for classes you don’t own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extensions in Kotlin
&lt;/h2&gt;

&lt;p&gt;Let’s start with a quick introduction for readers who are new to Kotlin — or don’t use it daily but are still curious.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;Kotlin extension&lt;/strong&gt; is a language feature that lets you add &lt;strong&gt;new behavior to an existing type without modifying it or inheriting from it&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In most object-oriented languages, the classic way to extend functionality is through &lt;strong&gt;inheritance&lt;/strong&gt;. This works well until you don’t own the class or inheritance is not an option — for example, when a type is a primitive, &lt;code&gt;final&lt;/code&gt;, &lt;code&gt;sealed&lt;/code&gt;, or already has too many implementations to reasonably extend.&lt;/p&gt;

&lt;p&gt;In Java, this limitation is usually handled by introducing so-called &lt;em&gt;helper&lt;/em&gt; or &lt;em&gt;utils&lt;/em&gt; classes (the naming varies, the idea does not). For instance, adding a function to find the maximum element in a list often looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListUtils&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;ListUtils&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, one could wrap a &lt;code&gt;List&lt;/code&gt; in another class to provide additional functionality using the &lt;a href="https://en.wikipedia.org/wiki/Delegation_pattern" rel="noopener noreferrer"&gt;delegation pattern&lt;/a&gt;. In practice, this is rarely done. Imagine creating a list with &lt;code&gt;List.of(...)&lt;/code&gt; and then wrapping it again in &lt;code&gt;FooList&amp;lt;E&amp;gt;&lt;/code&gt; just to gain a single extra operation — this introduces unnecessary ceremony and often extra allocations for very little benefit. As a result, static utility methods remain the dominant approach. That works, but comes with trade-offs.&lt;/p&gt;

&lt;p&gt;You must know that &lt;code&gt;ListUtils&lt;/code&gt; exists, remember its name, and hope it follows a predictable naming convention. In real codebases, these classes tend to grow large, multiply over time, and fragment into several variants. Finding an existing helper function often turns into a search problem, and duplicate implementations are common simply because someone didn’t know the functionality already existed.&lt;/p&gt;

&lt;p&gt;Kotlin addresses this with &lt;strong&gt;extension functions&lt;/strong&gt;. Instead of searching for a helper class, you look for behavior defined &lt;em&gt;on the type itself&lt;/em&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="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Comparable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fl2tjeurwy74b2ni2y5op.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%2Fl2tjeurwy74b2ni2y5op.png" alt="Code suggestions showcase" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This makes discovery significantly easier. The functionality is expressed in the vocabulary of the type it belongs to, rather than being hidden behind an arbitrary utility class name.&lt;/p&gt;

&lt;p&gt;That said, Kotlin extensions are sometimes perceived merely as a way to work around language limitations. A common example is the inability to declare a &lt;em&gt;final&lt;/em&gt; member in an &lt;code&gt;interface&lt;/code&gt;, even though some functions are inherently final by nature — such as those relying on &lt;code&gt;reified&lt;/code&gt; type parameters:&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;interface&lt;/span&gt; &lt;span class="nc"&gt;Serializer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;KClass&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&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="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;reified&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&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="nc"&gt;T&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the inline extension is meant to &lt;strong&gt;delegate to the underlying function&lt;/strong&gt;, without adding new behavior. Its purpose is purely to provide a more convenient call-site usage, not to change what the original function does. Even if the language allowed overriding it, doing so would be inappropriate, because it could break this delegation and expected contract if somebody would override it in an unexpected way.&lt;/p&gt;

&lt;p&gt;But is it only use of extensions? Not really.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separating Core from Capability
&lt;/h2&gt;

&lt;p&gt;Now to the most interesting part — how extensions help beyond working around language restrictions? But let's start with some kind of definition:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Extension-Oriented Design&lt;/strong&gt; is an approach where a type’s &lt;strong&gt;core capabilities&lt;/strong&gt; are kept minimal and stable, while &lt;strong&gt;derived, combinational, or convenience behavior&lt;/strong&gt; is expressed via extensions — regardless of whether the type is owned or not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While extensions are often used for classes we don’t own, ownership is not the point. The point is &lt;strong&gt;separating what a type &lt;em&gt;is&lt;/em&gt; from what can be &lt;em&gt;done with it&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A good example is &lt;code&gt;Flow&amp;lt;T&amp;gt;&lt;/code&gt; in &lt;code&gt;kotlinx.coroutines&lt;/code&gt;. The &lt;code&gt;Flow&lt;/code&gt; interface is intentionally minimal and is built around a &lt;strong&gt;single core capability&lt;/strong&gt;: &lt;code&gt;collect&lt;/code&gt;. Everything else is derived from it.&lt;/p&gt;

&lt;p&gt;Conceptually, the interface boils down to:&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;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="nc"&gt;T&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;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FlowCollector&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&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;p&gt;All higher-level operations — &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;combine&lt;/code&gt;, &lt;code&gt;flatMapLatest&lt;/code&gt;, and many others are implemented as &lt;strong&gt;extensions&lt;/strong&gt; that reuse this single primitive rather than expanding the interface.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;map&lt;/code&gt; is essentially defined as:&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;public&lt;/span&gt; &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;crossinline&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;R&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;R&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;flow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;collect&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="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;transform&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="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;Extension-oriented design makes this structure explicit. The core stays small and readable; derived behavior is clearly layered on top. This, in turn, simplifies understanding the codebase and reasoning about the abstraction.&lt;/p&gt;

&lt;p&gt;It also addresses a broader problem that abstractions often introduce: uncontrolled inheritance. When behavior is expressed through overridable members, it becomes harder to reason about what actually happens at runtime. A bug often turns into a guessing game in the code of endless chain of inheritence — what could have been overridden, and where?&lt;/p&gt;

&lt;p&gt;Extensions avoid this class of problems by design. They cannot be overridden, and therefore cannot silently alter behavior. This is not a limitation, but an intentional property: contracts remain stable, derived behavior stays predictable, and the set of things that can influence execution is reduced.&lt;/p&gt;

&lt;p&gt;As a result, extensions not only help structure code — they help preserve trust in the abstraction itself.&lt;/p&gt;

&lt;p&gt;And this is not a library-only technique — you can apply the same approach in your own code. A few general guidelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define a small, stable core capability.&lt;/li&gt;
&lt;li&gt;Express everything else as extensions built on top of it.&lt;/li&gt;
&lt;li&gt;Let behavior grow without inflating the core abstraction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this model, extensions are not “extra helpers”. They are the primary way behavior is composed, while the core remains minimal and explicit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're curious about other examples of this approach you can also go through &lt;a href="https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Result.kt#L173" rel="noopener noreferrer"&gt;kotlin.Result&lt;/a&gt; and Ktor sources. In general, all Standard Library, &lt;code&gt;kotlinx.coroutines&lt;/code&gt;, Ktor and other official libraries use this approach. Great source of inspiration!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Into the Deep End
&lt;/h2&gt;

&lt;p&gt;Let’s try to build something ourselves to really fixate this idea. Imagine you need to build a small and simple caching library. How would you approach it with &lt;strong&gt;extension-oriented design&lt;/strong&gt; in mind?&lt;/p&gt;

&lt;p&gt;First, we need to identify the &lt;strong&gt;core functionality&lt;/strong&gt; — the minimal set of operations that any cache must support. At its heart, a cache does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve a value by a key.&lt;/li&gt;
&lt;li&gt;Store a value under a key.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Everything else is just convenience layered on top. That gives us a clear, minimal interface:&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;interface&lt;/span&gt; &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;K&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;V&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&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;operator&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;K&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;V&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="c1"&gt;// Assigns [value] to a [key]. &lt;/span&gt;
    &lt;span class="c1"&gt;// If [value] is nulls — removes it from cache&lt;/span&gt;
    &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;K&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="nc"&gt;V&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This interface captures the &lt;strong&gt;core capabilities&lt;/strong&gt;: &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt;. It is small, stable, and predictable. Nothing else needs to live here, because everything additional can be expressed via extensions.&lt;/p&gt;

&lt;p&gt;Next, let’s think about the &lt;strong&gt;consumer experience&lt;/strong&gt;. In real-world usage, you often want to &lt;strong&gt;read a value or compute and store it if absent&lt;/strong&gt;. That’s a very common pattern:&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;cachedValue&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;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&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="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computeValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing it every time we work with cache is quite inconvenient, isn't it? We can encapsulate this pattern in a &lt;strong&gt;single extension&lt;/strong&gt;, leaving the core interface untouched:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;K&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;V&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;V&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOrAssign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;V&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;V&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nf"&gt;block&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;also&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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="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;Notice how this works: the interface itself stays tiny — just &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt;. Everything else lives in extensions. Here, &lt;code&gt;getOrAssign&lt;/code&gt; is exactly that kind of extension: it doesn’t change how the cache works, it just adds a clear, convenient way to use it.&lt;/p&gt;

&lt;p&gt;At the call-site it reads almost like plain language: &lt;code&gt;cache.getOrAssign(key) { expensiveComputation() }&lt;/code&gt;. You immediately know what’s happening: if the value is there, use it; otherwise, compute it and store it. The name &lt;code&gt;getOrAssign&lt;/code&gt; is chosen deliberately — we want it to be explicit about what this extension does, not leave anyone guessing.&lt;/p&gt;

&lt;p&gt;The beauty of this approach is that we can keep the core stable while layering on behavior in ways that are predictable and composable, keeping things small, clear, and lets the functionality grow naturally.&lt;/p&gt;

&lt;p&gt;Later on, you could add something like &lt;code&gt;invalidate(key: K)&lt;/code&gt; as an extension — all without touching the original interface. It makes the API feel cleaner than relying on &lt;code&gt;set(key, null)&lt;/code&gt; — which works, but feels a bit technical and not immediately obvious to someone using the cache.&lt;/p&gt;

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

&lt;p&gt;We use extension functions for many reasons: to work around technical limitations (for example, when a class is unavailable for modification or when certain features, such as &lt;code&gt;inline&lt;/code&gt; functions, cannot be used directly), and to structure code in a way that improves understanding.&lt;/p&gt;

&lt;p&gt;Extensions are not a silver bullet, especially in context of namespace pollution problem, and they shouldn’t be applied mechanically. Overengineering is easy, but ignoring this approach altogether often leads to bloated abstractions and poor discoverability.&lt;/p&gt;

&lt;p&gt;Used deliberately, extension-oriented design helps keep core abstractions small while allowing behavior to grow in a controlled and readable way.&lt;/p&gt;

&lt;p&gt;In the end, I also highly recommend &lt;a href="https://elizarov.medium.com/extension-oriented-design-13f4f27deaee" rel="noopener noreferrer"&gt;Roman Elizarov’s article&lt;/a&gt; on the same topic.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>programming</category>
      <category>codequality</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
