Lädt...


🔧 What is Clean Architecture: Part 19 -Add Controllers


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

In this article, we’ll see how to build lightweight and loosely coupled API controllers using MediatR in an ASP.NET Core 8 application. We’ve already set up the essential layers—Application, Infrastructure, and Persistence—and now it’s time to expose functionality through an API that communicates with the core of our application using the MediatR pattern.

Why Use MediatR?

MediatR is a powerful library that helps decouple the application’s layers by acting as a mediator between the controllers and the business logic (commands and queries). It promotes the Single Responsibility Principle (SRP) by separating the concerns of business logic from HTTP request handling, making our code easier to maintain, test, and extend.

Setting Up the API Project

In our project, we have already registered MediatR in the Application Layer via the AddApplicationServices() method, so there’s no need to add it again directly in the Program.cs file. Here's a reminder of how we set it up in the StartupExtensions.cs:

using GloboTicket.TicketManagement.Application;
using GloboTicket.TicketManagement.Infrastructure;
using GloboTicket.TicketManagement.Persistence;
namespace GloboTicket.TicketManagement.Api
{
    public static class StartupExtensions
    {
        public static WebApplication ConfigureServices(
           this WebApplicationBuilder builder)
        {
            builder.Services.AddApplicationServices();
            builder.Services.AddInfrastructureServices(builder.Configuration);
            builder.Services.AddPersistenceServices(builder.Configuration);
            builder.Services.AddControllers();

            builder.Services.AddCors(
                options => options.AddPolicy(
                    "open",
                    policy => policy.WithOrigins([builder.Configuration["ApiUrl"] ?? "https://localhost:7020",
                        builder.Configuration["BlazorUrl"] ?? "https://localhost:7080"])
            .AllowAnyMethod()
            .SetIsOriginAllowed(pol => true)
            .AllowAnyHeader()
            .AllowCredentials()));

            builder.Services.AddSwaggerGen();
            return builder.Build();
        }

        public static WebApplication ConfigurePipeline(this WebApplication app)
        {
            app.UseCors("open");

            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }


            app.UseHttpsRedirection();
            app.MapControllers();

            return app;
        }
    }
}

This ensures that MediatR is available throughout the application via dependency injection. Now, let’s move on to creating the controllers.

Creating the CategoryController

The CategoryController will expose endpoints to handle various category-related API requests, like fetching all categories or adding a new category. Each request will be handled by sending the appropriate query or command to MediatR, which in turn delegates the logic to the correct handler.

using GloboTicket.TicketManagement.Application.Features.Categories.Commands.CreateCategory;
using GloboTicket.TicketManagement.Application.Features.Categories.Queries.GetCategoriesList;
using GloboTicket.TicketManagement.Application.Features.Categories.Queries.GetCategoriesListWithEvents;
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace YourApp.Api.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class CategoryController : Controller
    {
        private readonly IMediator _mediator;

        public CategoryController(IMediator mediator)
        {
            _mediator = mediator;
        }

        [HttpGet("all", Name = "GetAllCategories")]
        public async Task<ActionResult<List<CategoryListVm>>> GetAllCategories()
        {
            var dtos = await _mediator.Send(new GetCategoriesListQuery());
            return Ok(dtos);
        }

        [HttpGet("allwithevents", Name = "GetCategoriesWithEvents")]
        public async Task<ActionResult<List<CategoryEventListVm>>> GetCategoriesWithEvents(bool includeHistory)
        {
            var query = new GetCategoriesListWithEventsQuery { IncludeHistory = includeHistory };
            var dtos = await _mediator.Send(query);
            return Ok(dtos);
        }

        [HttpPost(Name = "AddCategory")]
        public async Task<ActionResult<CreateCategoryCommandResponse>> Create([FromBody] CreateCategoryCommand command)
        {
            var response = await _mediator.Send(command);
            return Ok(response);
        }
    }
}

