diff --git a/DbFirst.API/Program.cs b/DbFirst.API/Program.cs index 12ce93e..58145d7 100644 --- a/DbFirst.API/Program.cs +++ b/DbFirst.API/Program.cs @@ -14,20 +14,22 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // TODO: allow listed origins configured in appsettings.json +// In any case, dont let them to free to use without cors. if there is no origin specified, block all. +// In development you can keep it easy. builder.Services.AddCors(options => -{ - var origins = builder.Configuration.GetSection("Cors:AllowedOrigins").Get() ?? Array.Empty(); +{ options.AddDefaultPolicy(policy => { - if (origins.Length > 0) + if(builder.Environment.IsDevelopment()) { - policy.WithOrigins(origins) + policy.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); } else { - policy.AllowAnyOrigin() + var origins = builder.Configuration.GetSection("Cors:AllowedOrigins").Get() ?? []; + policy.WithOrigins(origins) .AllowAnyHeader() .AllowAnyMethod(); } diff --git a/DbFirst.Application/Catalogs/CatalogService.cs b/DbFirst.Application/Catalogs/CatalogService.cs index 5d0d403..74bd421 100644 --- a/DbFirst.Application/Catalogs/CatalogService.cs +++ b/DbFirst.Application/Catalogs/CatalogService.cs @@ -6,9 +6,33 @@ using DbFirst.Domain; namespace DbFirst.Application.Catalogs; //TODO: create generic service to reduce code duplication -//TODO (TR): kod tekrar?n? azaltmak için generic bir servis/basit CRUD altyap?s? ekleyin + +/* 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 doesn’t fit cleanly because operations aren’t 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 -//TODO (TR): CQRS desenini MediatR ile uygulay?n + +/* 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; diff --git a/DbFirst.Domain/Repositories/ICatalogRepository.cs b/DbFirst.Domain/Repositories/ICatalogRepository.cs index a9444d9..8cd3b8b 100644 --- a/DbFirst.Domain/Repositories/ICatalogRepository.cs +++ b/DbFirst.Domain/Repositories/ICatalogRepository.cs @@ -3,6 +3,38 @@ using DbFirst.Domain; namespace DbFirst.Domain.Repositories; +// TODO: instead of creating interface per entity, consider using generic repository pattern (eg. IRepository) to reduce code duplication. + +/* Copilot's Response: + +A generic Repository isn’t really worthwhile here: + • Reads from the view are generic, but inserts/updates/deletes go through stored procedures with special parameters/output GUIDs.You’d need lots of exceptions/overrides—little gain. + • Operations aren’t symmetric (separate procs for insert/update/delete with output handling and reload), so a one-size-fits-all CRUD pattern doesn’t fit well. + • Better to keep the specialized repo.If you want reuse, extract small helpers (e.g., for proc calls/output parameters/reload) instead of forcing a generic repository. */ + + +/* Hakan's Response: + * I think it still makes sense. An execute SQL method can be added to repository. The relevant SQL can be executed in the application layer. + * Because the application layer is no longer dependent on a method like CreateAsync. It is now directly dependent on that procedure. + */ + +// TODO: move to application layer as a part of clean architecture + +/* Copilot's Response: + + • Common: Keep the interface in the Domain layer, implementation in Infrastructure. Domain defines the data access contract, Infrastructure provides the implementation. + That matches typical DDD/Clean Architecture. + • Alternative: Move the interface to the Application layer. Then only Application depends on Infrastructure, Domain stays isolated from infra contracts. + Pro: Domain contains only models/rules. + Con: If Domain logic needs repos, it would depend on Application, weakening the layer separation. + Recommendation: Keep the interface in Domain. It’s conventional, clean, and avoids a Domain -> Application dependency. Implementation stays in Infrastructure. */ + +/* Hakan's Response: + * I don't think there's any point in keeping repository interfaces in the domain layer. These interfaces will be used in the application layer, not the domain layer. + * Furthermore, this is how they are used in the most popular clean architecture pattern. + * See: https://github.com/jasontaylordev/CleanArchitecture/blob/main/src/Infrastructure/Identity/IdentityService.cs + */ + public interface ICatalogRepository { Task> GetAllAsync(CancellationToken cancellationToken = default);