The petfolio-service repository¶
The PetFolio Service is a .NET 9 backend API built with Clean Architecture and CQRS pattern for managing pet portfolios. The architecture emphasises separation of concerns, testability, and maintainability through strict layering and dependency rules.
Technology Stack¶
| Technology | Category | Purpose |
|---|---|---|
| .NET 9 | Framework | Modern, high-performance web API framework |
| ASP.NET Core | Web Framework | RESTful API hosting and HTTP layer |
| MySQL | Database | Relational data storage |
| Entity Framework | ORM | Database access and migrations |
| MediatR | Mediator | CQRS implementation and request handling |
| FluentValidation | Validation | Input validation with fluent API |
| AutoMapper | Mapping | DTO to entity mapping |
| xUnit | Testing | Test framework for all test types |
| Shouldly | Assertions | Fluent assertion library |
| NSubstitute | Mocking | Test doubles for dependencies |
| Testcontainers | Integration Tests | Docker-based test database provisioning |
| Bogus | Test Data | Fake data generation for tests |
| Swashbuckle | Documentation | OpenAPI/Swagger documentation |
Design Patterns¶
File Naming Conventions¶
| File Type | Pattern | Example |
|---|---|---|
| Entity | PascalCase | Animal.cs |
| Value Object | PascalCase | Name.cs, Weight.cs |
| Command | [Verb][Entity]Command |
CreateAnimalCommand.cs |
| Query | Get[Entity(s)]Query |
GetAnimalQuery.cs |
| Handler | [Command/Query]Handler |
CreateAnimalCommandHandler.cs |
| Validator | [Command/Query]Validator |
CreateAnimalCommandValidator.cs |
| DTO | [Entity]Dto |
AnimalDto.cs |
| Repository | I[Entity]Repository |
IAnimalRepository.cs |
| Error Definitions | [Entity]Errors |
AnimalErrors.cs |
| Test | [ClassUnderTest]Tests |
AnimalTests.cs |
Rules:
- One class per file, file name matches class name
- Tests mirror source structure exactly
- Use cases grouped under feature folders
Clean Architecture Layers¶
Dependencies flow inward toward the Domain layer, ensuring the core business logic has no external dependencies:
graph TD
A[API Layer] --> B[Application Layer]
A --> C[Infrastructure Layer]
B --> D[Domain Layer]
C --> D
style D fill:#e8f5e9
style B fill:#fff3e0
style C fill:#e3f2fd
style A fill:#fce4ec
subgraph "Petfolio.Service.Api"
A
end
subgraph "Petfolio.Service.Application"
B
end
subgraph "Petfolio.Service.Infrastructure"
C
end
subgraph "Petfolio.Service.Domain"
D
end
Layer Responsibilities
- Domain - Core business logic, entities, value objects, repository interfaces (NO dependencies)
- Application - Use cases (Commands/Queries), handlers, validators, DTOs (depends on Domain only)
- Infrastructure - External concerns like database, EF Core configurations (depends on Domain)
- API - HTTP endpoints, controllers, middleware (depends on Application and Infrastructure)
CQRS Pattern¶
Commands and Queries are strictly separated for clarity and scalability:
graph LR
A[Client Request] --> B{Command or Query?}
B -->|Write| C[Command Handler]
B -->|Read| D[Query Handler]
C --> E[Repository Write]
D --> F[Repository Read]
E --> G[Database]
F --> G
style C fill:#ffcdd2
style D fill:#c8e6c9
Commands and Queries
- Commands: Write operations (Create, Update, Delete) returning
Result<T> - Queries: Read operations returning
Result<T> - All requests routed through MediatR pipeline with automatic validation
Result Pattern¶
Error handling uses the Result pattern to avoid exceptions in the happy path:
- Domain layer: Throws exceptions for validation and business rule violations
- Application layer: Catches domain exceptions and converts to
Result<T>orResult<T, Error> - API layer: Converts
Result<T>to appropriate HTTP status codes
Value Objects¶
Immutable types enforcing business invariants:
- Always
sealed recordwithinitaccessors - Private parameterless constructor for EF Core (marked
[ExcludeFromCodeCoverage]) - Static
Create()factory method throwingArgumentExceptionon validation failure
Two-Layer Validation¶
- Domain Validation: Business invariants enforced in value objects and entities (throws exceptions)
- Application Validation: FluentValidation validators run automatically via
ValidationBehaviourpipeline
Security¶
=== Input Validation
- FluentValidation: Automatic validation pipeline prevents invalid data reaching handlers
- Domain Validation: Business rules enforced at the domain boundary
- DTO Pattern: External data never directly mapped to entities
Database Security¶
- Parameterised Queries: EF Core prevents SQL injection by default
- Connection String Management: Stored in appsettings (development) or environment variables (production)
- Migrations: Version-controlled schema changes
API Security¶
- HTTPS Only: Enforced in production
- CORS Configuration: Controlled cross-origin access
- Swagger: Disabled in production builds
Performance¶
Database Optimisations¶
- Async/Await: All database operations are asynchronous
- Query Efficiency: Repository pattern allows optimised queries
- Connection Pooling: Built-in ADO.NET connection pooling
Build Performance¶
- Centrally Managed Packages:
Directory.Packages.propsensures consistent versions - Incremental Builds: Only recompile changed projects
- Minimal API Surface: Thin controllers delegate to MediatR
Test Performance¶
- Shared Test Container: Integration tests reuse single MySQL container
- Parallel Execution: xUnit runs tests in parallel where possible
- Fast Feedback: Full test suite runs in ~1.5 seconds