Communicating Between the Application Layer and the API in .NET - Featured Image
App Development3 min read

Communicating Between the Application Layer and the API in .NET

When working with a layered or Clean Architecture in .NET, one of the key aspects for maintaining cohesion and separation of concerns is understanding how the Application Layer communicates with the API Layer (or Web Layer in the case of an ASP.NET Core application).

In this article, we’ll explore:


πŸ“ The Role of Each Layer (API vs Application)

🧠 How to Structure Communication Between Them

πŸ’‘ Examples Using Handlers, DTOs, and MediatR

πŸ§ͺ Best Practices for Testability and Decoupling


πŸ—οΈ Architectural Layers Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      API Layer      β”‚ ← HTTP Interface (Controllers, Endpoints)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Application Layer  β”‚ ← Use Cases, DTOs, Handlers
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    Domain Layer     β”‚ ← Entities, Business Rules, Interfaces
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Infrastructure Layerβ”‚ ← Implementations, Persistence, Services
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“€ 1. Who Calls Whom?

The API Layer is responsible for receiving HTTP requests and forwarding them to use cases (Application Layer). The business logic does not live in the controller β€” it's encapsulated inside the Application Handlers.


πŸ“¦ 2. Creating a Use Case (Command Handler)

// Application/Orders/Commands/CreateOrderCommand.cs
public record CreateOrderCommand(string CustomerEmail, List<string> Items) : IRequest<Guid>;
// Application/Orders/Handlers/CreateOrderHandler.cs
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, Guid>
{
    public async Task<Guid> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
    {
        // Business rules, validations, persistence via interface
        var orderId = Guid.NewGuid(); // Simulated example
        return await Task.FromResult(orderId);
    }
}

🧠 We are using MediatR to orchestrate the flow between layers without direct coupling.


🌐 3. Calling Application from API

// API/Controllers/OrdersController.cs
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    private readonly IMediator _mediator;

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

    [HttpPost]
    public async Task<IActionResult> Create([FromBody] CreateOrderCommand command)
    {
        var orderId = await _mediator.Send(command);
        return CreatedAtAction(nameof(GetById), new { id = orderId }, orderId);
    }

    [HttpGet("{id}")]
    public IActionResult GetById(Guid id)
    {
        // You could use another query/handler here
        return Ok(new { Id = id, Status = "Mocked Order" });
    }
}

πŸ” 4. Summary Flow

[HTTP Request]
     ↓
[API Controller] β†’ Receives and validates
     ↓
[Mediator.Send(command)]
     ↓
[Application Handler]
     ↓
[Domain + Business Rules]
     ↓
[Persistence Interface]

πŸ“Œ 5. DTO Separation

Avoid exposing domain models directly from the API. Use specific DTOs for input and output.

// API.DTOs
public record CreateOrderRequest(string CustomerEmail, List<string> Items);

[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateOrderRequest request)
{
    var command = new CreateOrderCommand(request.CustomerEmail, request.Items);
    var orderId = await _mediator.Send(command);
    return Ok(orderId);
}

πŸ§ͺ 6. Testability & Benefits

βœ… Easily test Handlers independently of the API
βœ… No ASP.NET Core dependency in Application Layer
βœ… Each layer with a single responsibility
βœ… Flexible for other interfaces (Blazor, Console, CLI, etc.)


🧭 7. Suggested Project Structure

/API
  └── Controllers/
  └── DTOs/
  └── Program.cs

/Application
  └── Orders/
      β”œβ”€β”€ Commands/
      β”œβ”€β”€ Queries/
      β”œβ”€β”€ Handlers/
      └── Validators/

/Domain
  └── Entities/
  └── Interfaces/

/Infrastructure
  └── Persistence/
  └── Services/

βœ… Conclusion

The communication between the API and the Application Layer in modern .NET should be clean, decoupled, and testable. By using MediatR, DTOs, and layered separation of responsibilities, you can:

  • Apply Clean Architecture in practice

  • Scale your application with confidence

  • Maintain readability and cohesion across layers


Perfeito! VocΓͺ pode adicionar esses links de contato no final do artigo traduzido em inglΓͺs com o seguinte bloco:


🀝 Connect with Me

If you work with modern .NET and want to master architecture, C#, DevOps, or interoperability, let’s talk:

Posted on: 19/7/2025

daniloopinheiro

.NET Software Architect | Sr. Consultant | Tech Instructor

Posted by

β€Œ
β€Œ
β€Œ
β€Œ

Subscribe to our newsletter

Join 2,000+ subscribers

Stay in the loop with everything you need to know.

We care about your data in our privacy policy

Background shadow leftBackground shadow right

Have something to share?

Write on the platform and dummy copy content

Be Part of Something Big

Shifters, a developer-first community platform, is launching soon with all the features. Don't miss out on day one access. Join the waitlist: