Skip to content

Refactor solution to adopt a Clean Architecture-inspired structureΒ #266

@nanotaboada

Description

@nanotaboada

Description

The current folder structure follows a traditional layered architecture (Controllers β†’ Services β†’ Repositories β†’ Data) which, while functional, has some architectural limitations:

src/Dotnet.Samples.AspNetCore.WebApi/
β”œβ”€β”€ Controllers/                                # HTTP/API concerns
β”œβ”€β”€ Services/                                   # Business logic + orchestration
β”œβ”€β”€ Repositories/                               # Data access abstractions & implementations
β”œβ”€β”€ Data/                                       # EF Core DbContext
β”œβ”€β”€ Models/                                     # Mix of entities + DTOs
β”œβ”€β”€ Enums/                                      # Domain enums
β”œβ”€β”€ Validators/                                 # Input validation
β”œβ”€β”€ Mappings/                                   # Object mapping
β”œβ”€β”€ Utilities/                                  # Various utilities
β”œβ”€β”€ Configurations/                             # Infrastructure configuration
└── Extensions/                                 # Service registration
  • Business logic is mixed across multiple layers
  • Dependencies flow in all directions, making testing difficult
  • Tight coupling between infrastructure and business logic
  • Limited ability to swap implementations (database, external services)

This issue proposes migrating to Clean Architecture-inspired folder structure to improve maintainability, testability, and adherence to SOLID principles.

Proposed Solution

Restructure the project into Clean Architecture layers with proper dependency inversion:

src/
β”œβ”€β”€ Dotnet.Samples.AspNetCore.WebApi.Domain/
β”‚   β”œβ”€β”€ Entities/
β”‚   β”‚   └── Player.cs                            # Pure domain entity
β”‚   └── Enums/
β”‚       β”œβ”€β”€ Enumeration.cs
β”‚       └── Position.cs
β”‚
β”œβ”€β”€ Dotnet.Samples.AspNetCore.WebApi.Core/
β”‚   β”œβ”€β”€ DTOs/
β”‚   β”‚   β”œβ”€β”€ PlayerRequestModel.cs                # Input DTOs
β”‚   β”‚   └── PlayerResponseModel.cs               # Output DTOs
β”‚   β”œβ”€β”€ Interfaces/
β”‚   β”‚   β”œβ”€β”€ IPlayerRepository.cs                 # Repository contracts
β”‚   β”‚   β”œβ”€β”€ IPlayerService.cs                    # Service contracts
β”‚   β”‚   └── IRepository.cs                       # Generic contracts
β”‚   β”œβ”€β”€ Services/
β”‚   β”‚   └── PlayerService.cs                     # Business logic orchestration
β”‚   β”œβ”€β”€ Validators/
β”‚   β”‚   └── PlayerRequestModelValidator.cs
β”‚   β”œβ”€β”€ Mappings/
β”‚   β”‚   └── PlayerMappingProfile.cs
β”‚   └── Exceptions/                              # Custom business exceptions
β”‚
β”œβ”€β”€ Dotnet.Samples.AspNetCore.WebApi.Infrastructure/
β”‚   β”œβ”€β”€ Data/
β”‚   β”‚   └── PlayerDbContext.cs
β”‚   β”œβ”€β”€ Repositories/
β”‚   β”‚   β”œβ”€β”€ Repository.cs                        # Generic implementation
β”‚   β”‚   └── PlayerRepository.cs                  # Specific implementation
β”‚   β”œβ”€β”€ Migrations/                              # EF Core migrations
β”‚   └── Extensions/                              # Infrastructure-specific extensions
β”‚
└── Dotnet.Samples.AspNetCore.WebApi.Api/
    β”œβ”€β”€ Controllers/
    β”‚   └── PlayerController.cs
    β”œβ”€β”€ Configurations/
    β”œβ”€β”€ Extensions/
    β”‚   └── ServiceCollectionExtensions.cs       # DI configuration
    β”œβ”€β”€ Utilities/                               # Presentation utilities
    └── Program.cs                               # Application entry point

Dependency Flow

Api β†’ Core β†’ Domain
Infrastructure β†’ Core β†’ Domain

Suggested Approach

  1. Create Domain project (Domain/)

    • Move Models/Player.cs β†’ Domain/Entities/Player.cs
    • Move Enums/ β†’ Domain/Enums/
    • No external dependencies
  2. Create Core project (Core/)

    • Move Services/ β†’ Core/Services/
    • Move Models/PlayerRequestModel.cs, PlayerResponseModel.cs β†’ Core/DTOs/
    • Move Repositories/IPlayerRepository.cs, IRepository.cs β†’ Core/Interfaces/
    • Move Validators/ β†’ Core/Validators/
    • Move Mappings/ β†’ Core/Mappings/
    • Reference: Domain only
  3. Create Infrastructure project (Infrastructure/)

    • Move Repositories/Repository.cs, PlayerRepository.cs β†’ Infrastructure/Repositories/
    • Move Data/ β†’ Infrastructure/Data/
    • Move Migrations/ β†’ Infrastructure/Migrations/
    • Reference: Domain, Core, EF Core packages
  4. Refactor API project (Api/)

    • Rename current project from WebApi to Api
    • Keep Controllers/, Program.cs
    • Update Extensions/ServiceCollectionExtensions.cs for DI configuration
    • Reference: Core only (not Infrastructure directly)
  5. Update Solution and Tests

    • Update .sln file with new project references
    • Update test projects to reference appropriate layers
    • Ensure all tests pass after migration

Acceptance Criteria

  • Domain project contains only pure domain logic with no external dependencies
  • Core project contains business logic and defines infrastructure contracts
  • Infrastructure project implements Core interfaces and handles data access
  • Api project contains only HTTP/presentation concerns
  • Dependency flow follows Clean Architecture principles (inward dependencies only)
  • All existing tests pass after migration
  • No circular dependencies between projects
  • Build and deployment work without issues
  • Docker compose still functions correctly
  • Database migrations continue to work
  • API functionality remains unchanged (same endpoints, same behavior)

References

Metadata

Metadata

Assignees

Labels

.NETPull requests that update .NET codeenhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions