DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Single Responsibility Principle: The Most Important Rule in the Software World
  • Fixing Your Microservices Architecture Using Graph Analysis
  • The Anatomy of a Microservice, One Service, Multiple Servers
  • Monolithic First

Trending

  • Building Reliable LLM-Powered Microservices With Kubernetes on AWS
  • Operational Principles, Architecture, Benefits, and Limitations of Artificial Intelligence Large Language Models
  • Next Evolution in Integration: Architecting With Intent Using Model Context Protocol
  • How to Ensure Cross-Time Zone Data Integrity and Consistency in Global Data Pipelines
  1. DZone
  2. Data Engineering
  3. Databases
  4. A Developer's Guide to CQRS Using .NET Core and MediatR

A Developer's Guide to CQRS Using .NET Core and MediatR

In this article, we discuss how to implement the CQRS pattern in a .NET Core application with MediatR for cleaner application architecture.

By 
Faris Karcic user avatar
Faris Karcic
·
Jun. 22, 20 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
31.0K Views

Join the DZone community and get the full member experience.

Join For Free

“What is CQRS?” you might ask. I hope you didn’t think you were going to get a definition because that’s what Wikipedia is for. Instead, what I’m hoping to achieve with this blog post is to help you understand CQRS through some practical examples.

I will assume you’re either a beginner or someone who is unfamiliar with this topic, so maybe you get scared whenever you run into these big programming acronyms and concepts. I myself have been there, so I’m here to help you figure it out in the easiest way possible.

Before we get talking about CQRS, we need to understand a few concepts, such as Clean Architecture, for a better understanding. If you are already familiar with Clean Architecture, then feel free to skip onto the next section. If you are one of those people reading this blog who dislikes theory and just want to get your hands on the code, I encourage you to be patient and try and grasp these concepts and patterns as they will prove to be helpful in the end.

In this blog, I’ll lead you through a step-by-step tutorial so you learn both about CQRS and also end up with a beautiful project structure that you can brag to your friends about. You may also learn an additional trick or two. At the end of the blog, I’ve provided a link to the entire solution.

Clean Architecture

Before tackling the concept of CQRS let’s learn a bit about Clean Architecture.

Why?

Because combining this duo gives us a pretty nice base for further development. Clean Architecture is all about layers and boundaries and creating a clean project structure just as the name itself implies.

Clean architecture diagram

We can see how these layers form one solution. It is important to know that the outer layers depend upon the inner layers and not vice versa.

Domain

In a perfect world, this layer wouldn’t have any dependencies, and it would only contain entities, value objects, and maybe some Domain level custom exceptions and entity logic. This layer can be shaped by following the Domain-Driven Design guidelines. I would recommend that you explore these guidelines in-depth. Since it’s a broad subject I’ll leave it up to you.

Application

Together with the Domain layer, the Application layer forms the Core of the solution that should be able to operate and provide business logic independently from outer layers and depend solely upon the Domain layer. It contains all of the good stuff, such as the business logic (use cases), DTO’s, interfaces, and all of the CQRS stuff that we will be discussing later.

Infrastructure

This is the layer where all of the communication logic with the outside systems should be implemented such as sending emails, communication with 3rd party API, etc. It only depends on the Application layer. It can also contain persistence logic if it’s not overly massive and/or complex.

Persistence

Compared to the Infrastructure layer, this layer also holds the logic for communication with outside systems, but its specific purpose is to communicate with databases. All of this logic can also be placed under the Infrastructure layer. This layer only depends on the Application layer.

Presentation

This is the interactable layer (by the outside world) which allows clients to get visible results after requesting data. This layer can be in the form of an API, console application, GUI client application, etc. Like Persistence, it also depends only on the Application layer.

Now, since you have this quick overview of the architecture, we can move forward to explore what CQRS is all about.

Let's Dive in

Have you ever experienced having to tweak some part of the logic or models and upon finishing that task you realize that you blew up half of the app? Or have you ever had to fix some bug (created by some other developer of course) and then you go strolling through the codebase searching for some specific part of logic but it’s hard to find because it’s all spaghetti code? Or maybe the number of users on your application has drastically increased, your current machine can’t handle it anymore, the “scale up” button is greyed out because it was so long ago that you already reached a top-level machine, you think of balancing load with microservices, but you do a facepalm because you know how much effort and time it will take to refactor all of that spaghetti?

