I'm reading up on Clean architecture on Microsoft Docs.
I have also downloaded the eShopOnWeb reference application.
https://github.com/dotnet-architecture/eShopOnWeb
As seen in the image below and the reference implementation View Models are kept in the Web project and Dtos are in the PublicApi project.
Looking at how entities are converted to View Models and Dtos it looks like this:
var items = await _catalogBrandRepository.ListAsync();
response.CatalogBrands.AddRange(items.Select(_mapper.Map<CatalogBrandDto>));
or
var orders = await _orderRepository.ListAsync(specification, cancellationToken);
return orders.Select(o => new OrderViewModel
Code that sets up IRepository for _catalogBrandRepository and _orderRepository.
builder.Services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
builder.Services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
or:
services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
This works really well until performance is needed and I only want to project properties I need.
Given the set up I can not not select directly to a Dto given that the references will be wrong:
public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
{
private readonly CatalogContext _dbContext;
public EfRepository(CatalogContext dbContext) : base(dbContext)
{
_dbContext = dbContext;
}
public List<OrderViewModel> GetAllOrderViewModels()
{
//Does not work
return _dbContext.Orders.Select(o => new OrderViewModel
{
OrderDate = o.OrderDate,
OrderNumber = o.Id,
ShippingAddress = o.ShipToAddress,
Total = o.Total()
}).ToList();
}
}
I could of course return the anonymous type as dynamic but this is not good architecture imao.
public List<dynamic> GetAllOrderViewModels()
{
return _dbContext.Orders.Select(o => new
{
OrderDate = o.OrderDate,
OrderNumber = o.Id,
ShippingAddress = o.ShipToAddress,
Total = o.Total()
}).ToList<dynamic>();
}
Is the correct approach to add specific Dtos to ApplicationCore, Infrastructure or is there something I'm missing?
The most similar I found was BasketQueryService but it only returns an int.
public class BasketQueryService : IBasketQueryService
{
private readonly CatalogContext _dbContext;
public BasketQueryService(CatalogContext dbContext)
{
_dbContext = dbContext;
}
/// <summary>
/// This method performs the sum on the database rather than in memory
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public async Task<int> CountTotalBasketItems(string username)
{
var totalItems = await _dbContext.Baskets
.Where(basket => basket.BuyerId == username)
.SelectMany(item => item.Items)
.SumAsync(sum => sum.Quantity);
return totalItems;
}
}
Given that the question is about Clean architecture I deemed it OK to be asked.
