<?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: Bohdan Turyk</title>
    <description>The latest articles on DEV Community by Bohdan Turyk (@benedya).</description>
    <link>https://dev.to/benedya</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F783211%2Fcfc0e4ca-f01d-499f-b88c-2a5201275a9e.png</url>
      <title>DEV Community: Bohdan Turyk</title>
      <link>https://dev.to/benedya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/benedya"/>
    <language>en</language>
    <item>
      <title>Architecture Is Even More Essential in the Age of AI</title>
      <dc:creator>Bohdan Turyk</dc:creator>
      <pubDate>Mon, 13 Jul 2026 11:53:32 +0000</pubDate>
      <link>https://dev.to/benedya/architecture-is-even-more-essential-in-the-age-of-ai-nil</link>
      <guid>https://dev.to/benedya/architecture-is-even-more-essential-in-the-age-of-ai-nil</guid>
      <description>&lt;p&gt;AI tools are changing the way we write software: code is becoming cheap and nearly instant. What hasn't changed is that systems still need to be understood, maintained, and grown. To me, that makes software architecture — the well-known fundamentals like SOLID, layered architecture, high cohesion and low coupling, design patterns, etc. — what keeps a system manageable and moving fast. It's something worth thinking about from day one.&lt;/p&gt;

&lt;p&gt;These fundamentals have been around for decades and proved their effectiveness long before AI entered the picture. In the age of AI, they seem to play an even more important role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;AI can generate any type of code, often faster than we can read it. Keeping the system manageable is becoming the real bottleneck — and without structure, a codebase can drift out of shape quicker than ever. The fundamentals can work as guardrails here: a proven, shared language that keeps the system in a form we can still understand and direct.&lt;/p&gt;

&lt;p&gt;Following these principles helps keep a system manageable and understandable, and brings the qualities that come with that — flexibility, extensibility, stability. These are what help an application move faster and stay robust: a well-structured system can be changed quickly and with confidence, so we keep moving fast largely &lt;em&gt;because&lt;/em&gt; of architecture, not despite it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Code is getting cheap; understanding it and keeping it under control is becoming the expensive part. Architecture keeps a system in shape — which makes it worth treating as a day-one concern. &lt;/p&gt;

&lt;p&gt;The code can be generated; the structure is still ours to shape.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>programming</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Anticorruption Layer in a Modular Application</title>
      <dc:creator>Bohdan Turyk</dc:creator>
      <pubDate>Wed, 19 Mar 2025 08:26:21 +0000</pubDate>
      <link>https://dev.to/benedya/anticorruption-layer-in-a-modular-application-53pe</link>
      <guid>https://dev.to/benedya/anticorruption-layer-in-a-modular-application-53pe</guid>
      <description>&lt;p&gt;In this article, I would like to describe an example of an anticorruption layer for modules based on a layered architecture. Hopefully, this will be useful to someone.&lt;/p&gt;

&lt;p&gt;To keep the article concise, I will try to be short in explanations/examples. Below, you can find a link to a more detailed example of this approach. And, of course, if you have any questions or thoughts, feel free to use the comments section. 😉&lt;/p&gt;

&lt;p&gt;So, let's get started. First, we will see a very short overview of layered architecture. Then, we will review an example of modules based on this architecture and identify issues with communication. After that, we will try to address these issues using the &lt;code&gt;Anticorruption&lt;/code&gt; layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short Overview of Layered Architecture
&lt;/h3&gt;

&lt;p&gt;The idea behind layered architecture is straightforward. Application logic is structured as a set of layers, ordered by importance (the deeper the layer, the more important it is) that follow dependency rule. In this article, we will use the &lt;code&gt;Domain&lt;/code&gt;, &lt;code&gt;Application&lt;/code&gt;, and &lt;code&gt;Infrastructure&lt;/code&gt; layers. It's quite similar to Clean Architecture.&lt;br&gt;
Here is illustration of it:&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
&amp;nbsp;&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%2Fyfyfatoyl1nv6fabu8xz.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%2Fyfyfatoyl1nv6fabu8xz.png" alt=" " width="688" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Domain&lt;/code&gt; layer contains entities and services with significant business rules (e.g., the &lt;code&gt;Order&lt;/code&gt; entity).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Application&lt;/code&gt; layer contains use cases (services) that the module offers (e.g., &lt;code&gt;CreateOrderService&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Infrastructure&lt;/code&gt; layer contains implementations of interfaces defined in the inner layers (low-level details) (e.g., &lt;code&gt;OrderRepository&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important thing here is the dependency rule (arrows on the screen). &lt;strong&gt;Dependencies should only be directed inward.&lt;/strong&gt; This means that a layer should not depend on or be aware of outer layers. In practice, this usually means that if you need to call a service from an outer layer, you should define an interface within the current layer that specifies the required functionality. However, the implementation should reside in the outer layer (dependency inversion principle).&lt;/p&gt;
&lt;h2&gt;
  
  
  Modules