Key Points of CategoryController:

  • GetAllCategories: Sends a GetCategoriesListQuery to MediatR. The query handler processes the logic and returns the result, which is sent back as an HTTP 200 response.
  • GetCategoriesWithEvents: Similar to GetAllCategories, this query includes an additional Boolean parameter (includeHistory) to determine whether historical data should be included.
  • Create: Handles POST requests to create a new category. The CreateCategoryCommand is passed to MediatR, which sends it to the appropriate handler, returning the response from the handler.

Creating the EventsController

Similarly, the EventsController will handle event-related API requests such as fetching all events, retrieving event details, creating, updating, and deleting events. Each action is decoupled from the controller logic by using MediatR.

using GloboTicket.TicketManagement.Application.Features.Events.Commands.CreateEvent;
using GloboTicket.TicketManagement.Application.Features.Events.Commands.DeleteEvent;
using GloboTicket.TicketManagement.Application.Features.Events.Commands.UpdateEvent;
using GloboTicket.TicketManagement.Application.Features.Events.Queries.GetEventDetail;
using GloboTicket.TicketManagement.Application.Features.Events.Queries.GetEventsList;
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace YourApp.Api.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class EventsController : Controller
    {
        private readonly IMediator _mediator;

        public EventsController(IMediator mediator)
        {
            _mediator = mediator;
        }

        [HttpGet(Name = "GetAllEvents")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesDefaultResponseType]
        public async Task<ActionResult<List<EventListVm>>> GetAllEvents()
        {
            var result = await _mediator.Send(new GetEventsListQuery());
            return Ok(result);
        }

        [HttpGet("{id}", Name = "GetEventById")]
        public async Task<ActionResult<EventDetailVm>> GetEventById(Guid id)
        {
            var query = new GetEventDetailQuery { Id = id };
            return Ok(await _mediator.Send(query));
        }

        [HttpPost(Name = "AddEvent")]
        public async Task<ActionResult<Guid>> Create([FromBody] CreateEventCommand command)
        {
            var id = await _mediator.Send(command);
            return Ok(id);
        }

        [HttpPut(Name = "UpdateEvent")]
        [ProducesResponseType(StatusCodes.Status204NoContent)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesDefaultResponseType]
        public async Task<ActionResult> Update([FromBody] UpdateEventCommand command)
        {
            await _mediator.Send(command);
            return NoContent();
        }

        [HttpDelete("{id}", Name = "DeleteEvent")]
        [ProducesResponseType(StatusCodes.Status204NoContent)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesDefaultResponseType]
        public async Task<ActionResult> Delete(Guid id)
        {
            var command = new DeleteEventCommand { EventId = id };
            await _mediator.Send(command);
            return NoContent();
        }
    }
}

Key Points of EventsController:

  • GetAllEvents: Retrieves all events using GetEventsListQuery and returns them to the client.
  • GetEventById: Fetches the details of a specific event by sending an id in the GetEventDetailQuery.
  • Create: Handles the creation of new events. A CreateEventCommand is passed from the client, and MediatR forwards it to the handler responsible for event creation.
  • Update: Updates an existing event by sending an UpdateEventCommand. The handler processes the update logic, and a 204 No Content response is returned.
  • Delete: Deletes an event based on its id using DeleteEventCommand.

Conclusion

By leveraging MediatR, we have created lightweight, loosely coupled controllers for handling categories and events in an ASP.NET Core 8 application. The key advantage of using MediatR is the separation of concerns: the controllers focus solely on handling HTTP requests and responses, while business logic is handled by specific handlers in the application layer.

This approach ensures that our API remains maintainable, scalable, and easy to extend as the application grows. In future articles, we’ll explore advanced MediatR features, such as pipeline behaviors, validation, and more complex business logic.

For the complete source code, feel free to visit the GitHub repository here.

...

🔧 Comparing All-in-One Architecture, Layered Architecture, and Clean Architecture


📈 37.07 Punkte
🔧 Programmierung

🔧 Clean Architecture: Keeping Code Clean and Maintainable


📈 29.26 Punkte
🔧 Programmierung

🔧 The difference between clean code and clean architecture?


📈 29.26 Punkte
🔧 Programmierung

