From 17fdb6ed5166b9d566cb070de6fe7f7ebb47f74c Mon Sep 17 00:00:00 2001 From: OlgunR Date: Mon, 19 Jan 2026 16:42:48 +0100 Subject: [PATCH] Move repository interfaces to Application layer Refactored IRepository and ICatalogRepository to reside in the DbFirst.Application layer instead of Domain. Updated namespaces, using statements, and all references in services and handlers. Adjusted csproj dependencies to reflect the new structure. Updated comments to clarify Clean Architecture rationale and improved separation of concerns. --- DbFirst.API/Program.cs | 2 +- .../Catalogs/CatalogService.cs | 30 ++------------ .../Catalogs/Commands/CreateCatalogHandler.cs | 2 +- .../Catalogs/Commands/DeleteCatalogHandler.cs | 2 +- .../Catalogs/Commands/UpdateCatalogHandler.cs | 2 +- .../Catalogs/Queries/GetAllCatalogsHandler.cs | 2 +- .../Catalogs/Queries/GetCatalogByIdHandler.cs | 2 +- .../Repositories/ICatalogRepository.cs | 10 +++++ .../Repositories/IRepository.cs | 2 +- DbFirst.Domain/DbFirst.Domain.csproj | 4 ++ .../Repositories/ICatalogRepository.cs | 41 ------------------- .../DbFirst.Infrastructure.csproj | 1 + .../Repositories/CatalogRepository.cs | 2 +- 13 files changed, 26 insertions(+), 76 deletions(-) create mode 100644 DbFirst.Application/Repositories/ICatalogRepository.cs rename {DbFirst.Domain => DbFirst.Application}/Repositories/IRepository.cs (91%) delete mode 100644 DbFirst.Domain/Repositories/ICatalogRepository.cs diff --git a/DbFirst.API/Program.cs b/DbFirst.API/Program.cs index d1e3c7e..3cc1af7 100644 --- a/DbFirst.API/Program.cs +++ b/DbFirst.API/Program.cs @@ -1,6 +1,6 @@ using DbFirst.API.Middleware; using DbFirst.Application; -using DbFirst.Domain.Repositories; +using DbFirst.Application.Repositories; using DbFirst.Infrastructure; using DbFirst.Infrastructure.Repositories; diff --git a/DbFirst.Application/Catalogs/CatalogService.cs b/DbFirst.Application/Catalogs/CatalogService.cs index 74bd421..74e0a06 100644 --- a/DbFirst.Application/Catalogs/CatalogService.cs +++ b/DbFirst.Application/Catalogs/CatalogService.cs @@ -1,38 +1,14 @@ using AutoMapper; -using DbFirst.Domain.Repositories; +using DbFirst.Application.Repositories; using DbFirst.Domain.Entities; using DbFirst.Domain; 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 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 (TR): kod tekrar?n? azaltmak için generic bir servis/basit CRUD altyap?s? ekleyin //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. - */ - +//TODO (TR): CQRS desenini MediatR ile uygulay?n public class CatalogService : ICatalogService { private readonly ICatalogRepository _repository; diff --git a/DbFirst.Application/Catalogs/Commands/CreateCatalogHandler.cs b/DbFirst.Application/Catalogs/Commands/CreateCatalogHandler.cs index 3be85da..ccef126 100644 --- a/DbFirst.Application/Catalogs/Commands/CreateCatalogHandler.cs +++ b/DbFirst.Application/Catalogs/Commands/CreateCatalogHandler.cs @@ -1,6 +1,6 @@ using AutoMapper; +using DbFirst.Application.Repositories; using DbFirst.Domain.Entities; -using DbFirst.Domain.Repositories; using MediatR; namespace DbFirst.Application.Catalogs.Commands; diff --git a/DbFirst.Application/Catalogs/Commands/DeleteCatalogHandler.cs b/DbFirst.Application/Catalogs/Commands/DeleteCatalogHandler.cs index 8c5e033..8356117 100644 --- a/DbFirst.Application/Catalogs/Commands/DeleteCatalogHandler.cs +++ b/DbFirst.Application/Catalogs/Commands/DeleteCatalogHandler.cs @@ -1,4 +1,4 @@ -using DbFirst.Domain.Repositories; +using DbFirst.Application.Repositories; using MediatR; namespace DbFirst.Application.Catalogs.Commands; diff --git a/DbFirst.Application/Catalogs/Commands/UpdateCatalogHandler.cs b/DbFirst.Application/Catalogs/Commands/UpdateCatalogHandler.cs index e236981..9e25ef5 100644 --- a/DbFirst.Application/Catalogs/Commands/UpdateCatalogHandler.cs +++ b/DbFirst.Application/Catalogs/Commands/UpdateCatalogHandler.cs @@ -1,6 +1,6 @@ using AutoMapper; +using DbFirst.Application.Repositories; using DbFirst.Domain.Entities; -using DbFirst.Domain.Repositories; using DbFirst.Domain; using MediatR; diff --git a/DbFirst.Application/Catalogs/Queries/GetAllCatalogsHandler.cs b/DbFirst.Application/Catalogs/Queries/GetAllCatalogsHandler.cs index b824f66..e1d952b 100644 --- a/DbFirst.Application/Catalogs/Queries/GetAllCatalogsHandler.cs +++ b/DbFirst.Application/Catalogs/Queries/GetAllCatalogsHandler.cs @@ -1,5 +1,5 @@ using AutoMapper; -using DbFirst.Domain.Repositories; +using DbFirst.Application.Repositories; using MediatR; namespace DbFirst.Application.Catalogs.Queries; diff --git a/DbFirst.Application/Catalogs/Queries/GetCatalogByIdHandler.cs b/DbFirst.Application/Catalogs/Queries/GetCatalogByIdHandler.cs index f61aed3..b463cf2 100644 --- a/DbFirst.Application/Catalogs/Queries/GetCatalogByIdHandler.cs +++ b/DbFirst.Application/Catalogs/Queries/GetCatalogByIdHandler.cs @@ -1,5 +1,5 @@ using AutoMapper; -using DbFirst.Domain.Repositories; +using DbFirst.Application.Repositories; using MediatR; namespace DbFirst.Application.Catalogs.Queries; diff --git a/DbFirst.Application/Repositories/ICatalogRepository.cs b/DbFirst.Application/Repositories/ICatalogRepository.cs new file mode 100644 index 0000000..69dd856 --- /dev/null +++ b/DbFirst.Application/Repositories/ICatalogRepository.cs @@ -0,0 +1,10 @@ +using DbFirst.Domain; +using DbFirst.Domain.Entities; + +namespace DbFirst.Application.Repositories; + +public interface ICatalogRepository : IRepository +{ + Task GetByTitleAsync(string title, CancellationToken cancellationToken = default); + Task UpdateAsync(int id, VwmyCatalog catalog, CatalogUpdateProcedure procedure, CancellationToken cancellationToken = default); +} diff --git a/DbFirst.Domain/Repositories/IRepository.cs b/DbFirst.Application/Repositories/IRepository.cs similarity index 91% rename from DbFirst.Domain/Repositories/IRepository.cs rename to DbFirst.Application/Repositories/IRepository.cs index 0064910..a44f313 100644 --- a/DbFirst.Domain/Repositories/IRepository.cs +++ b/DbFirst.Application/Repositories/IRepository.cs @@ -1,4 +1,4 @@ -namespace DbFirst.Domain.Repositories; +namespace DbFirst.Application.Repositories; public interface IRepository { diff --git a/DbFirst.Domain/DbFirst.Domain.csproj b/DbFirst.Domain/DbFirst.Domain.csproj index fa71b7a..bbf0ab3 100644 --- a/DbFirst.Domain/DbFirst.Domain.csproj +++ b/DbFirst.Domain/DbFirst.Domain.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/DbFirst.Domain/Repositories/ICatalogRepository.cs b/DbFirst.Domain/Repositories/ICatalogRepository.cs deleted file mode 100644 index 2534739..0000000 --- a/DbFirst.Domain/Repositories/ICatalogRepository.cs +++ /dev/null @@ -1,41 +0,0 @@ -using DbFirst.Domain.Entities; - -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 : IRepository -{ - Task GetByTitleAsync(string title, CancellationToken cancellationToken = default); - Task UpdateAsync(int id, VwmyCatalog catalog, CatalogUpdateProcedure procedure, CancellationToken cancellationToken = default); -} diff --git a/DbFirst.Infrastructure/DbFirst.Infrastructure.csproj b/DbFirst.Infrastructure/DbFirst.Infrastructure.csproj index 5a8052a..83b7a53 100644 --- a/DbFirst.Infrastructure/DbFirst.Infrastructure.csproj +++ b/DbFirst.Infrastructure/DbFirst.Infrastructure.csproj @@ -21,6 +21,7 @@ + diff --git a/DbFirst.Infrastructure/Repositories/CatalogRepository.cs b/DbFirst.Infrastructure/Repositories/CatalogRepository.cs index d15e5ff..7ee6102 100644 --- a/DbFirst.Infrastructure/Repositories/CatalogRepository.cs +++ b/DbFirst.Infrastructure/Repositories/CatalogRepository.cs @@ -1,6 +1,6 @@ using DbFirst.Domain; using DbFirst.Domain.Entities; -using DbFirst.Domain.Repositories; +using DbFirst.Application.Repositories; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using System.Data;