&lt;/h2&gt;

&lt;p&gt;Let’s go a bit further and assume we have two modules, &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Notification&lt;/code&gt;, that follow this architecture. The &lt;code&gt;Notification&lt;/code&gt; module is responsible for sending notifications to a user, while the &lt;code&gt;User&lt;/code&gt; module provides notification settings for a user.&lt;/p&gt;

&lt;p&gt;According to this architecture, the structure of these modules could be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Module
├── Notification
│&amp;nbsp;&amp;nbsp; ├── Application
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── CreateNotificationUseCase.ts
│&amp;nbsp;&amp;nbsp; ├── Domain
│&amp;nbsp;&amp;nbsp; └── Infrastructure
└── User
    ├── Application
    │&amp;nbsp;&amp;nbsp; ├── ProvideNotificationSettingsUseCase.ts
    ├── Domain
    └── Infrastructure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To send a notification (handled by the &lt;code&gt;Notification&lt;/code&gt; module), we need to retrieve the user's notification settings (provided by the &lt;code&gt;User&lt;/code&gt; module).&lt;br&gt;
Strictly speaking, the &lt;code&gt;CreateNotificationUseCase&lt;/code&gt; needs to call &lt;code&gt;ProvideNotificationSettingsUseCase&lt;/code&gt;. However, doing this directly introduces several issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Violation of the dependency rule&lt;/strong&gt; — the &lt;code&gt;Application&lt;/code&gt; layer "knows" about something that doesn't belong to it (not even to its own module).&lt;/li&gt;
&lt;li&gt;Uncontrolled dependencies, which become difficult to manage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In simple terms, the &lt;code&gt;Notification&lt;/code&gt; module becomes corrupted by the &lt;code&gt;User&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;So, what would be a better approach?&lt;/p&gt;
&lt;h2&gt;
  
  
  Anticorruption layer
&lt;/h2&gt;

&lt;p&gt;We can define clear communication rules for these two modules and encapsulate them in an &lt;code&gt;Anticorruption&lt;/code&gt; layer (actually sublayer, will see shortly).&lt;/p&gt;

&lt;p&gt;According to layered architecture principles, the &lt;code&gt;Domain&lt;/code&gt; and &lt;code&gt;Application&lt;/code&gt; layers contain the core business logic of a module and should not include low-level implementation details (such as database queries). Let's stick to this principle.&lt;/p&gt;

&lt;p&gt;To achieve this, we will create an interface in the &lt;code&gt;Application&lt;/code&gt; layer that provides the required functionality (retrieving notification settings) for the current layer. The implementation of this interface will reside in the &lt;code&gt;Infrastructure&lt;/code&gt; layer of this module, treating it as a low-level detail.&lt;/p&gt;

&lt;p&gt;Additionally, to maintain better control over such dependencies, we will place them in an &lt;code&gt;Anticorruption&lt;/code&gt; folder/sublayer within the &lt;code&gt;Infrastructure&lt;/code&gt; layer. Each module will have its own dedicated folder within this sublayer. This approach makes dependencies between modules explicit.&lt;/p&gt;

