<?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: Cyril Sahula</title>
    <description>The latest articles on DEV Community by Cyril Sahula (@cyrilsahula).</description>
    <link>https://dev.to/cyrilsahula</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%2F588710%2Fcc8b48fb-b2e1-4b3d-984e-3fd9211cc4f7.jpeg</url>
      <title>DEV Community: Cyril Sahula</title>
      <link>https://dev.to/cyrilsahula</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cyrilsahula"/>
    <language>en</language>
    <item>
      <title>Dapr in Azure Container Apps</title>
      <dc:creator>Cyril Sahula</dc:creator>
      <pubDate>Sat, 10 May 2025 04:06:43 +0000</pubDate>
      <link>https://dev.to/cyrilsahula/building-immutable-collection-dynamically-in-kotlin-13g2</link>
      <guid>https://dev.to/cyrilsahula/building-immutable-collection-dynamically-in-kotlin-13g2</guid>
      <description>&lt;p&gt;We decided to use &lt;a href="https://azure.microsoft.com/en-us/products/container-apps" rel="noopener noreferrer"&gt;Azure Container Apps&lt;/a&gt; as a managed Kubernetes platform because it offers everything we need for our project, with acceptable limitations. During the process, we realised that Microsoft includes managed &lt;a href="https://dapr.io" rel="noopener noreferrer"&gt;Dapr&lt;/a&gt; as part of the service—and we decided to use it. Why? I explain below—and I still don't regret it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Dapr?
&lt;/h2&gt;

&lt;p&gt;Dapr (Distributed Application Runtime) is an open-source, portable runtime that helps developers focus on coding and delivering business value without dealing with platform complexity or requiring deep infrastructure knowledge. &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%2F4jwz516wcuow3ehpo0lg.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%2F4jwz516wcuow3ehpo0lg.png" alt="Dapr usage case" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dapr showcase&lt;/p&gt;

&lt;p&gt;It provides &lt;a href="https://docs.dapr.io/concepts/building-blocks-concept/" rel="noopener noreferrer"&gt;building blocks&lt;/a&gt;—such as service invocation, pub/sub messaging, state management, and bindings—for cloud-native development.&lt;br&gt;
The key point? These features are available via standard HTTP or gRPC APIs, abstracting away the underlying infrastructure.&lt;br&gt;
You don’t need to deeply integrate SDKs or libraries. Instead, your service talks to the Dapr sidecar over a local network interface, and Dapr handles the rest. That simplicity is both elegant and powerful.&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%2Ffxmcxzebtwcf68ywk4tr.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%2Ffxmcxzebtwcf68ywk4tr.png" alt="Components overview" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud-Agnostic for free
&lt;/h2&gt;

&lt;p&gt;Being cloud-agnostic wasn’t our primary goal—but it turns out that Dapr gives us that capability almost for free. It decouples application logic from the underlying infrastructure.&lt;br&gt;
Need a message broker? Use Dapr’s pub/sub API. Whether it’s backed by Azure Service Bus, Kafka, or Redis doesn't matter to the application. The same applies to state stores, secret management, and service discovery. This gives us the flexibility to adapt our infrastructure without touching application code.&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%2Fl41vqrh28dq2cnhytjv6.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%2Fl41vqrh28dq2cnhytjv6.png" alt="Overview of proxy blocks with services behind" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dapr Is Perfect When Managed
&lt;/h2&gt;

&lt;p&gt;In a managed environment like Azure Container Apps, Dapr becomes almost invisible to developers. Azure takes care of provisioning, scaling, and running the Dapr sidecars. There’s no need to manage Kubernetes manifests, sidecar injection, or upgrades.&lt;br&gt;
This “it just works” experience allows our developers to focus on building features, not managing infrastructure. For us, Dapr in ACA became a seamless DevOps-to-code boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lightweight services
&lt;/h2&gt;

&lt;p&gt;In a traditional microservice architecture, each service often includes direct dependencies on external systems like Azure Key Vault, Azure Service Bus (or Kafka/RabbitMQ), Redis, and others. This leads to increased complexity, heavier containers, and tighter coupling. With Dapr, these concerns are abstracted behind a single, lightweight SDK. The Dapr sidecar handles integrations, allowing the microservice itself to remain lean, portable, and easier to maintain. The SDK is well-supported and actively maintained by a large, vibrant open-source community.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dapr Can Be Risky When Unmanaged
&lt;/h2&gt;

