<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Anil Gurau]]></title><description><![CDATA[Anil Gurau]]></description><link>https://blog.anilgurau.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 24 Apr 2026 15:29:39 GMT</lastBuildDate><atom:link href="https://blog.anilgurau.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Understanding Middleware in ASP.NET Core]]></title><description><![CDATA[Web applications can quickly become a mess, especially when handling repetitive tasks like user authentication, logging, and error management for every request. Fortunately, .NET's middleware offers a structured solution to these challenges.
The Basi...]]></description><link>https://blog.anilgurau.com/understanding-middleware-in-aspnet-core</link><guid isPermaLink="true">https://blog.anilgurau.com/understanding-middleware-in-aspnet-core</guid><category><![CDATA[.NET]]></category><category><![CDATA[.net core]]></category><dc:creator><![CDATA[Anil gurau]]></dc:creator><pubDate>Tue, 08 Apr 2025 19:32:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746727709413/714fbc72-a46a-4890-ae87-8bead11704b0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Web applications can quickly become a mess, especially when handling repetitive tasks like user authentication, logging, and error management for every request. Fortunately, .NET's middleware offers a structured solution to these challenges.</p>
<h2 id="heading-the-basics-of-aspnet-core-middleware">The Basics of ASP.NET Core Middleware</h2>
<p>Middleware in ASP.NET Core is a building block of the HTTP request pipeline. But what exactly is the HTTP request pipeline?</p>
<p>Imagine a series of interconnected stations, each performing a specific task. When a user sends a request to your ASP.NET Core web application, it doesn't immediately reach your application's core logic. Instead, it enters this pipeline. Each station in the pipeline, or each middleware component, has the opportunity to inspect, modify, or even short-circuit the request.</p>
<h3 id="heading-here-is-how-the-request-flows">Here is how the Request flows:</h3>
<ol>
<li><strong>Request Arrives at the Web Server (IIS or Kestrel):</strong></li>
</ol>
<ul>
<li>When a user sends a request, it first reaches your web server, typically either IIS (for Windows servers) or Kestrel (a cross-platform web server built into ASP.NET Core).</li>
</ul>
<ol start="2">
<li><strong>HttpContext is Generated:</strong></li>
</ol>
<ul>
<li>The web server then passes the request to the ASP.NET Core runtime. Here, <code>HttpContext</code> object is created from the request. This object holds all the details of the request, such as headers, the request body, and URL parameters.</li>
</ul>
<ol start="3">
<li><strong>Request Enters the Middleware Pipeline:</strong></li>
</ol>
<ul>
<li>The <code>HttpContext</code> object now enters the middleware pipeline, where a series of middleware process the request.</li>
</ul>
<ol start="4">
<li><strong>Middleware Components Process the Request:</strong></li>
</ol>
<ul>
<li><p>Each middleware component in the pipeline performs a specific task, in a defined order.</p>
<ul>
<li><p>For example, an authentication middleware might check user login details from the <code>HttpContext</code>.</p>
</li>
<li><p>A logging middleware logs the information from the request.</p>
</li>
<li><p>An error handling middleware might manage any errors that occur during the request.</p>
</li>
</ul>
</li>
</ul>
<ol start="5">
<li><strong>Routing Determines the Endpoint:</strong></li>
</ol>
<ul>
<li>After the middleware components have processed the request, the routing middleware determines which part of your application (endpoint) should handle it. This could be a controller action in your API or a page in your web application.</li>
</ul>
<ol start="6">
<li><strong>Endpoint Executes and generates a response:</strong></li>
</ol>
<ul>
<li>The selected endpoint then runs, using the information from the <code>HttpContext</code> and a response is generated.</li>
</ul>
<ol start="7">
<li><strong>Response Sent to User via Middleware (in Reverse Order):</strong></li>
</ol>
<ul>
<li>The generated response now travels back through the middleware pipeline, but in reverse order. This allows each middleware component to perform any necessary post-processing tasks before the web server (IIS or Kestrel) sends the final response back to the user.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743648579676/f8f7caf0-7e46-4570-9636-f08e5ab62565.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-why-middleware">Why Middleware?</h2>
<p>Imagine building a Web API that requires a number of actions to be performed for every incoming request. You might need to handle user authentication and authorization, implement rate limiting, provide centralized exception handling, and maintain logs. Without a centralized solution, you'll likely end up with duplicated code and a difficult-to-maintain application.</p>
<p>Consider a common scenario: logging request information. Without middleware, you might have logging code scattered throughout your API endpoints:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> IActionResult <span class="hljs-title">GetData</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> id</span>)</span>
{
    <span class="hljs-keyword">var</span> requestPath = HttpContext.Request.Path;
    _logger.LogInformation(<span class="hljs-string">$"Request to <span class="hljs-subst">{requestPath}</span> with id: <span class="hljs-subst">{id}</span>"</span>);
    <span class="hljs-comment">// logic to get data</span>
    <span class="hljs-keyword">return</span> Ok(data);
}