&lt;p&gt;Now, let's look at the updated module structure (pay attention to the &lt;code&gt;ADDED&lt;/code&gt; sections):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Module
├── Notification
│&amp;nbsp;&amp;nbsp; ├── Application
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── CreateNotificationUseCase.ts
│&amp;nbsp;&amp;nbsp; │   └── UserNotificationsSettingsProviderInterface.ts &lt;span class="c"&gt;# ADDED - defines required functionality for this layer&lt;/span&gt;
│&amp;nbsp;&amp;nbsp; ├── Domain
│&amp;nbsp;&amp;nbsp; └── Infrastructure
│&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;  └── Anticorruption  &lt;span class="c"&gt;# ADDED&lt;/span&gt;
│&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;      └── User &lt;span class="c"&gt;# Name of the module this one depends on&lt;/span&gt;
│&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;          └── UserNotificationsSettingsProvider.ts &lt;span class="c"&gt;# Implementation of the interface defined in the Application layer&lt;/span&gt;
└── User
    ├── Application
    │&amp;nbsp;&amp;nbsp; ├── ProvideNotificationSettingsUseCase.ts
    ├── Domain
    └── Infrastructure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, so good. Now, let’s take a closer look at the logic within these layers, starting with the &lt;code&gt;Application&lt;/code&gt; layer of the &lt;code&gt;Notification&lt;/code&gt; module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Module/Notification/Application/CreateNotificationUseCase.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateNotificationUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;userNotificationsSettingsProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserNotificationsSettingsProviderInterface&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;async&lt;/span&gt; &lt;span class="nf"&gt;createNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NotificationPayload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userNotificationsSettingsProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userUuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// todo send notification based on settings&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// src/Module/Notification/Application/UserNotificationsSettingsProviderInterface.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UserNotificationsSettingsProviderInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userUuid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NotificationsSettings&lt;/span&gt;&lt;span class="o"&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;As seen in the example above, we have not introduced any direct dependencies on the &lt;code&gt;User&lt;/code&gt; module within the &lt;code&gt;Application&lt;/code&gt; layer. This is great because this layer should not depend on outer layers (following the dependency rule).&lt;/p&gt;

&lt;p&gt;Instead, we created the &lt;code&gt;UserNotificationsSettingsProviderInterface&lt;/code&gt; interface, which is sufficient for &lt;code&gt;CreateNotificationUseCase&lt;/code&gt;. This inverts the dependency.&lt;/p&gt;

&lt;p&gt;Now, let’s see how this interface is implemented in the &lt;code&gt;Anticorruption&lt;/code&gt; layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Module/Notification/Infrastructure/Anticorruption/User/UserNotificationsSettingsProvider.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserNotificationsSettingsProviderInterface&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@Module/Notification/Application/UserNotificationsSettingsProviderInterface&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ProvideNotificationSettingsUseCase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@Module/User/Application/ProvideNotificationSettingsUseCase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserNotificationsSettingsProvider&lt;/span&gt;
  &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;UserNotificationsSettingsProviderInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// inject service from User module&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;provideNotificationSettingsUseCase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProvideNotificationSettingsUseCase&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;async&lt;/span&gt; &lt;span class="nf"&gt;getSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userUuid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NotificationsSettings&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userNotificationsSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provideNotificationSettingsUseCase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserNotificationsSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;userUuid&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="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;userNotificationsSettings&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;As you can see, we injected &lt;code&gt;ProvideNotificationSettingsUseCase&lt;/code&gt; from the &lt;code&gt;User&lt;/code&gt; module. That's okay because this implementation resides in the &lt;code&gt;Infrastructure&lt;/code&gt; layer.&lt;/p&gt;

&lt;p&gt;This is the only place where communication between modules happens. The communication looks manageable, clear, and does not break the dependency rule. &lt;/p&gt;

&lt;p&gt;You can find a more detailed version of this approach in my GitHub repository:&lt;br&gt;
&lt;a href="https://github.com/benedya/nestjs-layered-architecture" rel="noopener noreferrer"&gt;https://github.com/benedya/nestjs-layered-architecture&lt;/a&gt;&lt;br&gt;
Additionally, this repository includes preconfigured ESLint rules that help track dependency violations between layers.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>programming</category>
      <category>learning</category>
    </item>
    <item>
      <title>Domain vs Application Services: what is the difference</title>
      <dc:creator>Bohdan Turyk</dc:creator>
      <pubDate>Sat, 22 Feb 2025 05:31:12 +0000</pubDate>
      <link>https://dev.to/benedya/draft-domain-vs-application-services-what-is-difference-2867</link>
      <guid>https://dev.to/benedya/draft-domain-vs-application-services-what-is-difference-2867</guid>
      <description>&lt;p&gt;I've been learning and applying Domain-Driven Design (DDD) principles for a few years, and I often struggle with distinguishing between domain and application services. In this article, I’d like to share some observations and thoughts based on my experience. Hopefully, you’ll find something useful here!&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach
&lt;/h3&gt;

&lt;p&gt;I’ll first explore services in a complex domain and then in a simple one. After that, I’ll summarize my thoughts.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Complex Domain
&lt;/h2&gt;

&lt;p&gt;It seems easier to separate domain and application services in a complex domain. Let’s illustrate this with an example.&lt;/p&gt;