That’s what CQRS strives to solve!

Don't blame the code

CQRS stands for Command Query Responsibility Segregation and my initial thought when I was learning this for the first time was: “Well this name doesn’t help very much in understanding this” even though it does when you start understanding the concept behind the name. So the name basically is all there is: Let’s separate responsibilities of commands & queries.

Then the next question arises, “What are commands and queries?”

Well, it’s rather simple and I will use CRUD operations as an example.

CREATE, UPDATE, and DELETE are methods used to tell the system to insert, change, or remove something. As you’ve probably already figured out, you are giving out commands. While with the READ method you just want to get some data, and yes that’s a query, just like when you query the database.

Now that we have some basic idea of what CQRS should do, we come to the following question: but how can we use all of this in practice? Which then brings us to a more specific question - How do I separate these responsibilities?

That’s the next thing we are about to tackle.

CQRS in Practice

Let’s take a look at how CQRS looks in practice.

For now, let’s say we have an Application layer with the business logic separated into use cases, or rather services. Perhaps you would have a service for forum posts that would contain all of the logic regarding forum posts and may be dependent upon other services. In addition, this service could possibly be reused somewhere else.

It would look something like this:

CQRSForum

There may be issues with this approach down the line when you need to adjust a method in some service to adhere for a second service which could break logic in some other third service where the first service is used. You’ll end up with a headache since you need to adhere to multiple cases and then figure out the way to adjust logic for all these edge cases.

Or maybe you want to separate the application into microservices until you realize how hard it will be because of the intertwined logic?

The CQRS pattern solves these problems and has many pros. Of course, nothing is perfect, so the CQRS pattern also has its cons such as not being totally DRY (Don’t Repeat Yourself) and managing it would take a bit more time for some global changes.

Now let’s see how and why.

The CQRS structure would look something like this:

CQRS structure

As you can see, every one of these classes has only one responsibility and they aren’t reused. Every single one of them has its own models, even though they might be alike or exact copies of other models. However, programming and project architecture are subjective things so you can combine approaches by having some reusable common things. All of this separation makes it easy for us to find issues and not ruin the rest of the codebase by fiddling with something. As well, it makes it easy to extract microservices from code eventually.

Additional Nuggets Used

Check out some additional nuggets I used:

MediatR 

Before getting hooked on MediatR, I was really missing out. MediatR is such a handy addition to CQRS and Clean Architecture, which makes a developer’s life much easier with how everything is handled independently from each other which you will see later on in this blog.

I highly recommend checking it out since we are going to use it for our little project.

AutoMapper

AutoMapper is a tool that makes it easy to do mapping between two objects.

Swashbuckle Swagger

No backend application is whole without Swagger. It’s documentation GUI for endpoints and all of its details which are necessary for it to be consumed.

Enough Theory Already!

Now, before we get into the code, it’s important to keep in mind that even though we could create a Domain layer and some entities, we won’t be using them in this example project.

As an alternative to creating everything from scratch, you may want to check out this repo before you start reading so you can follow along with all the examples. It’s based on .NET Core 3.1.

The first step would be to prepare the project structure by layers, as defined per Clean Architecture and then add the additional Nuggets and make sure everything is running properly.

For the theme of the project, we are going to make a CQRS backend for a forum-like app, but it’s not important because the business logic is not what matters. As well, I used the JSONPlaceholder for obtaining data as a replacement for the database. In addition, the way I decided to go is to create it as an API backend solution.

Presentation

Necessary Nuggets:

  • Swashbuckle.AspNetCore

In our case, the Presentation layer is the entry point of the application and the two main entry files are Program.cs and Startup.cs. For what we are trying to learn here, we will just need to modify Startup since we define our services there, dependency injection, and request pipeline. Also, in this layer, we have controllers with the primary function to obtain some inputs and trigger our MediatR request pipelines.

Startup.cs

C#
 