🪟 PowerA’s new FUSION Controllers for Xbox are putting the native controllers to shame


📈 26.75 Punkte
🪟 Windows Tipps

🪟 Microsoft is branching out from custom Xbox controllers to custom chocolate controllers


📈 26.75 Punkte
🪟 Windows Tipps

🕵️ Mozilla Firefox up to 1.0.8/1.5.0.2 windows.controllers window.controllers cross site scriting


📈 26.75 Punkte
🕵️ Sicherheitslücken

🕵️ NodeBB up to 0.7.2 controllers/index.js Controllers.outgoing cross site scripting


📈 26.75 Punkte
🕵️ Sicherheitslücken

🔧 What is Clean Architecture: Part 16 - Adding Data Persistence with Entity Framework Core


📈 24.94 Punkte
🔧 Programmierung

🔧 What is Clean Architecture: Part 13-Adding Validation Using Fluent Validation


📈 24.94 Punkte
🔧 Programmierung

🔧 What is Clean Architecture: Part 14-last step in Application Core Layer


📈 24.94 Punkte
🔧 Programmierung

🔧 What is Clean Architecture: Part 12-Creating, Updating, and Deleting Entities Using Commands


📈 24.94 Punkte
🔧 Programmierung

🔧 What is Clean Architecture: Part 11-Organizing the Code Using Features


📈 24.94 Punkte
🔧 Programmierung

🔧 What is Clean Architecture: Part 10 - Writing the Application Logic in the Request Handler


📈 24.94 Punkte
🔧 Programmierung

🔧 Path To A Clean(er) React Architecture (Part 8) - How Does React-Query Fit Into The Picture?


📈 24.94 Punkte
🔧 Programmierung

🔧 Path To A Clean(er) React Architecture (Part 7) - Domain Logic


📈 24.94 Punkte
🔧 Programmierung

🔧 Path To A Clean(er) React Architecture (Part 6) - Business Logic Separation


📈 24.94 Punkte
🔧 Programmierung

🔧 What is Clean Architecture: Part 18 - Adding API


📈 24.94 Punkte
🔧 Programmierung

🔧 Building Your First Spring Boot App: A Complete Guide to MVC Architecture and REST Controllers


📈 22.35 Punkte
🔧 Programmierung

🔧 Clean Code: Definition and Principles - Part 3 (Last Part)


📈 21.78 Punkte
🔧 Programmierung

🔧 The Art of Writing Clean Functions: Clean Code Practices


📈 20.28 Punkte
🔧 Programmierung

🔧 The Clean Code book and the clean code paradigms


📈 20.28 Punkte
🔧 Programmierung

📰 How to Clean Up the Clean Energy Transition: Preventing Violence Over New ‘Conflict Minerals’


📈 20.28 Punkte
📰 IT Security Nachrichten

🔧 Excel Tutorial – How to Clean Data with the TRIM() and CLEAN() Functions


📈 20.28 Punkte
🔧 Programmierung

🔧 Creating your own ExpressJS from scratch (Part 2) - Middlewares and Controllers


📈 19.19 Punkte
🔧 Programmierung

🔧 Kubernetes 101, part III, controllers and self-healing


📈 19.19 Punkte
🔧 Programmierung

🕵️ Hardware Debugging for Reverse Engineers Part 1: SWD, OpenOCD and Xbox One Controllers


📈 19.19 Punkte
🕵️ Reverse Engineering

🔧 Mastering Flutter Architecture: From CLEAN to Feature-First for Faster, Scalable Development


📈 19.12 Punkte
🔧 Programmierung

🔧 Python Architecture Essentials: Building Scalable and Clean Application for Juniors


📈 19.12 Punkte
🔧 Programmierung

📰 heise-Angebot: betterCode() Clean Architecture: Konferenz für ein nachhaltiges Software-Design


📈 19.12 Punkte
📰 IT Nachrichten

🎥 Solution2: Clean Architecture with ASP.NET Core with Steve Smith


📈 19.12 Punkte
🎥 Video | Youtube

matomo