&lt;p&gt;Imagine a banking application where we need logic to transfer money from one account to another. Below is a simplified snippet demonstrating possible application and domain services for that purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Domain/Entity/Account.ts&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// src/Domain/Service/MoneyTransferDomainService.ts&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MoneyTransferDomainService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;transferMoney&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&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="nx"&gt;fromAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;amount&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Insufficient funds.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;fromAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;toAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;amount&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;// src/Application/MoneyTransferApplicationService.ts&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MoneyTransferApplicationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;transferMoney&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromAccountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toAccountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fromAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accountRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromAccountId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accountRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toAccountId&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fromAccount&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Account with id "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fromAccountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" not found`&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;toAccount&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Account with id "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;toAccountId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" not found`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moneyTransferDomainService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transferMoney&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&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 example, the &lt;code&gt;MoneyTransferDomainService&lt;/code&gt; encapsulates a crucial business rule: &lt;strong&gt;moving money between accounts&lt;/strong&gt;. It belongs in the domain layer because it represents core business logic. Meanwhile, the &lt;code&gt;MoneyTransferApplicationService&lt;/code&gt; acts as a coordinator, retrieving the necessary entities and delegating the core transfer logic to the domain service.&lt;/p&gt;

&lt;p&gt;This separation makes sense. We have a main part that belongs to domain layer and a secondary that belongs to application layer. Let's explore at a few alternatives applicable to this example.&lt;/p&gt;

&lt;h4&gt;
  
  
  Possible Alternatives
&lt;/h4&gt;

&lt;h5&gt;
  
  
  1. Placing the logic inside the &lt;code&gt;Account&lt;/code&gt; entity
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;This could lead to bloated entities (a “god class”).&lt;/li&gt;
&lt;li&gt;It sometimes unclear which entity should contain the logic when multiple entities are involved. In a more complex example with &lt;code&gt;PersonalAccount&lt;/code&gt; and &lt;code&gt;BusinessAccount&lt;/code&gt;, it would be harder to decide where to place this logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  2. Moving the logic to &lt;code&gt;MoneyTransferApplicationService&lt;/code&gt;
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;This would cause domain logic to leak into the application layer.&lt;/li&gt;
&lt;li&gt;The application layer would gain more responsibilities, some of which may seem redundant.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Hints for Identifying a Domain Service
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;The logic is crucial for the business.&lt;/li&gt;
&lt;li&gt;The logic involves interactions between multiple entities.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Simple Domain
&lt;/h2&gt;

&lt;p&gt;Now, let’s consider an example from a simpler domain. Suppose we have a user management component where we need to update a &lt;code&gt;User&lt;/code&gt; entity. Here’s a relevant snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Domain/Entity/User.ts&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// src/Application/UpdateUserApplicationService.ts&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdateUserApplicationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`User with id "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" not found`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we only have an application service, which looks okay since updating a user’s details does’t look like a complex business rules that should be encapsulated in the heart of the application. &lt;/p&gt;

&lt;p&gt;If there is no significant business rules it's hard to find reasons moving such logic into domain layer. In such cases having only application services look enough.&lt;/p&gt;

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

&lt;p&gt;So, what’s the difference then? It's still a tough question for me. But one guideline seems clear: &lt;strong&gt;domain services should encapsulate significant business rules.&lt;/strong&gt; The more essential a piece of logic is to the business, the more it navigates toward the domain layer.  Conversely, if the logic is less central to the business or mainly coordinates processes, it tends to the application layer. However, this distinction isn’t always straightforward.&lt;/p&gt;

&lt;p&gt;At times, I question whether creating domain services is even necessary. In such cases, Clean Architecture principles can be appealing. Instead of debating which layer a piece of logic belongs to, Clean Architecture offers a clear answer: it should be placed within a use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources
&lt;/h3&gt;

&lt;p&gt;If you’d like to dive deeper into these topics, here are some useful references:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;Domain-Driven Design&lt;/em&gt; by Eric Evans (Chapter Five: A Model Expressed in Software – Services)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Clean Architecture&lt;/em&gt; by Robert C. Martin (Part V: Architecture, Chapter 22: The Clean Architecture – Use Cases)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://khalilstemmler.com/articles/software-design-architecture/domain-driven-design-vs-clean-architecture/" rel="noopener noreferrer"&gt;Domain-Driven Design vs. Clean Architecture – Khalil Stemmler&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;This is my first post—hope you found it helpful! :)&lt;/p&gt;

</description>
      <category>ddd</category>
      <category>architecture</category>
      <category>cleancode</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