xxxxxxxxxx
1
60


 
1
using Application;
2
using Infrastructure;
3
using Microsoft.AspNetCore.Builder;
4
using Microsoft.AspNetCore.Hosting;
5
using Microsoft.Extensions.Configuration;
6
using Microsoft.Extensions.DependencyInjection;
7
using Microsoft.Extensions.Hosting;
8
using Microsoft.OpenApi.Models;
9
10
namespace Presentation
11
{
12
   public class Startup
13
   {
14
       public Startup(IConfiguration configuration)
15
       {
16
           Configuration = configuration;
17
       }
18
19
       private IConfiguration Configuration { get; }
20
21
       public void ConfigureServices(IServiceCollection services)
22
       {
23
           services.AddApplication();
24
           services.AddInfrastructure(Configuration);
25
           services.AddControllers();
26
27
           services.AddSwaggerGen(config =>
28
           {
29
               config.SwaggerDoc("v1", new OpenApiInfo() {Title = "CQRS Forum", Version = "v1"});
30
           });
31
32
           // Make routes globally lowercase.
33
           services.AddRouting(options =>
34
           {
35
               options.LowercaseUrls = true;
36
               options.LowercaseQueryStrings = true;
37
           });
38
       }
39
40
       public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
41
       {
42
           if (env.IsDevelopment())
43
           {
44
               app.UseDeveloperExceptionPage();
45
           }
46
47
           app.UseHttpsRedirection();
48
49
           app.UseRouting();
50
51
           app.UseAuthorization();
52
53
           app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
54
          
55
           app.UseSwagger();
56
57
           app.UseSwaggerUI(config => config.SwaggerEndpoint("/swagger/v1/swagger.json", "CQRS Forum v1"));
58
       }
59
   }
60
}



In Startup, an important part is adding those dependency injection container methods from the Application and Infrastructure layer named AddApplication() and AddInfrastructure(). I also added the basic Swagger configurations.

Controllers/BaseController.cs

C#
 




xxxxxxxxxx
1
15


 
1
using MediatR;
2
using Microsoft.AspNetCore.Mvc;
3
using Microsoft.Extensions.DependencyInjection;
4

          
5
namespace Presentation.Controllers
6
{
7
   [ApiController]
8
   [Route("api/[controller]")]
9
   public class BaseController : ControllerBase
10
   {
11
       private IMediator _mediator;
12

          
13
       protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService<IMediator>();
14
   }
15
}



The base controller has been created using new C# 8 features so that we follow the DRY principle and keep our controllers as clean as possible.

Controllers/PostsController.cs

C#
 




xxxxxxxxxx
1
27


 
1
using System.Collections.Generic;
2
using System.Threading.Tasks;
3
using Application.Posts.Commands.CreatePost;
4
using Application.Posts.Queries.GetAllPosts;
5
using Microsoft.AspNetCore.Mvc;
6

          
7
namespace Presentation.Controllers
8
{
9
   public class PostsController : BaseController
10
   {
11
       [HttpGet]
12
       public async Task<ActionResult<IEnumerable<GetAllPostsDto>>> GetAllPosts()
13
       {
14
           var response = await Mediator.Send(new GetAllPostsQuery());
15

          
16
           return Ok(response);
17
       }
18
      
19
       [HttpPost]
20
       public async Task<ActionResult<CreatePostDto>> CreatePost(CreatePostCommand command)
21
       {
22
           var response = await Mediator.Send(command);
23

          
24
           return CreatedAtAction(nameof(CreatePost), response);
25
       }
26
   }
27
}



Since we created the base controller with all of its perks, we can use it to keep the rest of our controllers simple. As you can see, the traditional way would be to inject some PostsService here and to call its methods. But you can see the difference is that we only send commands or queries as objects to MediatR and it will take care of handling the rest through its pipelines.

Application

Necessary Nuggets:

  • AutoMapper
  • AutoMapper.Extensions.Microsoft.DependencyInjection
  • MediatR
  • MediatR.Extensions.DependencyInjection

Just like on the Infrastructure, the root of the layer contains a DI container and the rest of the folders.This is the place where the CQRS magic happens.

First, let’s talk about the Common folder. As the name itself says, it is supposed to contain some commonly used stuff. In this particular case, I used it to add helpers for AutoMapper which will help to keep our code clean and located at proper places (for rebinding models) so it would be easier to maintain later down the line.

Common/Mappings/IMapFrom.cs

