I'm reading up on Clean architecture on Microsoft Docs.
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.
https://stackoverflow.com/help/dont-ask
CodePudding user response:
Application Core
The Application Core holds the business model, which includes entities, services, and interfaces. These interfaces include abstractions for operations that will be performed using Infrastructure, such as data access, file system access, network calls, etc. Sometimes services or interfaces defined at this layer will need to work with non-entity types that have no dependencies on UI or Infrastructure. These can be defined as simple Data Transfer Objects (DTOs).
Given this documentation I created a OrderDto
and placed it in ApplicationCore
-> Dto
.
namespace Microsoft.eShopWeb.ApplicationCore.Dto;
public class OrderDto
{
private const string DEFAULT_STATUS = "Pending";
public int OrderNumber { get; set; }
public DateTimeOffset OrderDate { get; set; }
public decimal Total { get; set; }
public string Status => DEFAULT_STATUS;
public Address ShippingAddress { get; set; }
}
Example in EfRepository
even though something like OrderQueryService
based on BasketQueryService
would be better:
public List<OrderDto> GetAllOrderDtos()
{
return _dbContext.Orders.Select(o => new OrderDto
{
OrderDate = o.OrderDate,
OrderNumber = o.Id,
ShippingAddress = o.ShipToAddress,
Total = o.Total()
}).ToList();
}