KINTO Tech Blog
Kotlin

[Server-side Kotlin] Using Kotlin’s Built-in Result Type for Error Management

Cover Image for [Server-side Kotlin] Using Kotlin’s Built-in Result Type for Error Management

<h1 id="introduction" data-line="1" class="code-line"><a class="header-anchor-link" href="#introduction" aria-hidden="true"></a> Introduction</h1>
<p data-line="3" class="code-line">Hello. My name is Shiode, and I do payment-related backend development in the Toyota Woven City Payment Solution Development Group. As mentioned in <a href="https://blog.kinto-technologies.com/posts/2022-09-27-kotlin-moshi/" target="_blank" rel="nofollow noopener noreferrer">my previous article</a>, our group uses Kotlin for development, with Ktor as our web framework and Exposed as the ORM. We also adopt Clean Architecture in our code architecture.</p>
<p data-line="5" class="code-line">Initially, we used Kotlin's <a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-result" target="_blank" rel="nofollow noopener noreferrer">Result type</a> for error handling, but as the number of developers increased, we started seeing a mix of Result and throw used in code. Mixing throw into code that uses Result defeats the purpose of expressing error handling through types, as it still requires try-catch blocks.</p>
<p data-line="7" class="code-line">Since Kotlin doesn't have Java's checked exceptions, it's easy to forget to call a try-catch block, which can lead to unhandled errors. To improve this situation, we discussed within the team and decided to standardize our error handling using Kotlin's Result type.</p>
<p data-line="9" class="code-line">In this article, I'll walk you through how our group writes error handling in practice.</p>
<p data-line="11" class="code-line">This article does not include the following.</p>
<ul data-line="13" class="code-line">
<li data-line="13" class="code-line">Explanation of Clean Architecture</li>
<li data-line="14" class="code-line">Explanation of Ktor and Exposed</li>
<li data-line="15" class="code-line">Comparison between <code>kotlin-result</code> and Kotlin's official Result type</li>
</ul>
<h2 id="application-directory-structure" data-line="17" class="code-line"><a class="header-anchor-link" href="#application-directory-structure" aria-hidden="true"></a> Application Directory Structure</h2>
<p data-line="19" class="code-line">Before getting into the main topic, I will explain the directory structure of the application in this group. Below is the well-known diagram of Clean Architecture along with our group's directory structure. As we've adopted Clean Architecture, our application's directory structure is generally organized in line with its principles.</p>
<p data-line="21" class="code-line"><img src="/assets/blog/authors/reona-shiode/error-handling/CleanArchitecture.jpg" alt="Clean Architecture" /> <em>(Source: <a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" target="_blank" rel="nofollow noopener noreferrer">The Clean Code Blog</a>)</em></p>
<div class="code-block-container"><pre class="language-text"><code class="language-text code-line" data-line="23">App Route/
├── domain
├── usecase/
│ ├── inputport
│ └── interactor
└── adapter/
├── web/
│ └── controller
└── gateway/
├── db
└── etc
</code></pre></div><p data-line="37" class="code-line">The correspondence between our directory structure and the Clean Architecture diagram is as follows:</p>
<ul data-line="39" class="code-line">
<li data-line="39" class="code-line"><code>domain</code> directory: <code>entities</code></li>
<li data-line="40" class="code-line"><code>usecase</code> directory: <code>Use Cases</code></li>
<li data-line="41" class="code-line"><code>adapter/web/controller</code> directory: <code>Controllers</code></li>
<li data-line="42" class="code-line"><code>adapter/gateway</code> directory: <code>Gateways</code></li>
</ul>
<p data-line="44" class="code-line">The terminology doesn't match exactly, but basically the <code>domain</code> directory sits at the core, the <code>usecase</code> directory surrounds it, and everything under <code>adapter</code> forms the outermost layer.</p>
<p data-line="46" class="code-line">Therefore, the allowed direction of dependency is as follows:</p>
<ul data-line="48" class="code-line">
<li data-line="48" class="code-line"><code>usecase</code> -> <code>domain</code></li>
<li data-line="49" class="code-line">Everything under <code>adapter</code> -> <code>usecase</code> or <code>domain</code></li>
</ul>
<p data-line="51" class="code-line">This direction of dependency makes it possible to develop business logic without being affected by factors such as web frameworks or database types.</p>
<h1 id="error-handling-policy" data-line="53" class="code-line"><a class="header-anchor-link" href="#error-handling-policy" aria-hidden="true"></a> Error Handling Policy</h1>
<p data-line="55" class="code-line">Basically, our error handling is based on the following policies:</p>
<ul data-line="57" class="code-line">
<li data-line="57" class="code-line">Use Result type instead of throw in case of processing failure</li>
<li data-line="58" class="code-line">When a function returns a Result type, do not use throw</li>
<li data-line="59" class="code-line">When returning an exception, use a custom-defined exception type</li>
</ul>
<p data-line="61" class="code-line">In the next section, I'll go over each of these policies in more detail, with code examples.</p>
<h2 id="use-the-result-type-when-a-function-may-fail" data-line="63" class="code-line"><a class="header-anchor-link" href="#use-the-result-type-when-a-function-may-fail" aria-hidden="true"></a> Use the Result type When a Function May Fail</h2>
<p data-line="65" class="code-line">Since Kotlin doesn't have checked exceptions like Java, there's no mechanism to force the caller to handle errors. By using the Result type, you can explicitly indicate that an error may be returned to the caller, reducing the chances of error handling being missed. However, in cases like <code>Result<Unit></code>, where the return value isn't used, error handling cannot be enforced unless a custom lint rule is defined. But as of now, we haven't defined one yet.</p>
<h3 id="code-examples" data-line="67" class="code-line"><a class="header-anchor-link" href="#code-examples" aria-hidden="true"></a> Code Examples</h3>
<p data-line="68" class="code-line">Below is a simple code example. When defining a function that performs division, it typically results in an error if the denominator is zero. If a function might fail, specify Result as its return type. In this example, <code>Result<Int></code> is specified.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="70"><span class="token keyword">fun</span> <span class="token function">divide</span><span class="token punctuation">(</span>numerator<span class="token operator">:</span> Int<span class="token punctuation">,</span> denominator<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">:</span> Result<span class="token operator"><</span>Int<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>denominator <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span><span class="token function">ZeroDenominatorException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>numerator<span class="token operator">/</span>denominator<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="when-returning-an-exception-as-a-result-type%2C-wrap-the-exception-in-a-custom-defined-exception" data-line="79" class="code-line"><a class="header-anchor-link" href="#when-returning-an-exception-as-a-result-type%2C-wrap-the-exception-in-a-custom-defined-exception" aria-hidden="true"></a> When Returning an Exception as a Result Type, Wrap the Exception in a Custom-defined Exception</h2>
<p data-line="81" class="code-line">Repositories are defined as interfaces in the domain layer, with their implementations residing in the adapter layer. If a use case layer calls a repository function and handles errors, and the adapter layer returns a third-party library exception as is, then the use case layer must be aware of that third-party exception. In that case, the use case layer becomes dependent on the adapter layer. Here's what that looks like in a diagram:</p>
<p data-line="83" class="code-line"><img src="/assets/blog/authors/reona-shiode/error-handling/dependency.png" alt="依存関係" width="400" /> <em>Interface-based Dependency and Exception-based Dependency (bad example)</em></p>
<p data-line="85" class="code-line">To avoid this, we always make sure to wrap any exception returned via Result in a custom-defined exception.</p>
<p data-line="87" class="code-line">One tricky point when applying Clean Architecture is deciding which layer exceptions belong to, but I personally think it should be in the domain layer. Our group uses a shared set of custom exceptions across multiple services, so we've extracted them into a separate domain library.</p>
<p data-line="89" class="code-line">Another tricky point with Kotlin's official Result type is that it doesn't allow you to specify the exception type, which means you can't enforce returning only custom exceptions. In cases like this, it may be worth considering the use of <a href="https://github.com/michaelbull/kotlin-result" target="_blank" rel="nofollow noopener noreferrer"><code>kotlin-result</code></a>. However, we chose not to adopt it in order to avoid introducing third-party types into the domain code.</p>
<h3 id="code-examples-1" data-line="91" class="code-line"><a class="header-anchor-link" href="#code-examples-1" aria-hidden="true"></a> Code Examples</h3>
<p data-line="93" class="code-line">Let's say the following interface is defined in the domain layer.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="95"><span class="token keyword">data</span> <span class="token keyword">class</span> <span class="token function">Entity</span><span class="token punctuation">(</span><span class="token keyword">val</span> id<span class="token operator">:</span> String<span class="token punctuation">)</span>
<span class="token keyword">interface</span> EntityRepository <span class="token punctuation">{</span>
<span class="token keyword">fun</span> <span class="token function">getEntityById</span><span class="token punctuation">(</span>id<span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span>
<span class="token punctuation">}</span>
</code></pre></div><p data-line="103" class="code-line">Now, consider a case where a third-party library exposes a method like the one shown below, and it's used as-is.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="105"><span class="token keyword">fun</span> <span class="token function">thirdPartyMethod</span><span class="token punctuation">(</span>id<span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token operator">:</span> Entity <span class="token punctuation">{</span>
<span class="token keyword">throw</span> <span class="token function">ThirdPartyException</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><h4 id="bad-example" data-line="111" class="code-line"><a class="header-anchor-link" href="#bad-example" aria-hidden="true"></a> Bad Example</h4>
<p data-line="113" class="code-line">If the implementation in the adapter layer returns the exception from the third-party library directly. As shown below, this causes it to leak into the caller such as <code>UseCase</code>.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="115"><span class="token keyword">class</span> EntityRepositoryImpl <span class="token operator">:</span> EntityRepository <span class="token punctuation">{</span>
<span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getEntityById</span><span class="token punctuation">(</span>id<span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> kotlin<span class="token punctuation">.</span><span class="token function">runCatching</span> <span class="token punctuation">{</span>
<span class="token function">thirdPartyMethod</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token comment">// This returns the third party exception</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h4 id="good-example" data-line="125" class="code-line"><a class="header-anchor-link" href="#good-example" aria-hidden="true"></a> Good Example</h4>
<p data-line="127" class="code-line">To prevent third-party exceptions from leaking to the caller, wrap them in a custom-defined exception.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="129"><span class="token keyword">class</span> EntityRepositoryImpl <span class="token operator">:</span> EntityRepository <span class="token punctuation">{</span>
<span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getEntityById</span><span class="token punctuation">(</span>id<span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> kotlin<span class="token punctuation">.</span><span class="token function">runCatching</span> <span class="token punctuation">{</span>
<span class="token function">thirdPartyMethod</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">onFailure</span> <span class="token punctuation">{</span> cause <span class="token operator">-></span>
<span class="token comment">// wrap with our own exception</span>
<span class="token function">CustomUnexpectedException</span><span class="token punctuation">(</span>cause<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="avoid-using-throw-in-functions-that-return-result" data-line="142" class="code-line"><a class="header-anchor-link" href="#avoid-using-throw-in-functions-that-return-result" aria-hidden="true"></a> Avoid Using <code>throw</code> in Functions that Return Result</h2>
<p data-line="144" class="code-line">If a function returns a Result type or throws an exception, the caller must handle both. Even if the function author thinks a particular exception doesn't need to be handled by the caller, there may be cases where the caller wants to handle it. For this reason, we have standardized on using the Result type and avoid throwing exceptions explicitly.</p>
<p data-line="146" class="code-line">In some cases such as database connection errors, it might seem acceptable to throw an exception from the adapter layer and let it propagate directly to the API response, since recovery at the use case level is impossible. However, for issues like failure to update the database, we may still want to log inconsistencies with third-party SaaS. In that case, if we scope out using throw, there's a risk that alerts won't be triggered appropriately.</p>
<p data-line="148" class="code-line">We believe it's up to the caller to decide whether error handling is necessary, so even if the function author considers it unnecessary, the exception is returned using a Result.</p>
<h3 id="code-examples-2" data-line="150" class="code-line"><a class="header-anchor-link" href="#code-examples-2" aria-hidden="true"></a> Code Examples</h3>
<p data-line="152" class="code-line">Let's take the save function of a repository as an example. The save function receives the entity class and returns the result as a <code>Result<Entity></code>.</p>
<h4 id="examples-of-what-not-to-do" data-line="154" class="code-line"><a class="header-anchor-link" href="#examples-of-what-not-to-do" aria-hidden="true"></a> Examples of what not to do</h4>
<p data-line="156" class="code-line">As shown below, assume that a connection error was thrown, while other errors are returned using the Result type.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="158"><span class="token keyword">class</span> <span class="token function">EntityRepository</span><span class="token punctuation">(</span><span class="token keyword">val</span> db<span class="token operator">:</span> Database<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">fun</span> <span class="token function">saveEntity</span><span class="token punctuation">(</span>entity<span class="token operator">:</span> Entity<span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
db<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
db<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> ConnectionException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// return result instead</span>
<span class="token keyword">throw</span> <span class="token function">OurConnectionException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> throwable<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span><span class="token function">OurUnexpectedException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p data-line="175" class="code-line">Now, suppose the use case layer wants to take some kind of action if an error occurs during the save operation. In this case, you must use <code>runCatching</code> (which internally uses try-catch to convert to Result type).</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="177"><span class="token keyword">class</span> <span class="token function">UseCase</span><span class="token punctuation">(</span><span class="token keyword">val</span> repo<span class="token operator">:</span> EntityRepository<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">fun</span> <span class="token function">createNewEntity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> entity <span class="token operator">=</span> Entity<span class="token punctuation">.</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> runCatching <span class="token punctuation">{</span> <span class="token comment">// need this runCatching in order to catch an exception</span>
repo<span class="token punctuation">.</span><span class="token function">saveEntity</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getOrThrow</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">onFailure</span> <span class="token punctuation">{</span>
<span class="token comment">// some error handling here</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h4 id="good-example-1" data-line="190" class="code-line"><a class="header-anchor-link" href="#good-example-1" aria-hidden="true"></a> Good Example</h4>
<p data-line="192" class="code-line">In the good example, all exceptions are wrapped in a custom-defined exception and returned using the Result type. This allows the caller to remove <code>runCatching</code>, simplifying the code.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="194"><span class="token keyword">class</span> <span class="token function">EntityRepository</span><span class="token punctuation">(</span><span class="token keyword">val</span> db<span class="token operator">:</span> Database<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">fun</span> <span class="token function">saveEntity</span><span class="token punctuation">(</span>entity<span class="token operator">:</span> Entity<span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
db<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
db<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> ConnectionException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span><span class="token function">OurConnectionException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> Exception<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span><span class="token function">OurUnexpectedException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token function">UseCase</span><span class="token punctuation">(</span><span class="token keyword">val</span> repo<span class="token operator">:</span> EntityRepository<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">fun</span> <span class="token function">createNewEntity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> entity <span class="token operator">=</span> Entity<span class="token punctuation">.</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> repo<span class="token punctuation">.</span><span class="token function">saveEntity</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">onFailure</span> <span class="token punctuation">{</span>
<span class="token comment">// some error handling here</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h1 id="useful-custom-function-for-using-the-result-type" data-line="218" class="code-line"><a class="header-anchor-link" href="#useful-custom-function-for-using-the-result-type" aria-hidden="true"></a> Useful Custom Function for Using the Result Type</h1>
<h2 id="andthen" data-line="220" class="code-line"><a class="header-anchor-link" href="#andthen" aria-hidden="true"></a> <code>andThen</code></h2>
<p data-line="222" class="code-line">When using a Result type, there are often times when you want to use that value of a successful Result to return a different Result type. For example, updating the status of a specific entity might look like this:</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="224"><span class="token keyword">fun</span> UseCaseImpl<span class="token punctuation">.</span><span class="token function">updataStatus</span><span class="token punctuation">(</span>id<span class="token operator">:</span> Id<span class="token punctuation">)</span> <span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> entity <span class="token operator">=</span> repository<span class="token punctuation">.</span><span class="token function">fetchEntityById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getOrElse</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">val</span> updatedEntity <span class="token operator">=</span> entity<span class="token punctuation">.</span><span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getOrElse</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> repository<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>updatedEntity<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><p data-line="236" class="code-line">In such cases, writing code becomes easier when the operations can be connected by a method chain. While the <code>kotlin-result</code> provides the <code>andThen</code> function for this purpose, Kotlin's official Result type does not. Therefore, our group defined and uses the following method:</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="238"><span class="token keyword">inline</span> <span class="token keyword">fun</span> <span class="token operator"><</span>T<span class="token punctuation">,</span> R<span class="token operator">></span> Result<span class="token operator"><</span>T<span class="token operator">></span><span class="token punctuation">.</span><span class="token function">andThen</span><span class="token punctuation">(</span>transform<span class="token operator">:</span> <span class="token punctuation">(</span>T<span class="token punctuation">)</span> <span class="token operator">-></span> Result<span class="token operator"><</span>R<span class="token operator">></span><span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>R<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>isSuccess<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">transform</span><span class="token punctuation">(</span><span class="token function">getOrThrow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span><span class="token function">exceptionOrNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">!!</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><p data-line="247" class="code-line">By using this, the previous example can be rewritten as shown below. The result is a bit cleaner, with less repetitive code.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="249"><span class="token keyword">fun</span> UseCaseImpl<span class="token punctuation">.</span><span class="token function">updataStatus</span><span class="token punctuation">(</span>id<span class="token operator">:</span> Id<span class="token punctuation">)</span> <span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> repository<span class="token punctuation">.</span><span class="token function">fetchEntityById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">andThen</span> <span class="token punctuation">{</span> entity <span class="token operator">-></span>
entity<span class="token punctuation">.</span><span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">andThen</span> <span class="token punctuation">{</span> updatedEntity <span class="token operator">-></span>
repository<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>updatedEntity<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="dointransaction-for-exposed" data-line="259" class="code-line"><a class="header-anchor-link" href="#dointransaction-for-exposed" aria-hidden="true"></a> <code>doInTransaction</code> for Exposed</h2>
<p data-line="261" class="code-line">Our group uses <a href="https://github.com/JetBrains/Exposed" target="_blank" rel="nofollow noopener noreferrer">Exposed</a> as the ORMapper. With Exposed, all database operations must be written within the lambda scope called <code>transaction</code>. If an exception is thrown within this <code>transaction</code> scope, it automatically performs a rollback. Since using the Result type avoids throwing exceptions, we created a function that performs a rollback automatically when the Result indicates failure.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="263"><span class="token keyword">fun</span> <span class="token operator"><</span>T<span class="token operator">></span> <span class="token function">doInTransaction</span><span class="token punctuation">(</span>db<span class="token operator">:</span> Database<span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">,</span> f<span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> Result<span class="token operator"><</span>T<span class="token operator">></span><span class="token punctuation">)</span><span class="token operator">:</span> Result<span class="token operator"><</span>T<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">transaction</span><span class="token punctuation">(</span>db<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">onFailure</span> <span class="token punctuation">{</span>
<span class="token function">rollback</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">onSuccess</span> <span class="token punctuation">{</span>
<span class="token function">commit</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p data-line="275" class="code-line">Applying this to the previous example of <code>UseCaseImpl</code>, it can be used as follows.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="277"><span class="token keyword">fun</span> UseCaseImpl<span class="token punctuation">.</span><span class="token function">updataStatus</span><span class="token punctuation">(</span>id<span class="token operator">:</span> Id<span class="token punctuation">)</span> <span class="token operator">:</span> Result<span class="token operator"><</span>Entity<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> doInTransaction <span class="token punctuation">{</span>
repository<span class="token punctuation">.</span><span class="token function">fetchEntityByIdForUpdate</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">andThen</span> <span class="token punctuation">{</span> entity <span class="token operator">-></span>
entity<span class="token punctuation">.</span><span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">andThen</span> <span class="token punctuation">{</span> updatedEntity <span class="token operator">-></span>
repository<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>updatedEntity<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="respondresult-for-ktor" data-line="289" class="code-line"><a class="header-anchor-link" href="#respondresult-for-ktor" aria-hidden="true"></a> <code>respondResult</code> for Ktor</h2>
<p data-line="291" class="code-line">Our group uses <a href="https://github.com/ktorio/ktor" target="_blank" rel="nofollow noopener noreferrer">Ktor</a> as the web framework. A function called <code>respondResult</code> was created to allow Result types from use cases to be returned directly as HTTP responses.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="293"><span class="token keyword">suspend</span> <span class="token keyword">inline</span> <span class="token keyword">fun</span> <span class="token operator"><</span><span class="token keyword">reified</span> T <span class="token operator">:</span> Any<span class="token operator">></span> ApplicationCall<span class="token punctuation">.</span><span class="token function">respondResult</span><span class="token punctuation">(</span>code<span class="token operator">:</span> HttpStatusCode<span class="token punctuation">,</span> result<span class="token operator">:</span> Result<span class="token operator"><</span>T<span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
result<span class="token punctuation">.</span><span class="token function">onSuccess</span> <span class="token punctuation">{</span>
<span class="token keyword">when</span> <span class="token punctuation">(</span>it<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">is</span> Unit <span class="token operator">-></span> <span class="token function">respond</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span>
<span class="token keyword">else</span> <span class="token operator">-></span> <span class="token function">respond</span><span class="token punctuation">(</span>code<span class="token punctuation">,</span> it<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">onFailure</span> <span class="token punctuation">{</span>
<span class="token comment">// defined below</span>
<span class="token function">respondError</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">suspend</span> <span class="token keyword">fun</span> ApplicationCall<span class="token punctuation">.</span><span class="token function">respondError</span><span class="token punctuation">(</span>error<span class="token operator">:</span> Throwable<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> response <span class="token operator">=</span> error<span class="token punctuation">.</span><span class="token function">toErrorResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">val</span> json <span class="token operator">=</span> serializer<span class="token punctuation">.</span><span class="token function">adapter</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>javaClass<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toJson</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span>
logger<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>json<span class="token punctuation">,</span> error<span class="token punctuation">)</span>

<span class="token function">respondText</span><span class="token punctuation">(</span>
    text <span class="token operator">=</span> json<span class="token punctuation">,</span>
    contentType <span class="token operator">=</span> ContentType<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Json<span class="token punctuation">,</span>
    status <span class="token operator">=</span> e<span class="token punctuation">.</span>errType<span class="token punctuation">.</span><span class="token function">toHttpStatusCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>

<span class="token punctuation">}</span>
</code></pre></div><p data-line="320" class="code-line">Although it's simple, using this function eliminates the need to call <code>Result.getOrThrow</code>, making the code a bit cleaner.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="322"><span class="token keyword">fun</span> Route<span class="token punctuation">.</span><span class="token function">route</span><span class="token punctuation">(</span>useCase<span class="token operator">:</span> UseCase<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> result <span class="token operator">=</span> useCase<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
call<span class="token punctuation">.</span><span class="token function">respondResult</span><span class="token punctuation">(</span>HttpStatusCode<span class="token punctuation">.</span>OK<span class="token punctuation">,</span> result<span class="token punctuation">.</span><span class="token function">map</span> <span class="token punctuation">{</span>it<span class="token punctuation">.</span><span class="token function">toViewModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><p data-line="329" class="code-line">By the way, <code>respondError</code> is a function that returns an error response from the throwable. We use this function to handle exceptions thrown in the Ktor pipeline and return appropriate responses. We've also created a custom Ktor plugin to handle exceptions.</p>
<div class="code-block-container"><pre class="language-kotlin"><code class="language-kotlin code-line" data-line="331"><span class="token keyword">val</span> ErrorHandler <span class="token operator">=</span> <span class="token function">createApplicationPlugin</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"ErrorHandler"</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">on</span><span class="token punctuation">(</span>CallFailed<span class="token punctuation">)</span> <span class="token punctuation">{</span> call<span class="token punctuation">,</span> cause <span class="token operator">-></span>
call<span class="token punctuation">.</span><span class="token function">respondError</span><span class="token punctuation">(</span>cause<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h1 id="conclusion" data-line="339" class="code-line"><a class="header-anchor-link" href="#conclusion" aria-hidden="true"></a> Conclusion</h1>
<p data-line="341" class="code-line">I introduced how our group handles errors, along with some helpful custom functions for the Result type. From what I've seen in various tech blogs, many companies seem to use <code>kotlin-result</code>, while there's relatively little information out there on using Kotlin's official Result type. We've found Kotlin's official Result type to be sufficient for error handling, so we encourage you to give it a try!</p>

Facebook

関連記事 | Related Posts

We are hiring!

リードエンジニア/プロジェクト推進G/東京・名古屋

業務内容バックエンド開発を中心に、サービスの企画から設計・開発・運用までプロダクトに関わっていただきます。サービスや会社の成長を考えて、自分やチームが何をすべきか自律的に動き、スピード感を持って開発に取り組むことを期待しています。

【PdM】契約管理システム開発G/東京

契約管理システム開発グループについて契約管理システム開発グループは、クルマのサブスクリプションサービス『KINTO』(新車・中古車)を中心とした国内向けサービスにおいて、申込から契約満了(中途解約を含む)までの社内業務プロセスのシステム化と、契約状態の管理を担っています。