&lt;p&gt;Dapr is a powerful abstraction—but like any abstraction, it has trade-offs. Running Dapr in an unmanaged environment (such as raw Kubernetes) requires caution. The sidecar must be healthy and synchronized with your app. Misconfigurations, version mismatches, or sidecar issues can lead to subtle bugs or outages.&lt;br&gt;
While Dapr’s tooling is improving, the operational complexity in unmanaged environments can outweigh the benefits. That’s why we only recommend using Dapr where it’s either fully managed or very well automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dapr Moves Infrastructure Dependencies to DevOps
&lt;/h2&gt;

&lt;p&gt;Traditionally, developers had to integrate directly with infrastructure services like queues, databases, or caches. Dapr flips that model. Developers use Dapr APIs, and it’s the DevOps team that configures how those APIs connect to real infrastructure under the hood.&lt;br&gt;
Need to switch from Redis to Cosmos DB? Just change the component configuration—no code changes. This separation of concerns reduces cognitive load and speeds up iteration. Developers gain velocity, and operators retain control.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Tech Stacks ONE knowledge
&lt;/h2&gt;

&lt;p&gt;Since Dapr communicates over HTTP and gRPC, it doesn’t care what language your service is written in. Whether it's Node.js, Go, Python, or .NET, all services can interact with Dapr using the same approach—via the provided SDKs.&lt;br&gt;
Even in a small project like dream.jobs, we benefit from this flexibility. The backend is written in Kotlin, server-side rendering for the browser is done in Node.js, and AI components are in Python. All services use the Dapr SDK and share a single configuration in the cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstraction = some disadvantages
&lt;/h2&gt;

&lt;p&gt;Dapr’s abstraction is powerful, but it’s also generalized. Its APIs are designed to cover common patterns across many tools—which means they sometimes expose only a subset of what a specific service can do.&lt;br&gt;
For example, Dapr’s pub/sub API may not support all advanced features of Azure Service Bus or Kafka. Similarly, its state store abstraction won’t provide full access to things like Cosmos DB’s consistency levels or Redis eviction policies.&lt;br&gt;
That’s a trade-off we accept. For 90% of our use cases, the Dapr API is sufficient. But when we need a specific capability—like dead-letter queues or fine-tuned partitioning—we can bypass Dapr and talk directly to the underlying service. Dapr doesn’t block us; it just offers a solid default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Most of the key benefits and risks are described above, but what I appreciate most is how Dapr helps the team be more efficient. Developers can focus on writing code and solving business problems, while a small DevOps team—even just one experienced engineer—can provide a scalable, secure platform.&lt;br&gt;
In the end, the overall efficiency of your development team can be much higher.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>dapr</category>
      <category>kubernetes</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Building immutable collection dynamically in Kotlin</title>
      <dc:creator>Cyril Sahula</dc:creator>
      <pubDate>Tue, 22 Apr 2025 12:38:37 +0000</pubDate>
      <link>https://dev.to/cyrilsahula/building-immutable-collection-dynamically-in-kotlin-2mnb</link>
      <guid>https://dev.to/cyrilsahula/building-immutable-collection-dynamically-in-kotlin-2mnb</guid>
      <description>&lt;h2&gt;
  
  
  When and Why to Use Kotlin Collection Builders
&lt;/h2&gt;

&lt;p&gt;If there's a requirement to dynamically construct a collection based on certain conditions, two main approaches are available in Kotlin:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using a mutable collection directly&lt;/li&gt;
&lt;li&gt;Employing a collection builder function (e.g., buildSet, buildList, buildMap)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The collection builder approach still uses a mutable collection internally, but the benefit lies in its encapsulation. This leads to cleaner and more concise code, and the final result is an immutable collection.&lt;/p&gt;

&lt;p&gt;This post is short and clear although I have met so much mutable collection in projects that is nice to remain it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutable way
&lt;/h2&gt;

&lt;p&gt;It works but collction must be muttable and code is less fluent.&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;mapToWorkTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;externalTypes&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="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;WorkType&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;workTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;WorkType&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;mutableSetOf&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;externalTypes&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;"#001"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;workTypes&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="nc"&gt;EMPLOYEE&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;externalTypes&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;"#002"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;workTypes&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="nc"&gt;CONTRACTOR&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;externalTypes&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;"#003"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;workTypes&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="nc"&gt;SEASONAL&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;externalTypes&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;"#004"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;workTypes&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="nc"&gt;INTERN&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;externalTypes&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;"#005"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;workTypes&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="nc"&gt;CONTRACTOR&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;workTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="n"&gt;workTypes&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="nc"&gt;EMPLOYEE&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;workTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toSet&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;h2&gt;
  
  
  Immutable way
&lt;/h2&gt;

