'Simplifying custom queries in a layered CQRS architecture
Question: How could I simplify creating custom queries and reduce boilerplate? Also any improvement suggestions for this architecture are welcome.
Background: I'm developing an .NET MVC application using CQRS and clean architecture. My solution consists of following projects:
- Core: Contains domain models and interfaces
- Application (BLL): Uses CQRS architecture. Contains commands and queries and their handlers.
- Web (PL): Contains API controllers.
- Infrastructure (DAL): Contains logic and wrappers related to external systems, e.g. database, mailing, REST apis etc.
Infrastructure project contains implementations of repositories. Repositories are used by commands to persist data. Queries may also use repositories for simple queries, which are related to only one domain model. Creating repositories is handled by generic repository factory which uses reflection to find the correct implementation. Therefore using repositories in the code is quite simple, and adding a new repository only requires creating an implementation and interface for the repository:
using(var uow = _uowFactory.Create())
{
var bookRepository = _repositoryFactory.Create<IBookRepository>(uow);
bookRepository.UpdateBook(request.Book.Id, request.Book);
uow.Commit();
}
Complex, multi-relational queries are handled by a separate query class for each query. Interface for a query contains definition of possible response type and a definition for Execute method. Implementation receives a database connection factory via dependency injection and implements the Execute method by raw SQL using Dapper. Example:
In Core project:
public class BookSaleStatisticsResultDa
{
public string BookName {get; set;}
public int AmountSold {get; set;}
}
public interface IBookSaleStatisticsQueryDa
{
BookSaleStatisticsResultDa Execute (int bookId);
}
In Infrastructure project:
public class BookSaleStatisticsQueryDa
{
private readonly IDbConnectionFactory _connectionFactory;
public BookSaleStatisticsQueryDa(IDbConnectionFactory connectionFactory)
{
_connectionFactory = connectionFactory;
}
BookSaleStatisticsResultDa Execute (int bookId)
{
// Get data with dapper...
}
}
Problems with the current implementation:
- Creating new custom queries is a bit cumbersome. It should be simplified because there will be plenty of them. Currently you have to create an interface, implementation, register classes for DI and add queries as dependencies to class constructors of classes which use them. For repositories some of these operations are not necessary due to using a factory. Would same approach be suitable for queries, or is there a better alternative?
- Naming. There are queries on different layers (BLL and DAL). They and their result classes must be named differently to keep them easily distinguishable. Currently this is done by giving DAL queries a suffix "Da". I feel like the names are a bit long and unclear.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
