Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions src/Supermarket.API/Controllers/CategoriesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,20 @@

namespace Supermarket.API.Controllers
{
public class CategoriesController : BaseApiController
public class CategoriesController(IServiceManager serviceManager, IMapper mapper) : BaseApiController
{
private readonly ICategoryService _categoryService;
private readonly IMapper _mapper;
private readonly IServiceManager _serviceManager = serviceManager;
private readonly IMapper _mapper = mapper;

public CategoriesController(ICategoryService categoryService, IMapper mapper)
{
_categoryService = categoryService;
_mapper = mapper;
}

/// <summary>
/// Lists all categories.
/// </summary>
/// <returns>List os categories.</returns>
[HttpGet]
/// <summary>
/// Lists all categories.
/// </summary>
/// <returns>List os categories.</returns>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<CategoryResource>), 200)]
public async Task<IEnumerable<CategoryResource>> ListAsync()
{
var categories = await _categoryService.ListAsync();
var categories = await _serviceManager.CategoryService.ListAsync();
return _mapper.Map<IEnumerable<CategoryResource>>(categories);
}

Expand All @@ -39,7 +33,7 @@ public async Task<IEnumerable<CategoryResource>> ListAsync()
public async Task<IActionResult> PostAsync([FromBody] SaveCategoryResource resource)
{
var category = _mapper.Map<Category>(resource);
var result = await _categoryService.SaveAsync(category);
var result = await _serviceManager.CategoryService.SaveAsync(category);

if (!result.Success)
{
Expand All @@ -62,7 +56,7 @@ public async Task<IActionResult> PostAsync([FromBody] SaveCategoryResource resou
public async Task<IActionResult> PutAsync(int id, [FromBody] SaveCategoryResource resource)
{
var category = _mapper.Map<Category>(resource);
var result = await _categoryService.UpdateAsync(id, category);
var result = await _serviceManager.CategoryService.UpdateAsync(id, category);

if (!result.Success)
{
Expand All @@ -83,7 +77,7 @@ public async Task<IActionResult> PutAsync(int id, [FromBody] SaveCategoryResourc
[ProducesResponseType(typeof(ErrorResource), 400)]
public async Task<IActionResult> DeleteAsync(int id)
{
var result = await _categoryService.DeleteAsync(id);
var result = await _serviceManager.CategoryService.DeleteAsync(id);

if (!result.Success)
{
Expand Down
20 changes: 7 additions & 13 deletions src/Supermarket.API/Controllers/ProductsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,10 @@

namespace Supermarket.API.Controllers
{
public class ProductsController : BaseApiController
public class ProductsController(IServiceManager serviceManager, IMapper mapper) : BaseApiController
{
private readonly IProductService _productService;
private readonly IMapper _mapper;

public ProductsController(IProductService productService, IMapper mapper)
{
_productService = productService;
_mapper = mapper;
}
private readonly IServiceManager _serviceManager = serviceManager;
private readonly IMapper _mapper = mapper;

/// <summary>
/// Lists all existing products according to query filters.
Expand All @@ -25,7 +19,7 @@ public ProductsController(IProductService productService, IMapper mapper)
public async Task<QueryResultResource<ProductResource>> ListAsync([FromQuery] ProductsQueryResource query)
{
var productsQuery = _mapper.Map<ProductsQuery>(query);
var queryResult = await _productService.ListAsync(productsQuery);
var queryResult = await _serviceManager.ProductService.ListAsync(productsQuery);

return _mapper.Map<QueryResultResource<ProductResource>>(queryResult);
}
Expand All @@ -41,7 +35,7 @@ public async Task<QueryResultResource<ProductResource>> ListAsync([FromQuery] Pr
public async Task<IActionResult> PostAsync([FromBody] SaveProductResource resource)
{
var product = _mapper.Map<Product>(resource);
var result = await _productService.SaveAsync(product);
var result = await _serviceManager.ProductService.SaveAsync(product);

if (!result.Success)
{
Expand All @@ -64,7 +58,7 @@ public async Task<IActionResult> PostAsync([FromBody] SaveProductResource resour
public async Task<IActionResult> PutAsync(int id, [FromBody] SaveProductResource resource)
{
var product = _mapper.Map<Product>(resource);
var result = await _productService.UpdateAsync(id, product);
var result = await _serviceManager.ProductService.UpdateAsync(id, product);

if (!result.Success)
{
Expand All @@ -85,7 +79,7 @@ public async Task<IActionResult> PutAsync(int id, [FromBody] SaveProductResource
[ProducesResponseType(typeof(ErrorResource), 400)]
public async Task<IActionResult> DeleteAsync(int id)
{
var result = await _productService.DeleteAsync(id);
var result = await _serviceManager.ProductService.DeleteAsync(id);

if (!result.Success)
{
Expand Down
2 changes: 2 additions & 0 deletions src/Supermarket.API/Domain/Repositories/IUnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ namespace Supermarket.API.Domain.Repositories
{
public interface IUnitOfWork
{
IProductRepository ProductRepository { get; }
ICategoryRepository CategoryRepository { get; }
Task CompleteAsync();
}
}
8 changes: 8 additions & 0 deletions src/Supermarket.API/Domain/Services/IServiceManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Supermarket.API.Domain.Services
{
public interface IServiceManager
{
IProductService ProductService { get; }
ICategoryService CategoryService { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@

namespace Supermarket.API.Persistence.Repositories
{
public abstract class BaseRepository
public abstract class BaseRepository(AppDbContext context)
{
protected readonly AppDbContext _context;

public BaseRepository(AppDbContext context)
{
_context = context;
}
protected readonly AppDbContext _context = context;
}
}
11 changes: 7 additions & 4 deletions src/Supermarket.API/Persistence/Repositories/UnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ namespace Supermarket.API.Persistence.Repositories
public class UnitOfWork(AppDbContext context) : IUnitOfWork
{
private readonly AppDbContext _context = context;
private readonly Lazy<IProductRepository> _productRepository= new Lazy<IProductRepository>(() => new ProductRepository(context));
private readonly Lazy<ICategoryRepository> _categoryRepository= new Lazy<ICategoryRepository>(() => new CategoryRepository(context));

public async Task CompleteAsync()
{
await _context.SaveChangesAsync();
}

public IProductRepository ProductRepository => _productRepository.Value;
public ICategoryRepository CategoryRepository => _categoryRepository.Value;

public async Task CompleteAsync() => await _context.SaveChangesAsync();
}
}
10 changes: 9 additions & 1 deletion src/Supermarket.API/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,13 @@
"httpPort": 5000
}
},
"$schema": "http://json.schemastore.org/launchsettings.json"
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:62953/",
"sslPort": 44321
}
}
}
5 changes: 1 addition & 4 deletions src/Supermarket.API/Resources/ErrorResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ public record ErrorResource
public bool Success => false;
public List<string> Messages { get; private set; }

public ErrorResource(List<string> messages)
{
Messages = messages ?? [];
}
public ErrorResource(List<string> messages) => Messages = messages ?? [];

public ErrorResource(string message)
{
Expand Down
39 changes: 14 additions & 25 deletions src/Supermarket.API/Services/CategoryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,24 @@

namespace Supermarket.API.Services
{
public class CategoryService : ICategoryService
public class CategoryService(
IUnitOfWork unitOfWork,
IMemoryCache cache,
ILogger<CategoryService> logger
) : ICategoryService
{
private readonly ICategoryRepository _categoryRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly IMemoryCache _cache;
private readonly ILogger<CategoryService> _logger;
private readonly IUnitOfWork _unitOfWork = unitOfWork;
private readonly IMemoryCache _cache = cache;
private readonly ILogger<CategoryService> _logger = logger;

public CategoryService
(
ICategoryRepository categoryRepository,
IUnitOfWork unitOfWork,
IMemoryCache cache,
ILogger<CategoryService> logger
)
{
_categoryRepository = categoryRepository;
_unitOfWork = unitOfWork;
_cache = cache;
_logger = logger;
}

public async Task<IEnumerable<Category>> ListAsync()
public async Task<IEnumerable<Category>> ListAsync()
{
// Here I try to get the categories list from the memory cache. If there is no data in cache, the anonymous method will be
// called, setting the cache to expire one minute ahead and returning the Task that lists the categories from the repository.
var categories = await _cache.GetOrCreateAsync(CacheKeys.CategoriesList, (entry) =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
return _categoryRepository.ListAsync();
return _unitOfWork.CategoryRepository.ListAsync();
});

return categories ?? new List<Category>();
Expand All @@ -44,7 +33,7 @@ public async Task<Response<Category>> SaveAsync(Category category)
{
try
{
await _categoryRepository.AddAsync(category);
await _unitOfWork.CategoryRepository.AddAsync(category);
await _unitOfWork.CompleteAsync();

return new Response<Category>(category);
Expand All @@ -58,7 +47,7 @@ public async Task<Response<Category>> SaveAsync(Category category)

public async Task<Response<Category>> UpdateAsync(int id, Category category)
{
var existingCategory = await _categoryRepository.FindByIdAsync(id);
var existingCategory = await _unitOfWork.CategoryRepository.FindByIdAsync(id);
if (existingCategory == null)
{
return new Response<Category>("Category not found.");
Expand All @@ -80,15 +69,15 @@ public async Task<Response<Category>> UpdateAsync(int id, Category category)

public async Task<Response<Category>> DeleteAsync(int id)
{
var existingCategory = await _categoryRepository.FindByIdAsync(id);
var existingCategory = await _unitOfWork.CategoryRepository.FindByIdAsync(id);
if (existingCategory == null)
{
return new Response<Category>("Category not found.");
}

try
{
_categoryRepository.Remove(existingCategory);
_unitOfWork.CategoryRepository.Remove(existingCategory);
await _unitOfWork.CompleteAsync();

return new Response<Category>(existingCategory);
Expand Down
47 changes: 17 additions & 30 deletions src/Supermarket.API/Services/ProductService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,16 @@

namespace Supermarket.API.Services
{
public class ProductService : IProductService
public class ProductService(
IUnitOfWork unitOfWork,
IMemoryCache cache,
ILogger<ProductService> logger
) : IProductService
{
private readonly IProductRepository _productRepository;
private readonly ICategoryRepository _categoryRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly IMemoryCache _cache;
private readonly ILogger<ProductService> _logger;

public ProductService
(
IProductRepository productRepository,
ICategoryRepository categoryRepository,
IUnitOfWork unitOfWork,
IMemoryCache cache,
ILogger<ProductService> logger
)
{
_productRepository = productRepository;
_categoryRepository = categoryRepository;
_unitOfWork = unitOfWork;
_cache = cache;
_logger = logger;
}

private readonly IUnitOfWork _unitOfWork = unitOfWork;
private readonly IMemoryCache _cache = cache;
private readonly ILogger<ProductService> _logger = logger;

public async Task<QueryResult<Product>> ListAsync(ProductsQuery query)
{
Expand All @@ -39,7 +26,7 @@ public async Task<QueryResult<Product>> ListAsync(ProductsQuery query)
var products = await _cache.GetOrCreateAsync(cacheKey, (entry) =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
return _productRepository.ListAsync(query);
return _unitOfWork.ProductRepository.ListAsync(query);
});

return products!;
Expand All @@ -54,11 +41,11 @@ public async Task<Response<Product>> SaveAsync(Product product)
You can create a method into the CategoryService class to return the category and inject the service here if you prefer, but
it doesn't matter given the API scope.
*/
var existingCategory = await _categoryRepository.FindByIdAsync(product.CategoryId);
var existingCategory = await _unitOfWork.CategoryRepository.FindByIdAsync(product.CategoryId);
if (existingCategory == null)
return new Response<Product>("Invalid category.");

await _productRepository.AddAsync(product);
await _unitOfWork.ProductRepository.AddAsync(product);
await _unitOfWork.CompleteAsync();

return new Response<Product>(product);
Expand All @@ -72,12 +59,12 @@ it doesn't matter given the API scope.

public async Task<Response<Product>> UpdateAsync(int id, Product product)
{
var existingProduct = await _productRepository.FindByIdAsync(id);
var existingProduct = await _unitOfWork.ProductRepository.FindByIdAsync(id);

if (existingProduct == null)
return new Response<Product>("Product not found.");

var existingCategory = await _categoryRepository.FindByIdAsync(product.CategoryId);
var existingCategory = await _unitOfWork.CategoryRepository.FindByIdAsync(product.CategoryId);
if (existingCategory == null)
return new Response<Product>("Invalid category.");

Expand All @@ -88,7 +75,7 @@ public async Task<Response<Product>> UpdateAsync(int id, Product product)

try
{
_productRepository.Update(existingProduct);
_unitOfWork.ProductRepository.Update(existingProduct);
await _unitOfWork.CompleteAsync();

return new Response<Product>(existingProduct);
Expand All @@ -102,14 +89,14 @@ public async Task<Response<Product>> UpdateAsync(int id, Product product)

public async Task<Response<Product>> DeleteAsync(int id)
{
var existingProduct = await _productRepository.FindByIdAsync(id);
var existingProduct = await _unitOfWork.ProductRepository.FindByIdAsync(id);

if (existingProduct == null)
return new Response<Product>("Product not found.");

try
{
_productRepository.Remove(existingProduct);
_unitOfWork.ProductRepository.Remove(existingProduct);
await _unitOfWork.CompleteAsync();

return new Response<Product>(existingProduct);
Expand Down
Loading