&lt;p&gt;The code below uses collection builder. Check &lt;a href="https://www.sahula.me/post/building-immutable-collections-dynamically" rel="noopener noreferrer"&gt;documentations&lt;/a&gt; of collection builders buildSet, buildList, buildMap.&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;mapToWorkTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;externalTypes&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;buildSet&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;externalTypes&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;"#001"&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="nc"&gt;EMPLOYEE&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;externalTypes&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;"#002"&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="nc"&gt;CONTRACTOR&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;externalTypes&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;"#003"&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="nc"&gt;SEASONAL&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;externalTypes&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;"#004"&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="nc"&gt;INTERN&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;externalTypes&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;"#005"&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="nc"&gt;CONTRACTOR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;ifEmpty&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="nc"&gt;EMPLOYEE&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;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using a mutable collection directly requires more boilerplate and an explicit conversion to an immutable form via .toSet(). It also increases the risk of logic errors, such as accidentally checking or modifying the wrong collection (workTypes vs. externalTypes).&lt;br&gt;
In contrast, collection builders provide a cleaner and more concise alternative:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less boilerplate&lt;/li&gt;
&lt;li&gt;Clearer logic&lt;/li&gt;
&lt;li&gt;Automatically return immutable collections&lt;/li&gt;
&lt;li&gt;Encourage encapsulated and expressive code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever possible, prefer collection builders for constructing collections based on dynamic conditions.&lt;/p&gt;

</description>
      <category>kotlin</category>
    </item>
    <item>
      <title>DDD-like Kotlin object cooperating with Spring context</title>
      <dc:creator>Cyril Sahula</dc:creator>
      <pubDate>Fri, 18 Apr 2025 10:22:02 +0000</pubDate>
      <link>https://dev.to/cyrilsahula/ddd-like-kotlin-object-cooperating-with-spring-context-1c7c</link>
      <guid>https://dev.to/cyrilsahula/ddd-like-kotlin-object-cooperating-with-spring-context-1c7c</guid>
      <description>&lt;p&gt;My friend Dominik helped me figure out which direction to take my coding skills. I never really liked the standard industry patterns that come with the Spring framework, where code is split into APIs, services, and repositories communicating via DTOs, DAOs, etc. I missed encapsulation and clear business objects. Plus, I enjoy both functional and OOP programming, and this layered approach often goes against those principles. DDD is full of abstract rules, but I decided to follow two simple ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain objects are created on the edge of the domain layer or from another domain object.&lt;/li&gt;
&lt;li&gt;A domain object is a real object — it contains both data and business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Kotlin implementation
&lt;/h2&gt;

&lt;p&gt;Sticking to this leads to fluent, readable code that's easy to test and keeps domain problems well-encapsulated. Unfortunately, the Spring framework doesn’t really support this approach by default — it manages beans and stores them in the application context. So the question became: how can I keep clean, OOP-style domain objects while still calling functions that rely on the application context, like opening a DB transaction?&lt;br&gt;
The example below shows that if a function from a bean (usually a repository) is passed into a domain object as a lambda, the Spring context remains available — and even Spring aspects continue to work as expected.&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;// GraphQl endpoint and edge of a domain layer&lt;/span&gt;
&lt;span class="nd"&gt;@DgsComponent&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeleteCertificateMutation&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;certificateDbAdapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CertificateDbAdapter&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;DomainValidator&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;@DgsMutation&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;deleteCertificate&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;String&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="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsCertificate&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="nf"&gt;deleteCertificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;certificateDbAdapter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;deleteCertificate&lt;/span&gt;&lt;span class="p"&gt;,&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Snipped from Certificate domain object &lt;/span&gt;
&lt;span class="k"&gt;open&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;deleteCertificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;deleteCertificate&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;CertificateId&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="n"&gt;getSupplier&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;CertificateId&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;SupplierId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Either&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NotAdminError&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;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// some business logic before like checking right etc.&lt;/span&gt;
        &lt;span class="nf"&gt;deleteCertificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="nd"&gt;@CertificateId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt; 
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;supplier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recalculateStatus&lt;/span&gt;&lt;span class="p"&gt;()&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, there are also ways to avoid relying on the Spring context between the domain and repository layers, but in my case, I needed to use Spring Data R2DBC — probably the only reactive SQL database library that supports transactions — and it relies on aspects.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;This approach allows you to have clean, OOP-style domain objects without losing the benefits of the Spring Framework environment.&lt;/li&gt;
&lt;li&gt;Domain objects are easy to unit test without needing mocking frameworks — which often lead to brittle tests focused too much on implementation details.&lt;/li&gt;
&lt;li&gt;To save time, I still use mock() for method or constructor arguments that aren't relevant to the test itself.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kotlin</category>
      <category>ddd</category>
      <category>spring</category>
      <category>springboot</category>
    </item>
  </channel>
</rss>