<span class="hljs-function"><span class="hljs-keyword">public</span> IActionResult <span class="hljs-title">CreateData</span>(<span class="hljs-params">Data data</span>)</span>
{
    <span class="hljs-keyword">var</span> requestPath = HttpContext.Request.Path;
    _logger.LogInformation(<span class="hljs-string">$"Request to <span class="hljs-subst">{requestPath}</span>"</span>);
    <span class="hljs-comment">// logic to create data</span>
    <span class="hljs-keyword">return</span> CreatedAtAction(<span class="hljs-keyword">nameof</span>(GetData), <span class="hljs-keyword">new</span> { id = data.Id }, data);
}
</code></pre>
<p>As you add more endpoints, this duplicated logging code becomes increasingly difficult to manage. If you need to change the logging behavior (e.g. add more details, change the log format), you'd have to modify it in multiple places.</p>
<p>To address this duplication issue, middleware effectively centralizes this common logging logic into a reusable component, applied to every request. This centralized approach simplifies development, enhances maintainability, and ensures consistency.</p>
<h2 id="heading-the-middleware-pipeline-ordering-matters">The Middleware Pipeline: Ordering Matters</h2>
<p>The order in which middleware components are added to the pipeline is important. For instance, authentication middleware should typically come before authorization middleware. If the order is reversed, authorization might be attempted before the user's identity is established, leading to unexpected behavior. Similarly, error handling middleware should be placed early in the pipeline to catch exceptions thrown by subsequent components. If placed after other middleware, it might miss exceptions from earlier stages, resulting in unhandled errors.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743663636449/d817ad7f-b9f6-4d16-9c05-f621ec32f5bf.png" alt class="image--center mx-auto" /></p>
<p>As the diagram above illustrates, the sequence of middleware components directly affects the request/response flow. In this example, if the 'Endpoint Middleware' handles the request and generates a response, the request never reaches the 'Authentication' and 'Authorization' middleware.</p>
<h2 id="heading-short-circuiting-the-pipeline">Short-Circuiting the pipeline</h2>
<p>Middleware can "short-circuit" the pipeline, stopping further processing and sending a response directly. This is crucial for security and performance.</p>
<p>For example, authentication middleware might return a 401 Unauthorized response, preventing unauthenticated requests from reaching your application. Similarly, error handling middleware placed early in the pipeline ensures that exceptions from other components are caught, preventing unhandled errors and potential security risks.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743664149810/30ea2894-610c-437e-b829-d9f242ea8109.png" alt class="image--center mx-auto" /></p>
<p>The diagram above demonstrates how authentication and authorization middleware interact and how short-circuiting can occur. Authentication middleware verifies user credentials, and if valid, associates the authenticated user with the current request. Authorization middleware then checks if the user is authorized to access the requested resource. If the user is not allowed, the authorization middleware will short-circuit the pipeline and generate an Unauthorized response, preventing the request from reaching the application's core logic.</p>
<h2 id="heading-building-a-custom-exception-handling-middleware-with-logging">Building a Custom Exception Handling Middleware with Logging</h2>
<p>Middleware's way of managing requests gives you a lot of flexibility, and one great use is for handling errors and logging them yourself. This custom approach in your <a target="_blank" href="http://ASP.NET">ASP.NET</a> Core application can make things smoother for users and easier to figure out when things go wrong, beyond what the default settings offer.  </p>
<p><strong>Step 1: Creating the Custom Middleware</strong></p>
<p>We define a custom middleware class, <code>ExceptionHandlingMiddleware</code>. This middleware follows the standard <a target="_blank" href="http://ASP.NET">ASP.NET</a> Core pattern, requiring a constructor that takes a <code>RequestDelegate</code> (representing the next middleware in the pipeline) and an <code>InvokeAsync</code> method that handles the request. We also inject an <code>ILogger</code> instance to enable logging of exceptions.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ExceptionHandlingMiddleware</span>
{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> RequestDelegate _next;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ILogger&lt;ExceptionHandlingMiddleware&gt; _logger;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ExceptionHandlingMiddleware</span>(<span class="hljs-params">RequestDelegate next, ILogger&lt;ExceptionHandlingMiddleware&gt; logger</span>)</span>
    {
        _next = next;
        _logger = logger;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InvokeAsync</span>(<span class="hljs-params">HttpContext context</span>)</span>
    {
        <span class="hljs-keyword">try</span>
        {
            <span class="hljs-keyword">await</span> _next(context);
        }
        <span class="hljs-keyword">catch</span> (Exception ex)
        {
            <span class="hljs-keyword">await</span> HandleExceptionAsync(context, ex, _logger);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">HandleExceptionAsync</span>(<span class="hljs-params">HttpContext context, Exception exception, ILogger&lt;ExceptionHandlingMiddleware&gt; logger</span>)</span>
    {
        HttpStatusCode statusCode;
        <span class="hljs-keyword">string</span> message;

        <span class="hljs-keyword">switch</span> (exception)
        {
            <span class="hljs-keyword">case</span> AuthenticationException ex:
                statusCode = HttpStatusCode.Unauthorized;
                message = <span class="hljs-string">"Authentication failed."</span>;
                logger.LogWarning(exception, <span class="hljs-string">"Authentication failure."</span>);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> UnauthorizedAccessException ex:
                statusCode = HttpStatusCode.Unauthorized;
                message = <span class="hljs-string">"You are not authorized to access this resource."</span>;
                logger.LogWarning(exception, <span class="hljs-string">"Unauthorized access attempt."</span>);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> KeyNotFoundException _:
                statusCode = HttpStatusCode.NotFound;
                message = <span class="hljs-string">"The requested resource was not found."</span>;
                logger.LogWarning(exception, <span class="hljs-string">"Resource not found."</span>);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> ArgumentException _:
                statusCode = HttpStatusCode.BadRequest;
                message = <span class="hljs-string">"Invalid request parameters."</span>;
                logger.LogWarning(exception, <span class="hljs-string">"Invalid request."</span>);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">default</span>:
                statusCode = HttpStatusCode.InternalServerError;
                message = <span class="hljs-string">"An unexpected error occurred. Please try again later."</span>;
                logger.LogError(exception, <span class="hljs-string">"Unhandled exception occurred."</span>);
                <span class="hljs-keyword">break</span>;
        }

        context.Response.ContentType = <span class="hljs-string">"application/json"</span>;
        context.Response.StatusCode = (<span class="hljs-keyword">int</span>)statusCode;

        <span class="hljs-keyword">var</span> errorResponse = <span class="hljs-keyword">new</span> { StatusCode = (<span class="hljs-keyword">int</span>)statusCode, Message = message };
        <span class="hljs-keyword">var</span> jsonResponse = JsonSerializer.Serialize(errorResponse);

        <span class="hljs-keyword">await</span> context.Response.WriteAsync(jsonResponse);
    }
}
</code></pre>
<p>The <code>InvokeAsync</code> method operates by attempting to execute the subsequent middleware in the pipeline (<code>_next(context)</code>). If any exception is thrown during this process, the <code>catch</code> block is executed. Here, the exception is logged using the injected <code>ILogger</code>. The <code>HandleExceptionAsync</code> method then formats an error response as JSON, which is sent back to the client.</p>
<p><strong>Step 2: Registering the Middleware</strong></p>
<p>To integrate the <code>ExceptionHandlingMiddleware</code> into the request pipeline, it is registered in the <code>Startup.cs</code> or <code>Program.cs</code>(.Net core 6.0 and later) using <code>app.UseMiddleware&lt;ExceptionHandlingMiddleware&gt;()</code> . Because the goal is to capture all unhandled exceptions that might occur during the processing of a request, it's crucial to register this middleware early in the pipeline. This ensures that it wraps the execution of subsequent middleware components, allowing it to intercept any exceptions before they reach the default error handling mechanisms.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> app = builder.Build();

<span class="hljs-comment">// Configure the HTTP request pipeline.</span>
<span class="hljs-keyword">if</span> (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseMiddleware&lt;ExceptionHandlingMiddleware&gt;();
app.UseAuthorization();

app.MapControllers();

app.Run();
</code></pre>
<p><strong>Step 3: Testing the Middleware</strong></p>
<p>To demonstrate the functionality of the <code>ExceptionHandlingMiddleware</code>, an API endpoint can be created to intentionally trigger an exception:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">ApiController</span>]
[<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ErrorController</span> : <span class="hljs-title">ControllerBase</span>
{
    [<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"trigger"</span>)</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> IActionResult <span class="hljs-title">TriggerError</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidOperationException(<span class="hljs-string">"This is a test exception from the ErrorController."</span>);
    }
}
</code></pre>
<p>When a request is made to the <code>/api/error/trigger</code> endpoint, the <code>InvalidOperationException</code> will be thrown. Our <code>ExceptionHandlingMiddleware</code> will catch this exception, log it on the server, and return a JSON response to the client with a 500 Internal Server Error status code and a generic error message. The server-side logs will contain the detailed exception information.</p>
<h2 id="heading-the-power-of-middleware-a-final-look">The Power of Middleware: A Final Look</h2>
<p>Understanding and utilizing <a target="_blank" href="http://ASP.NET">ASP.NET</a> Core middleware is essential for building effective web applications. From its role in the request pipeline to the creation of custom components like our exception handler, middleware offers powerful tools for managing request flow, handling errors, and implementing cross-cutting concerns. By embracing middleware, you gain significant control over your application's behavior and architecture.</p>
<p>Happy Coding!!</p>
]]></content:encoded></item><item><title><![CDATA[Step-by-Step Approach to Use Onion Architecture in .NET]]></title><description><![CDATA[Alright, let's start coding! Before we dive into the code, let's quickly recap the core principles of Onion Architecture. If you want a more in-depth look, you can always check out my previous post on https://anilgurau.hashnode.dev/why-choose-onion-a...]]></description><link>https://blog.anilgurau.com/step-by-step-approach-to-use-onion-architecture-in-net</link><guid isPermaLink="true">https://blog.anilgurau.com/step-by-step-approach-to-use-onion-architecture-in-net</guid><category><![CDATA[onion architecture]]></category><category><![CDATA[.NET]]></category><category><![CDATA[C#]]></category><category><![CDATA[software development]]></category><category><![CDATA[software architecture]]></category><dc:creator><![CDATA[Anil gurau]]></dc:creator><pubDate>Sun, 30 Mar 2025 18:39:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743360673893/dcfbff4e-60c3-4208-9e04-e12e48487a7c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Alright, let's start coding! Before we dive into the code, let's quickly recap the core principles of Onion Architecture. If you want a more in-depth look, you can always check out my previous post on <a target="_blank" href="https://anilgurau.hashnode.dev/why-choose-onion-architecture-for-better-software-development-practices">https://anilgurau.hashnode.dev/why-choose-onion-architecture-for-better-software-development-practicese</a>.</p>
<p>But for those who just need a quick refresher, here's the gist:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743036577620/c692b514-09b0-4697-a94f-d5275d7a1f4e.png" alt class="image--center mx-auto" /></p>
<p>As you can see, the key idea is that dependencies point inwards, towards the domain core. But there's another crucial aspect: layers communicate exclusively through abstractions, not concrete implementations. This keeps our application flexible, maintainable, and testable. Each layer has a specific responsibility, and we'll be exploring how to implement them step by step in the following posts.</p>
<p>We're going to focus on the practical application of these principles, so let's get started!</p>
<h2 id="heading-project-setup-creating-the-solution-and-layers">Project Setup: Creating the Solution and Layers</h2>
<ol>
<li><p><strong>Create Solution and Solution Folders:</strong></p>
<ol>
<li><p>In Visual Studio, create a new "Blank Solution" named "<code>OnionArchitectureGuide</code>".</p>
</li>
<li><p>Create Solution Folders named "Core", "Infrastructure", and "Presentation".</p>
</li>
</ol>
</li>
<li><p><strong>Create Domain Layer:</strong></p>
<ol>
<li>Inside the "Core" Solution Folder, add a new "Class Library (.NET Standard/.NET Library)" project named "<code>OnionArchitectureGuide.Domain</code>".</li>
</ol>
</li>
<li><p><strong>Create Application Layer:</strong></p>
<ol>
<li><p>Inside the "Core" Solution Folder, add a new "Class Library (.NET Standard/.NET Library)" project named "<code>OnionArchitectureGuide.Application.Abstraction</code>".</p>
</li>
<li><p>Inside the "Core" Solution Folder, add a new "Class Library (.NET Standard/.NET Library)" project named "<code>OnionArchitectureGuide.Application.Implementation</code>".</p>
</li>
</ol>
</li>
<li><p><strong>Create Infrastructure Layer:</strong></p>
<ol>
<li>Inside the "Infrastructure" Solution Folder, add a new "Class Library (.NET Standard/.NET Library)" project named "<code>OnionArchitectureGuide.Persistence</code>".</li>
</ol>
</li>
<li><p><strong>Create Presentation Layer:</strong></p>
<ol>
<li>Inside the "Presentation" Solution Folder, add a new "ASP.NET Core Web API" project named "<code>OnionArchitectureGuide.Api</code>".</li>
</ol>
</li>
</ol>
<p>    <strong>Note:</strong> Application layer is split into <code>OnionArchitectureGuide.Application.Abstraction</code> and <code>OnionArchitectureGuide.Application.Implementation</code>. The Presentation layer communicates with the Application layer, but it should not have direct access to implementations. Placing service interfaces and their implementations together violates the principles of Onion Architecture and creates tight coupling between layers. The outer layer should interact with the inner layer only through abstractions, not concrete implementations.</p>
<p>Your Folder Structure Look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743043079247/e4962ad9-99a9-42c9-9295-ee3c04ddf58c.png" alt /></p>
<h2 id="heading-setting-up-project-dependencies"><strong>Setting up Project Dependencies:</strong></h2>
<p>Now, we need to set up the project dependencies according to the Onion Architecture principles. Remember, dependencies should point inwards.</p>
<ol>
<li><p><code>OnionArchitectureGuide.Application.Implementation</code> references <code>OnionArchitectureGuide.Domain</code> and <code>OnionArchitectureGuide.Application.Abstraction</code>.</p>
</li>
<li><p><code>OnionArchitectureGuide.Infrastructure</code> references <code>OnionArchitectureGuide.Application.Abstraction</code> and <code>OnionArchitectureGuide.Domain</code>.</p>
</li>
<li><p><code>OnionArchitectureGuide.Api</code> references <code>OnionArchitectureGuide.Application.Abstraction</code></p>
</li>
</ol>
<p>Here is a visual Representation of the Project Dependencies.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743032957611/850a0a70-c1d6-406c-83de-d2b494b30ec8.png" alt="Project dependencies Diagram" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743032908281/91aa2e64-01fb-48e3-8601-04be4febf710.png" alt class="image--center mx-auto" /></p>
<p>This dependency structure is crucial. The API depends on the Application layer, and Infrastructure depends on the Application and Domain Layer, and finally Application layer depends on the Domain layer. This keeps our core logic independent and allows us to easily swap out implementations. This inward dependency flow also ensures that changes in outer layers have minimal impact on the inner layers, enhancing stability. Furthermore, it allows for better testability as the core business logic can be tested in isolation.</p>
<p>We now have the basic project structure set up. In the next post, we'll start implementing Onion Architecture layer by layer.</p>
<h2 id="heading-layer-by-layer-implementation">Layer-by-Layer Implementation</h2>
<p>Alright, we've laid the foundation, now lets construct Domain layer.</p>
<h3 id="heading-constructing-domain-layer">Constructing Domain Layer</h3>
<p>Now, let's define our domain entities and repository interfaces within the <code>OnionArchitectureGuide.Domain</code> project. This layer holds our core business objects and their contracts.</p>
<p>Create Folder named <code>Entities</code> inside <code>OnionArchitectureGuide.Core</code> project and add classes:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Book</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> BookId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Title { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> DateTime PublishedOn { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> AuthorId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> Author Author { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Author</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> AuthorId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> List&lt;Book&gt; Books { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>Create <code>Contracts</code> Folder inside <code>OnionArchitectureGuide.Core</code> project and add Repository Interfaces:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IBookRepository</span>
{
    Task&lt;List&lt;Book&gt;&gt; GetAllAsync();
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IAuthorRepository</span>
{
    Task&lt;List&lt;Author&gt;&gt; GetAllAsync();
}
</code></pre>
<p>Your Domain layer should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743046093544/6960b24f-d4d3-4886-8017-3f100c534f99.png" alt /></p>
<p>The Core layer holds our domain model, free from external dependencies. This ensures core logic is reusable and maintainable.</p>
<p>Next, we'll create abstractions in the <code>OnionArchitectureGuide.Application.Abstraction</code> before implementing these contracts in the <code>OnionArchitectureGuide.Application.Implementation</code>.</p>
<h3 id="heading-building-application-contracts-abstraction"><strong>Building Application Contracts (Abstraction)</strong></h3>
<p>Now that we've defined our domain entities and repository contracts in the Domain layer, let's move on to creating the application contracts in the <code>OnionArchitectureGuide.Application.Abstraction</code> project. This layer will define the DTOs and interfaces that our application services will implement.</p>
<p>Inside the <code>OnionArchitectureGuide.Application.Abstraction</code> project, create a folder named <code>Contracts</code>. This folder will hold our service interface definitions.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IBookService</span>
{
    Task&lt;List&lt;BookDto&gt;&gt; GetAllBooks();
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IAuthorService</span>
{
    Task&lt;List&lt;AuthorDto&gt;&gt; GetAllAuthors();
}
</code></pre>
<p>Create <code>DTOs</code> Folder inside <code>OnionArchitectureGuide.Application.Abstraction</code> project and add Data transfer objects:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookDto</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> BookId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Title { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> DateTime PublishedOn { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AuthorDto</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> AuthorId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>Your <code>OnionArchitectureGuide.Application.Abstraction</code> project should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743053205746/e88eccbb-0ddf-40a5-9c38-95dc585be9c5.png" alt /></p>
<p>By defining these service interfaces and DTOs in a separate <code>OnionArchitectureGuide.Application.Abstraction</code> layer, we decouple our application's core logic from the specific implementations and the data transfer details. This allows us to easily switch out implementations or change data transfer formats without affecting the rest of the application.</p>
<p>Next, we'll implement these service contracts in the <code>OnionArchitectureGuide.Application.Implementation</code>.</p>
<h3 id="heading-implementing-application-services"><strong>Implementing Application Services</strong></h3>
<p>Alright, let's move on to the <code>OnionArchitectureGuide.Application.Implementation</code> project. We'll be focusing on implementing the service contracts and utilizing the DTOs we defined in the <code>OnionArchitectureGuide.Application.Abstraction</code>.</p>
<p>Before Implementing this layer, lets first install required packages from Nuget package manager or package manager console.</p>
<pre><code class="lang-bash">NuGet\Install-Package AutoMapper -Version 14.0.0
</code></pre>
<p>Lets Create Mapping Profiles now. Create Folder named <code>Mappings</code> inside <code>OnionArchitectureGuide.Application.Implementation</code> project and add AutoMapper profiles:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">internal</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationProfile</span>: <span class="hljs-title">Profile</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationProfile</span>(<span class="hljs-params"></span>)</span>
    {
        CreateMap&lt;BookDto, Book&gt;().ReverseMap();
        CreateMap&lt;Author, AuthorDto&gt;().ReverseMap();
    }
}
</code></pre>
<p>Create Folder named <code>Services</code> inside <code>OnionArchitectureGuide.Application.Implementation</code> project and add service implementations:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookService</span> : <span class="hljs-title">IBookService</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IBookRepository _bookRepository;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IMapper _mapper;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">BookService</span>(<span class="hljs-params">IBookRepository bookRepository, IMapper mapper</span>)</span>
    {
        _bookRepository = bookRepository;
        _mapper = mapper;
    }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;List&lt;BookDto&gt;&gt; GetAllBooks()
    {
        <span class="hljs-keyword">return</span> _mapper.Map&lt;List&lt;BookDto&gt;&gt;(<span class="hljs-keyword">await</span> _bookRepository.GetAllAsync());
    }
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">internal</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AuthorService</span> : <span class="hljs-title">IAuthorService</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IAuthorRepository _authorRepository;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IMapper _mapper;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AuthorService</span>(<span class="hljs-params">IAuthorRepository authorRepository, IMapper mapper</span>)</span>
    {
        _authorRepository = authorRepository;
        _mapper = mapper;
    }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;List&lt;AuthorDto&gt;&gt; GetAllAuthors()
    {
        <span class="hljs-keyword">return</span> _mapper.Map&lt;List&lt;AuthorDto&gt;&gt;(<span class="hljs-keyword">await</span> _authorRepository.GetAllAsync());
    }
}
</code></pre>
<p>This layer contains the core business logic of our application. By keeping it separate from the presentation and infrastructure layers, we ensure that our business logic is reusable and testable.</p>
<p>Before implementing Infrastructure layer, lets register all the Dependencies for this layer.</p>
<p>Create a file named <code>ApplicationServiceExtensions.cs</code> in the root level of the <code>OnionArchitectureGuide.Application.Implementation</code> project.</p>
<p>The <code>OnionArchitectureGuide.Application.Implementation</code> layer should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743059521789/ff40c4ad-5359-4e92-a72a-85d95e2a9382.png" alt class="image--center mx-auto" /></p>
<p>Now, register all the dependencies for this project:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationServiceExtensions</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">AddApplicationServices</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceCollection services</span>)</span>
    {
        <span class="hljs-comment">//Registering all the automapper profiles</span>
        services.AddAutoMapper(Assembly.GetExecutingAssembly());

        services.AddScoped&lt;IBookService,BookService&gt;();
        services.AddScoped&lt;IAuthorService,AuthorService&gt;();
    }
}
</code></pre>
<p>Next, we'll integrate the infrastructure layer to handle data access and implement repositories.</p>
<h3 id="heading-integrating-infrastructure">Integrating Infrastructure</h3>
<p>Now that we've implemented our application services in the <code>OnionArchitectureGuide.Application.Implementation</code> project, let's integrate the Infrastructure layer (<code>OnionArchitectureGuide.Persistence</code>). The main purpose of this layer is to handle data access, external API integrations, and other infrastructure-related concerns. Since this is a simple implementation, we will only focus on data access and Repository implementation.</p>
<p>Make sure you have installed <code>Microsoft.EntityFrameworkCore</code> , <code>Microsoft.EntityFrameworkCore.SqlServer</code> and <code>Microsoft.EntityFrameworkCore.Tools</code> packages for SQL Server connection, Database operations and Migrations.</p>
<p>Run these commands in Package Manager Console:</p>
<pre><code class="lang-bash">NuGet\Install-Package Microsoft.EntityFrameworkCore -Version 9.0.3
</code></pre>
<pre><code class="lang-bash">NuGet\Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 9.0.3
</code></pre>
<pre><code class="lang-bash">NuGet\Install-Package Microsoft.EntityFrameworkCore.Tools -Version 9.0.3
</code></pre>
<p>After packages are installed, create <code>Data</code> Folder, which will hold our data access(DbContext).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">internal</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationDbContext</span> : <span class="hljs-title">DbContext</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationDbContext</span>(<span class="hljs-params">DbContextOptions options</span>) : <span class="hljs-title">base</span>(<span class="hljs-params">options</span>)</span>
    {
    }

    <span class="hljs-keyword">public</span> DbSet&lt;Book&gt; Books { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> DbSet&lt;Author&gt; Authors { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>Create Folder named <code>Repositories</code>, which holds the repository implementations that we declared in the domain layer.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">internal</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookRepository</span> : <span class="hljs-title">IBookRepository</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ApplicationDbContext _context;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">BookRepository</span>(<span class="hljs-params">ApplicationDbContext context</span>)</span>
    {
        _context = context;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;List&lt;Book&gt;&gt; GetAllAsync()
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _context.Books.ToListAsync();
    }
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">internal</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AuthorRepository</span> : <span class="hljs-title">IAuthorRepository</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ApplicationDbContext _context;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AuthorRepository</span>(<span class="hljs-params">ApplicationDbContext context</span>)</span>
    {
        _context = context;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;List&lt;Author&gt;&gt; GetAllAsync()
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _context.Authors.ToListAsync();
    }
}
</code></pre>
<p>Now lets register all the dependencies for the Infrastructure layer in the Dependency Injection container.</p>
<p>Create a file named <code>InfrastructureServiceExtensions.cs</code> in the root level of the <code>OnionArchitectureGuide.Persistence</code> project.</p>
<p>Your <code>OnionArchitectureGuide.Presistence</code> should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743098582438/6ec74067-8fbf-4aa3-8835-a50d21da0850.png" alt /></p>
<p>Register Infrastructure Dependencies as:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">InfrastructureServiceExtensions</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">AddInfrastructureServices</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceCollection services, <span class="hljs-keyword">string</span> connectionString</span>)</span>
    {
        <span class="hljs-comment">//Registering Application DbContext and Seeding Values</span>
        services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
        {
            options.UseSqlServer(connectionString);
            options.UseSeeding((context, _) =&gt;
            {
                <span class="hljs-keyword">var</span> book = context.Set&lt;Book&gt;().FirstOrDefault();
                <span class="hljs-keyword">if</span> (book == <span class="hljs-literal">null</span>)
                {
                    context.Set&lt;Book&gt;().AddRange(BookSeeds());
                    context.SaveChanges();
                }
            })
            .UseAsyncSeeding(<span class="hljs-keyword">async</span> (context, _, cancellationToken) =&gt;
                {
                    <span class="hljs-keyword">var</span> book = context.Set&lt;Book&gt;().FirstOrDefaultAsync();
                    <span class="hljs-keyword">if</span> (book == <span class="hljs-literal">null</span>)
                    {
                        context.Set&lt;Book&gt;().AddRange(BookSeeds());
                        <span class="hljs-keyword">await</span> context.SaveChangesAsync();
                    }
                });

        });
        services.AddScoped&lt;IBookRepository, BookRepository&gt;();
        services.AddScoped&lt;IAuthorRepository, AuthorRepository&gt;();
    }
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> List&lt;Book&gt; <span class="hljs-title">BookSeeds</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> List&lt;Book&gt; { 
            <span class="hljs-keyword">new</span> Book { Title = <span class="hljs-string">"Book 1"</span>, Author= <span class="hljs-keyword">new</span> Author(){ Name =<span class="hljs-string">"XXX"</span>} }, 
            <span class="hljs-keyword">new</span> Book { Title = <span class="hljs-string">"Book 2"</span>, Author= <span class="hljs-keyword">new</span> Author(){ Name =<span class="hljs-string">"YYY"</span>} }
            };
    }
}
</code></pre>
<p>Infrastructure layer isolates our application's core logic from specific data access and external dependencies. This allows us to easily switch out data access implementations or integrate with different external systems without affecting the rest of the application.</p>
<p>Next, we'll expose our application through an API in the Presentation layer.</p>
<h3 id="heading-exposing-the-application-api">Exposing the Application (API)</h3>
<p>Now that we've integrated the Infrastructure layer, let's expose our application's functionality through an API in the Presentation layer. This layer will handle incoming requests and outgoing responses. We'll also configure our dependencies and set up the connection string for our data access.</p>
<p>Inside the <code>OnionArchitectureGuide.Api/Controllers</code>, add new API controllers <code>BookController.cs</code> and <code>AuthorController.cs</code> and create endpoints as shown below.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
[<span class="hljs-meta">ApiController</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BookController</span> : <span class="hljs-title">ControllerBase</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IBookService _bookService;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">BookController</span>(<span class="hljs-params">IBookService bookService</span>)</span>
    {
        _bookService = bookService;
    }

    [<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"get-books"</span>)</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IActionResult&gt; <span class="hljs-title">GetBooks</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> Ok(<span class="hljs-keyword">await</span> _bookService.GetAllBooks());
    }
}
</code></pre>
<pre><code class="lang-csharp">[<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
[<span class="hljs-meta">ApiController</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AuthorController</span> : <span class="hljs-title">ControllerBase</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IAuthorService _authorService;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AuthorController</span>(<span class="hljs-params">IAuthorService authorService</span>)</span>
    {
        _authorService = authorService;
    }

    [<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"get-authors"</span>)</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IActionResult&gt; <span class="hljs-title">GetBooks</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> Ok(<span class="hljs-keyword">await</span> _authorService.GetAllAuthors());
    }
}
</code></pre>
<p>We're almost there, but first, we must register all necessary dependencies from <code>OnionArchitectureGuide.Application.Implementation</code> and <code>OnionArchitectureGuide.Infrastructure</code>.</p>
<p>Currently, our <code>OnionArchitectureGuide.Api</code> project only references <code>OnionArchitectureGuide.Application.Abstraction</code>. While we <em>could</em> add references to <code>OnionArchitectureGuide.Application.Implementation</code> and <code>OnionArchitectureGuide.Infrastructure</code> directly, I prefer to handle dependency registration in a separate class library dedicated to managing these dependencies. This keeps our API project cleaner.</p>
<p>The <code>OnionArchitectureGuide.Api</code> project should not directly communicate with <code>OnionArchitectureGuide.Application.Implementation</code> or <code>OnionArchitectureGuide.Infrastructure</code> via concrete implementations. We should avoid using <code>ApplicationDbContext</code>, repositories, or service implementations directly in the API project. This is why all implementations have <code>internal</code> access modifiers.</p>
<p>To achieve this, let's create a new "Class Library (.NET Standard/.NET Library)" project named OnionArchitectureGuide.DependencyManager within the 'Presentation' solution folder. Then, add project references to OnionArchitectureGuide.Application.Implementation and OnionArchitectureGuide.Infrastructure to this new project.</p>
<p>The Dependency Flow should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743104670592/dac9c1fa-0d5f-4e10-9105-38932aea1be4.png" alt class="image--center mx-auto" /></p>
<p>Here is a screenshot of the <code>OnionArchitectureGuide.Api</code> and Dependency Manager.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743104844512/6eb78d7b-964f-44c1-beee-9dfd5b2ecdbc.png" alt /></p>
<p>Lets manage dependencies:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DependencyManager</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">RegisterApplicationDependencies</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceCollection services</span>)</span>
    {
        services.AddApplicationServices();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">RegisterInfrastructureDependencies</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceCollection services, <span class="hljs-keyword">string</span> connectionString</span>)</span>
    {
        services.AddInfrastructureServices(connectionString);
    }
}
</code></pre>
<p>Register all Dependencies in <code>Program.cs</code> of <code>OnionArchitectureGuide.Api</code> project.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">//Register Application and Infrastructure services using Dependency manager</span>
<span class="hljs-comment">// you can also register directly from OnionArchitectureGuide.Application.Implementation and OnionArchitectureGuide.Presistence</span>
<span class="hljs-comment">//E.g. builder.Services.AddApplicationServices(); and builder.Services.AddInfrastructureServices(builder.Configuration.GetConnectionString("DefaultConnection") ?? String.Empty);</span>

builder.Services.RegisterApplicationDependencies();
builder.Services.RegisterInfrastructureDependencies(builder.Configuration.GetConnectionString(<span class="hljs-string">"DefaultConnection"</span>)?? String.Empty);
</code></pre>
<p>Add ConnectionString to <code>appsettings.json</code></p>
<pre><code class="lang-json"><span class="hljs-string">"ConnectionStrings"</span>: {
  <span class="hljs-attr">"DefaultConnection"</span>:  <span class="hljs-string">"Your connection string"</span>
}
</code></pre>
<p>One last step before we run our application.</p>
<p><strong>Running Migration:</strong></p>
<p>Install <code>Microsoft.EntityFrameworkCore.Design</code> package in <code>OnionArchitectureGuide.Api</code> project, for the migration to work.</p>
<pre><code class="lang-bash">NuGet\Install-Package Microsoft.EntityFrameworkCore.Design -Version 9.0.3
</code></pre>
<ul>
<li><p>Open Package Manager Console, set Default project to <code>OnionArchitectureGuide.</code>Persistence.</p>
</li>
<li><p>Set a Project Startup to <code>OnionArchitectureGuide.Api</code>.</p>
</li>
<li><p>Run: <code>Add-Migration InitialMigration</code> then <code>Update-Database</code></p>
</li>
</ul>
<pre><code class="lang-bash">add-migration initialMigration
</code></pre>
<pre><code class="lang-bash">update-database
</code></pre>
<p>We're now ready to run our application. Ensure all dependencies are correctly registered and the connection string is properly configured before proceeding.</p>
<ul>
<li><p>Run the <code>OnionArchitectureGuide.Api</code> project.</p>
</li>
<li><p>Use Postman or Swagger to send requests to the API endpoints.</p>
</li>
</ul>
<p>Example Output:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743112220719/3df86dcd-78e5-41a5-81ac-8b3b0f892720.png" alt class="image--center mx-auto" /></p>
<p>Congratulations! You've successfully built a simple application using Onion Architecture. We've navigated through each layer, implementing the core logic and exposing it through an API.</p>
<p>Now that you've grasped the fundamentals, it's time to put this knowledge into practice. Start by exploring how you can apply these principles to your own projects. Consider:</p>
<ul>
<li><p><strong>Refactoring existing code:</strong> Identify areas where you can apply Onion Architecture to improve maintainability.</p>
</li>
<li><p><strong>Expanding your application:</strong> Add new features and functionalities, ensuring they fit within the established architecture.</p>
</li>
<li><p><strong>Experimenting with different technologies:</strong> Explore how Onion Architecture can be adapted to various databases, frameworks, and external systems.</p>
</li>
</ul>
<p>Keep exploring, keep coding, and keep building robust applications. Thank you for joining me on this practical journey. Happy Coding!</p>
]]></content:encoded></item><item><title><![CDATA[Why Choose Onion Architecture for Better Software Development Practices]]></title><description><![CDATA[Ever felt trapped by your application's architecture?  Last year, a simple database change turned into a massive team effort, forcing us to touch almost every part of our old N-layered setup. That's when we realized we needed a better approach: Onion...]]></description><link>https://blog.anilgurau.com/why-choose-onion-architecture-for-better-software-development-practices</link><guid isPermaLink="true">https://blog.anilgurau.com/why-choose-onion-architecture-for-better-software-development-practices</guid><category><![CDATA[onion architecture]]></category><category><![CDATA[.NET]]></category><category><![CDATA[software architecture]]></category><dc:creator><![CDATA[Anil gurau]]></dc:creator><pubDate>Wed, 26 Mar 2025 21:29:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743023848497/39f4a86b-9315-4fa1-8de0-be031da3ad60.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ever felt trapped by your application's architecture?  Last year, a simple database change turned into a massive team effort, forcing us to touch almost every part of our old N-layered setup. That's when we realized we needed a better approach: <strong>Onion Architecture.</strong></p>
<p>Before we get into the details of Onion Architecture, let's talk about why we switched. It wasn't just about fixing that database problem; we wanted a system that was easier to maintain and change in the future.</p>
<h2 id="heading-why-we-chose-onion-architecture">Why We Chose Onion Architecture?</h2>
<p>So, why did we pick Onion Architecture? Well, after that database change mess, we took a hard look at our old setup. We realized it had some serious issues that were slowing us down:</p>
<ul>
<li><p><strong>Everything Was Mixed Together:</strong> Changing one thing broke other things.</p>
</li>
<li><p><strong>Business Rules were Hard to Find:</strong> Our main logic was spread all over.</p>
</li>
<li><p><strong>Hard to add new features:</strong> Adding new features took way longer than it should have.</p>
</li>
<li><p><strong>Hard to understand:</strong> New team members had a really hard time understanding the code.</p>
</li>
</ul>
<p>We needed something to fix this. Onion Architecture looked good because:</p>
<ul>
<li><p><strong>Things don't depend on each other:</strong> Our main code wouldn't depend on databases or the user interface.</p>
</li>
<li><p><strong>Business rules are the most important:</strong> Our main logic is at the center and untouched.</p>
</li>
<li><p><strong>Easy to swap out technology:</strong> If we wanted to change database or UI technology, it would be much easier.</p>
</li>
<li><p><strong>Easy to integrate third-party services:</strong> It would be much easier to integrate third-party services like email services, payment integration, etc.</p>
</li>
<li><p><strong>Code is easier to understand:</strong> The code becomes more organized and easier to follow.</p>
</li>
</ul>
<p>So, we knew Onion Architecture was the answer for us. But what does it actually look like inside?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743014853961/27887264-f4d6-431b-8998-a98cd6b65d24.png" alt class="image--center mx-auto" /></p>
<p>As you can see, it's like an onion, with different rings inside. Each ring has its own job. The important thing is how these rings talk to each other, or more importantly, how they don't talk to each other. Let's break down each layer and see what it does.</p>
<h2 id="heading-layers-of-onion-architecture">Layers of Onion Architecture</h2>
<p><strong>1. Domain Layer</strong></p>
<p>It's the innermost layer, containing your core business logic and entities. This layer is incredibly important because it's almost entirely untouched by changes happening around it. It's pure business logic, meaning it doesn't care about databases, user interfaces, or any other external factors. It's just the rules and data that make your business work.</p>
<p><strong>The Domain Layer typically contains:</strong></p>
<ul>
<li><p><strong>Entities:</strong> These are the core business objects, like 'Customer,' 'Order,' or 'Product.'</p>
</li>
<li><p><strong>Aggregates:</strong> Collections of related entities treated as a single unit.</p>
</li>
<li><p><strong>Repository Interfaces:</strong> Definitions of how to access and persist entities, without specifying the actual implementation.</p>
</li>
<li><p><strong>Domain Events:</strong> Notifications of significant changes within the domain.</p>
</li>
<li><p><strong>Value Objects:</strong> Immutable objects that represent domain concepts.</p>
</li>
</ul>
<p><strong>2. Application Layer</strong></p>
<p>The Application Layer coordinates how your app uses its core logic. It sits between the Domain Layer and the UI/external layers, connecting them without exposing the Domain directly.</p>
<p><strong>The Application Layer typically contains:</strong></p>
<ul>
<li><p><strong>Application Services:</strong> These services define the application's use cases. They receive requests directly from the UI or other outer layers and instruct the Domain Services on what to do.</p>
</li>
<li><p><strong>Data Transfer Objects (DTOs):</strong> These objects handle the transfer of data between the Application Layer and the outer layers, keeping the Domain Layer isolated from external data structures.</p>
</li>
<li><p><strong>Interfaces:</strong> This layer defines interfaces that the Infrastructure Layer will implement. This ensures that the Application Layer remains independent of specific infrastructure implementations.</p>
</li>
</ul>
<p><strong>3. Infrastructure Layer</strong></p>
<p>The Infrastructure Layer connects your app to the outside world. It handles databases and other external services. It's the 'where' your app interacts. This layer changes most but doesn't affect the core logic.</p>
<p><strong>The Infrastructure Layer typically contains:</strong></p>
<ul>
<li><p><strong>Database Implementations:</strong> This is where the actual code for interacting with databases resides. It implements the repository interfaces defined in the Domain Layer.</p>
</li>
<li><p><strong>API Integrations:</strong> This is where your application connects to external APIs, handling communication and data translation.</p>
</li>
<li><p><strong>External Service Integrations:</strong> This is where your application connects to external services like email, payment gateways, etc.</p>
</li>
<li><p><strong>Specific Technology Implementations:</strong> This is where any specific technological implementations live. For example, file system access, network communication etc.</p>
</li>
</ul>
<p><strong>4. Presentation Layer:</strong> It's the layer users interact with directly, whether it's a web page, mobile app, or desktop application.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743015010739/aab54497-926c-4496-a905-e8a4e044da56.png" alt class="image--center mx-auto" /></p>
<p>So, that's how the Onion Architecture works! We have different layers, and we know how they talk to each other. Remember, the middle part (Domain Layer) should stay clean.</p>
<h2 id="heading-onion-vs-n-layered-architecture">Onion vs N-Layered Architecture</h2>
<p>We have seen how Onion architecture works, now let’s see how it is better than N-Layered Architecture.</p>
<ol>
<li><strong>Dependency Direction:</strong></li>
</ol>
<ul>
<li><p><strong>N-Layered:</strong> Each layer depends on the layer below it. This creates a fragile structure where changes in lower layers can ripple through the entire application.</p>
</li>
<li><p><strong>Onion:</strong> Dependencies point inwards, towards the core business logic. This isolates the core, making changes in outer layers less risky.</p>
</li>
</ul>
<ol start="2">
<li><strong>Testability:</strong></li>
</ol>
<ul>
<li><p><strong>N-Layered:</strong> Testing becomes complex due to tight coupling. Changes in one layer often require multiple layers testing.</p>
</li>
<li><p><strong>Onion:</strong> The isolated core allows for easier unit testing of business logic, leading to more reliable code.</p>
</li>
</ul>
<ol start="3">
<li><strong>Maintainability:</strong></li>
</ol>
<ul>
<li><p><strong>N-Layered:</strong> Code becomes harder to maintain as dependencies grow. Changes require careful coordination across multiple layers.</p>
</li>
<li><p><strong>Onion:</strong> The clear separation of concerns simplifies maintenance. Changes in infrastructure or UI don't impact the core logic.</p>
</li>
</ul>
<ol start="4">
<li><strong>Separation of Concerns:</strong></li>
</ol>
<ul>
<li><p><strong>N-Layered:</strong> Business logic can bleed into other layers, making the code harder to understand and evolve.</p>
</li>
<li><p><strong>Onion:</strong> Enforces a clean separation, with each layer having a distinct responsibility.</p>
</li>
</ul>
<p><strong>In essence:</strong> Onion Architecture prioritizes a stable core, making your application more adaptable to change. It's about building an architecture that evolves**.**</p>
<h2 id="heading-real-world-scenarios-when-onion-architecture-fits-and-when-it-doesnt"><strong>Real-World Scenarios: When Onion Architecture Fits (and When It Doesn't)</strong></h2>
<p>Onion Architecture shines in projects with complex business rules, evolving needs, microservices, TDD, long-term maintenance, and when large teams work on separate modules.</p>
<p>It's less ideal for simple CRUD apps, small projects, performance-critical systems (sometimes), or when your team lacks familiarity.</p>
<p>Choose Onion when you need stability, flexibility, and clear module separation for complex projects, especially with larger teams. OPT for simpler architectures for basic or short-term needs.</p>
<h2 id="heading-from-scenarios-to-practice-seeing-onion-in-action"><strong>From Scenarios to Practice: Seeing Onion in Action</strong></h2>
<p>Now that we've seen when Onion Architecture fits, let's look at a practical code example. To provide you with a real-world context, I've created a simple project to demonstrates the key principles we've discussed, which is available on GitHub:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/gurau-anil/LibManEase">https://github.com/gurau-anil/LibManEase</a></div>
<p> </p>
<h2 id="heading-conclusion-building-maintainable-and-scalable-applications"><strong>Conclusion: Building Maintainable and Scalable Applications</strong></h2>
<p>Onion Architecture offers a powerful way to build maintainable and scalable applications. While it might seem complex at first, its benefits become clear as your application grows.</p>
<p>By understanding and applying Onion Architecture, you can build applications that are easier to test, maintain, and evolve. Happy coding!</p>
]]></content:encoded></item></channel></rss>