C#
 




xxxxxxxxxx
1


 
1
using AutoMapper;
2

          
3
namespace Application.Common.Mappings
4
{
5
   public interface IMapFrom<T>
6
   {
7
       void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
8
   }
9
}



By inheriting IMapFrom on model we get access to this Mapping function which we can then use to set our transformations.

Common/Mappings/MappingProfile.cs

C#
 




xxxxxxxxxx
1
30


 
1
using System;
2
using System.Linq;
3
using System.Reflection;
4
using AutoMapper;
5

          
6
namespace Application.Common.Mappings
7
{
8
   public class MappingProfile : Profile
9
   {
10
       public MappingProfile()
11
       {
12
           ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
13
       }
14

          
15
       private void ApplyMappingsFromAssembly(Assembly assembly)
16
       {
17
           var types = assembly.GetExportedTypes()
18
               .Where(t => t.GetInterfaces().Any(i =>
19
                   i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
20
               .ToList();
21

          
22
           foreach (var type in types)
23
           {
24
               var instance = Activator.CreateInstance(type);
25
               var methodInfo = type.GetMethod("Mapping");
26
               methodInfo?.Invoke(instance, new object[] { this });
27
           }
28
       }
29
   }
30
}



Now for the main part, the Posts folder. The root contains the interface for PostsApi with the Commands and Queries folders.

In the next part, we will focus on querying posts.

Posts/IPostsApi.cs

C#
 




xxxxxxxxxx
1
13


 
1
using System.Collections.Generic;
2
using System.Threading.Tasks;
3
using Application.Posts.Commands.CreatePost;
4
using Application.Posts.Queries.GetAllPosts;
5

          
6
namespace Application.Posts
7
{
8
   public interface IPostsApi
9
   {
10
       Task<IEnumerable<GetAllPostsResponse>> GetAllPosts();
11
       Task<CreatePostResponse> CreatePost(CreatePostRequest request);
12
   }
13
}



Posts/Queries/GetAllPosts/GetAllPostsQuery.cs

C#
 




xxxxxxxxxx
1
29


 
1
using System.Collections.Generic;
2
using System.Threading;
3
using System.Threading.Tasks;
4
using AutoMapper;
5
using MediatR;
6

          
7
namespace Application.Posts.Queries.GetAllPosts
8
{
9
   public class GetAllPostsQuery : IRequest<IEnumerable<GetAllPostsVm>>
10
   {
11
       public class GetAllPostsQueryHandler : IRequestHandler<GetAllPostsQuery, IEnumerable<GetAllPostsVm>>
12
       {
13
           private readonly IPostsApi _postsApi;
14
           private readonly IMapper _mapper;
15

          
16
           public GetAllPostsQueryHandler(IPostsApi postsApi, IMapper mapper)
17
           {
18
               _postsApi = postsApi;
19
               _mapper = mapper;
20
           }
21
          
22
           public async Task<IEnumerable<GetAllPostsVm>> Handle(GetAllPostsQuery request, CancellationToken cancellationToken)
23
           {
24
               var posts = await _postsApi.GetAllPosts();
25
               return _mapper.Map<IEnumerable<GetAllPostsVm>>(posts);
26
           }
27
       }
28
   }
29
}



So as you can see above, there are three main parts when using MediatR. We have a query class, query handler class, and handler method. If we were doing a CREATE method, our query class which in that case would be called command would hold all necessary input properties.

It’s defined as a MediatR class by inheriting the IRequest interface and since it’s of generic nature we have to add a response type to it. If your method is void, then don’t add any return type, but the return type of your handle method would have to be of Task<Unit> type.

Handler class is defined by inheriting IRequestHandler<queryClass returnType> and if you return void just omit returnType.

And finally, we have a handler method which holds the business logic and if it’s supposed to return void it’s signature return type should be defined as Task<Unit> as said previously and it should be returning Unit.Value.

Posts/Queries/GetAllPosts/GetAllPostsResponse.cs

C#
 




xxxxxxxxxx
1
16


 
1
using System.Text.Json.Serialization;
2

          
3
namespace Application.Posts.Queries.GetAllPosts
4
{
5
   public class GetAllPostsResponse
6
   {
7
       [JsonPropertyName("id")]
8
       public int Id { get; set; }
9
       [JsonPropertyName("title")]
10
       public string Title { get; set; }
11
       [JsonPropertyName("body")]
12
       public string Body { get; set; }
13
       [JsonPropertyName("userId")]
14
       public int UserId { get; set; }
15
   }
16
}



Here we have defined a class that will be serialized as a response from a 3rd party API. We also added data annotations to serve as a guide for System.Text.Json on how it should bind properties.

Posts/Queries/GetAllPosts/GetAllPostsDto.cs

C#
 




xxxxxxxxxx
1
18


 
1
using Application.Common.Mappings;
2
using AutoMapper;
3

          
4
namespace Application.Posts.Queries.GetAllPosts
5
{
6
   public class GetAllPostsDto : IMapFrom<GetAllPostsResponse>
7
   {
8
       public int UserId { get; set; }
9
       public int Id { get; set; }
10
       public string Title { get; set; }
11
       public string Body { get; set; }
12

          
13
       public void Mapping(Profile profile)
14
       {
15
           profile.CreateMap<GetAllPostsResponse, GetAllPostsDto>();
16
       }
17
   }
18
}



And finally, we see a DTO model that will serve as a view model and in it, we can see the example of using auto mapping from response class.

Infrastructure

Necessary Nuggets:

  • Microsoft.Extensions.Configuration
  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.DependencyInjection.Abstractions
  • Microsoft.Extensions.Http

I have created a typed base HttpClient which is inherited by a specific client made for JSONPlaceholder API and did an abstraction of forum posts logic as PostsApi.cs. All of it is added through a DI container which is located on the root of the layer. I tried to keep it as simple as possible while using some best practices. You may wonder, "Why are there so many abstractions for the HTTP client?" The answer is simple - I’m a .NET developer and I love my abstractions. It also makes code cleaner and more understanding about what it's doing.

BaseHttpClient.cs

C#
 




xxxxxxxxxx
1
65


 
1
using System;
2
using System.Net.Http;
3
using System.Text;
4
using System.Text.Json;
5
using System.Threading.Tasks;
6

          
7
namespace Infrastructure
8
{
9
   public class BaseHttpClient
10
   {
11
       private readonly HttpClient _httpClient;
12

          
13
       protected BaseHttpClient(HttpClient httpClient)
14
       {
15
           _httpClient = httpClient;
16
       }
17

          
18
       protected async Task<T> Get<T>(string uri)
19
       {
20
           var request = CreateRequest(HttpMethod.Get, uri);
21

          
22
           return await ExecuteRequest<T>(request);
23
       }
24

          
25
       protected async Task<T> Post<T>(string uri, object content)
26
       {
27
           var request = CreateRequest(HttpMethod.Post, uri, content);
28

          
29
           return await ExecuteRequest<T>(request);
30
       }
31
      
32
       private static HttpRequestMessage CreateRequest(HttpMethod httpMethod, string uri, object content = null)
33
       {
34
           var request = new HttpRequestMessage(httpMethod, uri);
35
           if (content == null) return request;
36

          
37
           // Serialize content
38
           var json = JsonSerializer.Serialize(content);
39
           request.Content = new StringContent(json, Encoding.UTF8, "application/json");
40

          
41
           return request;
42
       }
43
      
44
       private async Task<T> ExecuteRequest<T>(HttpRequestMessage request)
45
       {
46
           try
47
           {
48
               var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
49
                   .ConfigureAwait(false);
50
               var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
51

          
52
               response.EnsureSuccessStatusCode();
53

          
54
               return string.IsNullOrEmpty(responseContent) ? default : JsonSerializer.Deserialize<T>(responseContent);
55
           }
56
           catch (Exception ex) when (ex is ArgumentNullException ||
57
                                      ex is InvalidOperationException ||
58
                                      ex is HttpRequestException ||
59
                                      ex is JsonException)
60
           {
61
               throw new Exception("HttpClient exception", ex);
62
           }
63
       }
64
   }
65
}



Now that we have implemented a generic base HttpClient which can be further extended with more methods, I will leave it up to you to make the decision of whether you’d like to further extend it or not. The reason why I have decided to do it this way is because of making it simpler to use, making it reusable, and keeping it DRY.

JsonPlaceholderApi/JsonPlaceholderClient.cs

C#
 




xxxxxxxxxx
1
26


 
1
using System.Collections.Generic;
2
using System.Net.Http;
3
using System.Threading.Tasks;
4
using Application.Posts.Commands.CreatePost;
5
using Application.Posts.Queries.GetAllPosts;
6

          
7
namespace Infrastructure.JsonPlaceholderApi
8
{
9
   public class JsonPlaceholderClient : BaseHttpClient
10
   {
11
       public JsonPlaceholderClient(HttpClient httpClient) : base(httpClient)
12
       {
13
          
14
       }
15

          
16
       public async Task<IEnumerable<GetAllPostsResponse>> GetAllPosts()
17
       {
18
           return await Get<IEnumerable<GetAllPostsResponse>>(Endpoints.Posts.GetAllPosts);
19
       }
20

          
21
       public async Task<CreatePostResponse> CreatePost(CreatePostRequest request)
22
       {
23
           return await Post<CreatePostResponse>(Endpoints.Posts.CreatePost, request);
24
       }
25
   }
26
}



After the implementation of base HttpClient, we can use it for specific tasks like in our case communicating with JsonPlaceholder API for obtaining a list of posts. Again, since we created the base HttpClient the way we did we can create more of these specific type clients for communication with more than one 3rd party API.

JsonPlaceholderApi/PostsApi.cs

C#
 




xxxxxxxxxx
1
28


 
1
using System.Collections.Generic;
2
using System.Threading.Tasks;
3
using Application.Posts;
4
using Application.Posts.Commands.CreatePost;
5
using Application.Posts.Queries.GetAllPosts;
6

          
7
namespace Infrastructure.JsonPlaceholderApi
8
{
9
   public class PostsApi : IPostsApi
10
   {
11
       private readonly JsonPlaceholderClient _client;
12

          
13
       public PostsApi(JsonPlaceholderClient client)
14
       {
15
           _client = client;
16
       }
17
      
18
       public async Task<IEnumerable<GetAllPostsResponse>> GetAllPosts()
19
       {
20
           return await _client.GetAllPosts();
21
       }
22

          
23
       public async Task<CreatePostResponse> CreatePost(CreatePostRequest request)
24
       {
25
           return await _client.CreatePost(request);
26
       }
27
   }
28
}



Here we have one more abstraction on our way to the HttpClient. Why? Why not combine JsonPlaceholderClient and PostsApi into one class?

The reason is the instancing of HttpClient primarily and separation. Most of us are used to repository patterns so by somewhat following them we know what this class is about.

If you want to learn more about issues with instancing HttpClient, I suggest you read this.

DependencyInjection.cs

C#
 




xxxxxxxxxx
1
24


 
1
using System;
2
using Application.Posts;
3
using Infrastructure.JsonPlaceholderApi;
4
using Microsoft.Extensions.Configuration;
5
using Microsoft.Extensions.DependencyInjection;
6

          
7
namespace Infrastructure
8
{
9
   public static class DependencyInjection
10
   {
11
       public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
12
       {
13
           services.AddHttpClient<JsonPlaceholderClient>("JsonPlaceholderClient", config =>
14
           {
15
               config.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
16
               config.Timeout = TimeSpan.FromSeconds(30);
17
           });
18

          
19
           services.AddTransient<IPostsApi, PostsApi>();
20
          
21
           return services;
22
       }
23
   }
24
}



GitHub Codebase

The entire project can be found here.

Conclusion

After reading this blog, I hope that you’ve gained a better understanding of CQRS and are excited to take on new challenges. Personally, for me, CQRS is the way to go and there is no better way to organize your project, subjectively speaking, until the next new big programming acronym comes around to do bigger and better things.  Thank you for your patience for going through this with me and I wish you good luck and happy coding!

You can find the original blog source here. 

Database application IT Business logic .NET microservice Architecture Concept (generic programming) POST (HTTP) Blog

Published at DZone with permission of Faris Karcic. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Single Responsibility Principle: The Most Important Rule in the Software World
  • Fixing Your Microservices Architecture Using Graph Analysis
  • The Anatomy of a Microservice, One Service, Multiple Servers
  • Monolithic First

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!