Files
DbFirst/DbFirst.Application/Catalogs/CatalogService.cs
TekH d608ab1a6d Refactor CORS config; add architectural commentary
Refactored CORS setup to be environment-aware, restricting origins in production and relaxing in development. Added extensive comments and discussion on service and repository layer design, including clean architecture best practices and CQRS/MediatR considerations. No changes to business logic; documentation and intent clarified for maintainers.
2026-01-19 11:17:36 +01:00

101 lines
3.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using AutoMapper;
using DbFirst.Domain.Repositories;
using DbFirst.Domain.Entities;
namespace DbFirst.Application.Catalogs;
//TODO: create generic service to reduce code duplication
/* Copilot's Response:
A generic CRUD base service adds little value in your case:
Pros:
• Less boilerplate for simple entities without special logic.
• Uniform CRUD signatures.
Cons/Practical here:
• Domain logic differs per entity(unique title check, setting audit fields, forbidding title changes, stored procs with output GUID).
• Generic services tend to be diluted by virtual methods/hooks for special cases—ending up with per-entity overrides and little real gain.
• With stored procedures and output parameters, the pattern doesnt fit cleanly because operations arent symmetric (separate procs for insert/update/delete).
Conclusion: For this solution a generic service would be more overhead than benefit. If you later have multiple very similar entities without special logic,
you could consider a lightweight generic interface/base; for now, the specialized service implementation is cleaner. */
/* Hakan's Response:
* No, it absolutely makes sense to create a generic service using Options pattern. So, you can easily inject your SQL queries or stored procedure names via configuration.
* see: https://docs.microsoft.com/en-us/dotnet/core/extensions/options
*/
//TODO: implement CQRS pattern with MediatR
/* Hakan's response
* Here is the main part. We dont even need a service layer if we implement CQRS with MediatR at least for CRUD operations.
*/
public class CatalogService : ICatalogService
{
private readonly ICatalogRepository _repository;
private readonly IMapper _mapper;
public CatalogService(ICatalogRepository repository, IMapper mapper)
{
_repository = repository;
_mapper = mapper;
}
public async Task<List<CatalogReadDto>> GetAllAsync(CancellationToken cancellationToken = default)
{
var items = await _repository.GetAllAsync(cancellationToken);
return _mapper.Map<List<CatalogReadDto>>(items);
}
public async Task<CatalogReadDto?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
{
var item = await _repository.GetByIdAsync(id, cancellationToken);
return item == null ? null : _mapper.Map<CatalogReadDto>(item);
}
public async Task<CatalogReadDto?> CreateAsync(CatalogWriteDto dto, CancellationToken cancellationToken = default)
{
var existing = await _repository.GetByTitleAsync(dto.CatTitle, cancellationToken);
if (existing != null)
{
return null;
}
var entity = _mapper.Map<VwmyCatalog>(dto);
entity.AddedWho = "system";
entity.AddedWhen = DateTime.UtcNow;
entity.ChangedWho = "system";
entity.ChangedWhen = DateTime.UtcNow;
var created = await _repository.InsertAsync(entity, cancellationToken);
return _mapper.Map<CatalogReadDto>(created);
}
public async Task<CatalogReadDto?> UpdateAsync(int id, CatalogWriteDto dto, CancellationToken cancellationToken = default)
{
var existing = await _repository.GetByIdAsync(id, cancellationToken);
if (existing == null)
{
return null;
}
var entity = _mapper.Map<VwmyCatalog>(dto);
entity.Guid = id;
entity.CatTitle = existing.CatTitle;
entity.AddedWho = existing.AddedWho;
entity.AddedWhen = existing.AddedWhen;
entity.ChangedWho = "system";
entity.ChangedWhen = DateTime.UtcNow;
var updated = await _repository.UpdateAsync(id, entity, cancellationToken);
return updated == null ? null : _mapper.Map<CatalogReadDto>(updated);
}
public async Task<bool> DeleteAsync(int id, CancellationToken cancellationToken = default)
{
return await _repository.DeleteAsync(id, cancellationToken);
}
}