Onion Architecture In ASP.NET Core With CQRS

Aykut Aktaş
4 min readJan 25, 2024

A software architectural pattern that promotes a modular and maintainable structure by organizing code into concentric layers. The layers represent different concerns, with the core functionality at the center and external dependencies in the outer layers.

Layers of Onion Architecture

Unveiling the Power of Onion Architecture in ASP.NET Core with CQRS

In the ever-evolving landscape of web development, creating scalable, maintainable, and modular applications is crucial. One powerful approach gaining traction is the combination of Onion Architecture and Command Query Responsibility Segregation (CQRS) in ASP.NET Core. Let’s delve into the intricacies of this architectural pattern and explore how it can revolutionize the way we design and build web applications.

The Core of Onion Architecture

Onion Architecture, resembling the layers of an onion, consists of concentric circles representing different concerns. At its core lies the business logic, surrounded by layers for application services, interfaces, and external dependencies. This modular structure promotes a clean separation of concerns, making the codebase more understandable and maintainable.

1. Core (Business Logic):

// Example Domain Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
// ... other properties and business logic
}

// Example Repository Interface
public interface IProductRepository
{
Product GetById(int id);
void Save(Product product);
// ... other repository methods
}

// Example Service in Core
public class ProductService
{
private readonly IProductRepository _productRepository;

public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
}

public Product GetProductById(int id)
{
return _productRepository.GetById(id);
}

public void SaveProduct(Product product)
{
_productRepository.Save(product);
}
// ... other business logic methods
}

2. Infrastructure (External Dependencies):

// Example Implementation of Repository using Entity Framework
public class EFProductRepository : IProductRepository
{
private readonly DbContext _dbContext;

public EFProductRepository(DbContext dbContext)
{
_dbContext = dbContext;
}

public Product GetById(int id)
{
return _dbContext.Set<Product>().Find(id);
}

public void Save(Product product)
{
_dbContext.Set<Product>().Add(product);
_dbContext.SaveChanges();
}
// ... other repository methods
}

3. Application Services:

// Example DTO (Data Transfer Object)
public class ProductDTO
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
// ... other properties
}

// Example Application Service
public class ProductAppService
{
private readonly ProductService _productService;

public ProductAppService(ProductService productService)
{
_productService = productService;
}

public ProductDTO GetProductById(int id)
{
var product = _productService.GetProductById(id);

// Map domain model to DTO
var productDTO = new ProductDTO
{
Id = product.Id,
Name = product.Name,
Price = product.Price
// ... map other properties
};

return productDTO;
}
// ... other application service methods
}

4. Presentation (Interfaces):

// Example Controller in ASP.NET Core
public class ProductController : ControllerBase
{
private readonly ProductAppService _productAppService;

public ProductController(ProductAppService productAppService)
{
_productAppService = productAppService;
}

[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
var productDTO = _productAppService.GetProductById(id);

// Handle presentation logic, e.g., return a view or JSON response
return Ok(productDTO);
}
// ... other controller actions
}

These example code blocks showcase the structure of Onion Architecture, with clear separation of concerns among different layers. The business logic resides in the core, external dependencies are implemented in the infrastructure layer, application services handle the application-specific logic, and interfaces (such as controllers) are part of the presentation layer.

ASP.NET Core: A Robust Foundation

ASP.NET Core provides a cross-platform, high-performance framework for developing modern, cloud-ready applications. Its flexibility aligns seamlessly with Onion Architecture, allowing developers to leverage its features for building scalable and efficient web applications.

Command Query Responsibility Segregation (CQRS)

CQRS is a design pattern that advocates the separation of concerns for handling commands (operations that change state) and queries (operations that retrieve information). This pattern complements Onion Architecture by aligning with its layered structure, facilitating the development of applications that are not only modular but also scalable and maintainable.

Integration in ASP.NET Core

Onion Architecture’s clear separation of concerns facilitates smooth integration with ASP.NET Core. By defining distinct boundaries between layers, this architecture enhances testability, minimizes dependencies, and fosters a development environment where each module’s responsibilities are well-defined.

Unlocking Modularity

One of the key advantages of Onion Architecture is its ability to promote modularity. With clearly defined layers, extending or modifying the application becomes more straightforward. This modular approach ensures that changes are isolated, reducing the risk of unintended consequences and making the codebase more adaptable to evolving requirements.

Scalability and Maintainability

The combination of Onion Architecture and CQRS addresses scalability and maintainability challenges. By separating command and query responsibilities, applications can scale more effectively, and maintenance becomes less error-prone. This ensures a more responsive and resilient system as your application grows.

Embracing Command and Query Separation

CQRS encourages a clear separation between code paths for commands and queries. This aligns naturally with Onion Architecture, creating a development environment where each layer plays a distinct role in the application’s overall functionality. The result is a more cohesive and streamlined development process.

Testability at Its Core

Onion Architecture’s layered structure enhances testability by isolating each layer’s responsibilities. Unit testing becomes more straightforward, allowing developers to validate individual components without the need for extensive integration testing. This results in a more robust and reliable codebase.

Emphasizing Dependency Inversion

Onion Architecture’s reliance on the Dependency Inversion Principle promotes flexibility and ease of updates. High-level modules depend on abstractions rather than concrete implementations, making it simpler to introduce changes without affecting the entire codebase.

In conclusion, in the realm of ASP.NET Core development, adopting Onion Architecture with CQRS can be a game-changer. The modular, scalable, and maintainable nature of this approach empowers developers to create robust applications that stand the test of time. By embracing these architectural principles, you lay the foundation for a responsive, adaptable, and future-proof web application.

Github Link : https://github.com/bierquelle06/onionarchitecture

#OnionArchitecture #ASPNETCore #CQRS #ModularDevelopment #CleanCode #SoftwareArchitecture #CodeSeparation #Scalability #Maintainability #DependencyInversion #WebDevelopment #AspNet #ProgrammingPatterns #CodeModularity #Testability #ApplicationDesign #BusinessLogic #LayeredArchitecture #CodingBestPractices #SoftwareEngineering

--

--