- Project Overview
- Architecture Pattern
- Project Structure
- Layer Responsibilities
- Dependency Flow
- Workflow Guidelines
- Development Standards
- Getting Started
This project follows Clean Architecture principles combined with Domain-Driven Design (DDD) patterns to create a maintainable, testable, and scalable .NET Web API.
The solution implements the Onion Architecture (Clean Architecture) with these core principles:
- Dependency Inversion: Dependencies point inward toward the domain
- Separation of Concerns: Each layer has distinct responsibilities
- Framework Independence: Business logic is isolated from external frameworks
- Testability: Easy to unit test business logic in isolation
Farrahni/
βββ src/
β βββ Farrahni.API/ # π Presentation Layer
β βββ Farrahni.Application/ # π Application Layer
β βββ Farrahni.Domain/ # ποΈ Domain Layer (Core)
β βββ Farrahni.Infrastructure/ # π§ Infrastructure Layer
βββ tests/
β βββ Farrahni.UnitTests/ # Unit Tests
β βββ Farrahni.IntegrationTests/ # Integration Tests
βββ docs/ # Documentation
βββ docker-compose.yml # Docker configuration
βββ Farrahni.sln # Solution file
βββ README.md # This file
Purpose: Handles HTTP requests and responses, user interface concerns.
Farrahni.API/
βββ Controllers/ # API endpoints and HTTP logic
β βββ ProductsController.cs
β βββ UsersController.cs
β βββ OrdersController.cs
βββ Middlewares/ # Custom middleware components
β βββ GlobalExceptionHandlerMiddleware.cs
β βββ RequestLoggingMiddleware.cs
β βββ AuthenticationMiddleware.cs
βββ Filters/ # Action filters and attributes
β βββ ValidationFilter.cs
β βββ AuthorizeFilter.cs
β βββ CacheFilter.cs
βββ Extensions/ # Service configuration extensions
β βββ PresentationServiceExtensions.cs
β βββ SwaggerExtensions.cs
βββ Program.cs # Application entry point
βββ appsettings.json # Configuration files
Responsibilities:
- β HTTP request/response handling
- β Input validation and model binding
- β Authentication and authorization
- β API documentation (Swagger)
- β Dependency injection configuration
- β Business logic (belongs in Application/Domain)
- β Data access (belongs in Infrastructure)
Purpose: Orchestrates business workflows and use cases.
Farrahni.Application/
βββ DTOs/ # Data Transfer Objects
β βββ ProductDto.cs
β βββ CreateProductDto.cs
β βββ UpdateProductDto.cs
βββ Interfaces/ # Service contracts
β βββ IProductService.cs
β βββ IUserService.cs
β βββ IEmailService.cs
βββ Services/ # Application services
β βββ ProductService.cs
β βββ UserService.cs
β βββ OrderService.cs
βββ Validators/ # Input validation rules
β βββ CreateProductValidator.cs
β βββ UpdateUserValidator.cs
βββ Mappings/ # AutoMapper profiles
β βββ ProductProfile.cs
β βββ UserProfile.cs
βββ Common/ # Shared application logic
β βββ Models/
β β βββ ApiResponse.cs
β β βββ PaginatedResult.cs
β βββ Exceptions/
β β βββ NotFoundException.cs
β β βββ ValidationException.cs
β βββ Behaviors/ # MediatR behaviors
β βββ ValidationBehavior.cs
β βββ LoggingBehavior.cs
βββ ApplicationServiceExtensions.cs
Responsibilities:
- β Use case implementation
- β DTO mapping and transformation
- β Input validation
- β Business workflow orchestration
- β Cross-cutting concerns (logging, caching)
- β UI concerns (belongs in Presentation)
- β Data persistence logic (belongs in Infrastructure)
- β Core business rules (belongs in Domain)
Purpose: Contains pure business logic and domain models.
Farrahni.Domain/
βββ Entities/ # Domain entities (business objects)
β βββ Product.cs
β βββ User.cs
β βββ Order.cs
β βββ Category.cs
βββ ValueObjects/ # Immutable domain values
β βββ Money.cs
β βββ Address.cs
β βββ Email.cs
βββ Interfaces/ # Domain contracts
β βββ IRepository.cs
β βββ IUnitOfWork.cs
β βββ IDomainService.cs
βββ Events/ # Domain events
β βββ ProductCreatedEvent.cs
β βββ OrderPlacedEvent.cs
βββ Services/ # Domain services
β βββ PricingService.cs
β βββ InventoryService.cs
βββ Enums/ # Domain enumerations
β βββ OrderStatus.cs
β βββ UserRole.cs
βββ Common/ # Shared domain logic
βββ BaseEntity.cs
βββ IAggregateRoot.cs
βββ IDomainEvent.cs
Responsibilities:
- β Core business entities and rules
- β Domain services and logic
- β Value objects and enumerations
- β Domain events and behaviors
- β Repository interfaces
- β External dependencies (UI, Database, APIs)
- β Framework-specific code
Purpose: Handles external concerns and technical implementations.
Farrahni.Infrastructure/
βββ Data/ # Database-related implementations
β βββ Context/
β β βββ ApplicationDbContext.cs
β βββ Repositories/
β β βββ Repository.cs
β β βββ ProductRepository.cs
β β βββ UserRepository.cs
β βββ Configurations/ # Entity Framework configurations
β β βββ ProductConfiguration.cs
β β βββ UserConfiguration.cs
β βββ Migrations/ # EF Core migrations
βββ Services/ # External service implementations
β βββ EmailService.cs
β βββ FileStorageService.cs
β βββ CachingService.cs
βββ External/ # Third-party integrations
β βββ PaymentService.cs
β βββ GoogleMapsService.cs
β βββ AzureBlobStorage.cs
βββ InfrastructureServiceExtensions.cs
Responsibilities:
- β Database access and ORM configuration
- β External API integrations
- β File system operations
- β Email/SMS services
- β Caching implementations
- β Third-party service integrations
- β Business logic (belongs in Domain/Application)
βββββββββββββββββββββββββββββββββββββββββββ
β Presentation β
β (Farrahni.API) β
βββββββββββ¬ββββββββββββββββββββ¬ββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β Application β β Infrastructure β
β (Farrahni.App) β β (Farrahni.Infra)β
βββββββββββ¬ββββββββ βββββββββββ¬ββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β Domain β
β (Farrahni.Domain) β
β β CORE β β
βββββββββββββββββββββββββββββββββββββββββββ
Dependency Rules:
- β API β Application + Infrastructure
- β Application β Domain only
- β Infrastructure β Domain + Application
- β Domain β No external dependencies
- β Domain should never depend on outer layers
// Farrahni.Domain/Entities/Product.cs
public class Product : BaseEntity
{
private Product() { } // EF Constructor
public Product(string name, decimal price)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Name is required");
if (price <= 0)
throw new ArgumentException("Price must be positive");
Name = name;
Price = price;
}
public string Name { get; private set; }
public decimal Price { get; private set; }
public void UpdatePrice(decimal newPrice)
{
if (newPrice <= 0)
throw new ArgumentException("Price must be positive");
Price = newPrice;
}
}// Farrahni.Application/DTOs/ProductDto.cs
public class ProductDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// Farrahni.Application/Interfaces/IProductService.cs
public interface IProductService
{
Task<ApiResponse<ProductDto>> GetByIdAsync(Guid id);
Task<ApiResponse<ProductDto>> CreateAsync(CreateProductDto dto);
}
// Farrahni.Application/Services/ProductService.cs
public class ProductService : IProductService
{
// Implementation using repository and mapping
}// Farrahni.Infrastructure/Data/Configurations/ProductConfiguration.cs
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.Property(p => p.Name).HasMaxLength(200);
builder.Property(p => p.Price).HasPrecision(18, 2);
}
}// Farrahni.API/Controllers/ProductsController.cs
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
[HttpGet("{id}")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await _productService.GetByIdAsync(id);
return result.Success ? Ok(result) : NotFound(result);
}
}// Update ApplicationServiceExtensions.cs
services.AddScoped<IProductService, ProductService>();
// Update InfrastructureServiceExtensions.cs
services.AddScoped<IProductRepository, ProductRepository>();- Domain First: Start with entities and business rules
- Test Early: Write unit tests for domain logic
- Application Layer: Create DTOs, services, and validators
- Infrastructure: Implement repositories and external services
- API Layer: Create controllers and configure endpoints
- Integration: Wire up dependency injection
- Test: Integration and end-to-end testing
- Entities:
Product,User,Order - DTOs:
ProductDto,CreateProductDto,UpdateProductDto - Services:
IProductService,ProductService - Repositories:
IProductRepository,ProductRepository - Controllers:
ProductsController(plural)
β
One class per file
β
Namespace matches folder structure
β
Group related functionality together
β
Separate interfaces from implementations
// β
Use interfaces for loose coupling
services.AddScoped<IProductService, ProductService>();
// β Don't register concrete classes directly
services.AddScoped<ProductService>();// β
Use custom exceptions with meaningful messages
throw new NotFoundException($"Product with ID {id} not found");
// β
Use ApiResponse for consistent API responses
return ApiResponse<ProductDto>.FailureResult("Product not found");- .NET 8.0 SDK
- SQL Server or PostgreSQL
- Docker (optional)
-
Clone the repository
git clone <repository-url> cd Farrahni
-
Restore dependencies
dotnet restore
-
Update connection string
// appsettings.json { "ConnectionStrings": { "DefaultConnection": "your-connection-string-here" } }
-
Run migrations
dotnet ef database update --project src/Farrahni.API
-
Run the application
dotnet run --project src/Farrahni.API
-
Access Swagger UI
https://localhost:7xxx/swagger
-
Build and run with Docker Compose
docker-compose up --build
-
Access the application
- API: http://localhost:8080/swagger
- Database: localhost:5432 (PostgreSQL)
- pgAdmin: http://localhost:8081
Remember: The goal is to keep business logic pure and independent of external frameworks, making the application maintainable, testable, and adaptable to change.