A production-grade REST API for task management built with .NET 8, following Onion Architecture and Domain-Driven Design (DDD) principles.
This project demonstrates best practices for building maintainable, testable, and scalable enterprise applications using:
- Onion Architecture (strict dependency inversion)
- Domain-Driven Design (rich domain models, value objects, aggregates)
- CQRS Pattern (manual implementation without MediatR)
- Result Pattern (explicit error handling)
- Repository Pattern with Unit of Work
- EF Core as a state manager (not data access abstraction)
- RESTful API design with proper HTTP semantics
TaskFlow/
├── TaskFlow.Domain # Core business logic (zero dependencies)
├── TaskFlow.Application # Use cases, handlers (depends on Domain only)
├── TaskFlow.Infrastructure # EF Core, repositories (depends on Domain only)
└── TaskFlow.API # HTTP layer, controllers (depends on all layers)
API → Application → Domain
↘ Infrastructure ↗
Key Rule: Dependencies point inward. Inner layers never reference outer layers.
- Entities: Task, Project, TeamMember, Comment
- Value Objects: TaskId, ProjectId, TeamMemberId, CommentId, Priority, TaskStatus, DateRange, Tag
- Aggregates: Task (root), Comment (child)
- Domain Events: TaskAssigned, TaskCompleted, TaskStatusChanged
- Business Rules: State machine for task status transitions, invariant validation
POST /api/projects— Create projectGET /api/projects— List all projectsGET /api/projects/{id}— Get project by IDPUT /api/projects/{id}— Update projectDELETE /api/projects/{id}— Delete project
POST /api/projects/{projectId}/tasks— Create task in projectGET /api/projects/{projectId}/tasks— Get all tasks in projectGET /api/tasks?status=...&assigneeId=...— Filter tasks (query parameters)GET /api/tasks/{id}— Get task by IDPUT /api/tasks/{id}— Update task detailsDELETE /api/tasks/{id}— Delete taskPUT /api/tasks/{id}/assign— Assign task to team memberPUT /api/tasks/{id}/status— Change task statusPOST /api/tasks/{id}/comments— Add comment to taskPUT /api/tasks/{id}/tags— Update task tags
POST /api/team-members— Create team memberGET /api/team-members— List all team membersGET /api/team-members/{id}— Get team member by IDPUT /api/team-members/{id}— Update team memberDELETE /api/team-members/{id}— Delete team member
| Layer | Technologies |
|---|---|
| API | ASP.NET Core 8, Swagger (OpenAPI) |
| Application | CQRS (manual), Result Pattern |
| Infrastructure | EF Core 8, SQL Server, Fluent API |
| Domain | C# 12, Record types, Value objects |
| DevOps | Docker (SQL Server), Git |
- Clone the repository:
git clone https://github.com/YOUR_USERNAME/TaskFlow.git
cd TaskFlow- Start SQL Server (Docker):
docker-compose up -d- Restore dependencies:
dotnet restore- Run migrations:
cd src/TaskFlow.Infrastructure
dotnet ef database update --startup-project ../TaskFlow.API
cd ../..- Run the API:
dotnet run --project src/TaskFlow.API- Open Swagger UI:
http://localhost:5000 (or the port shown in terminal)
End-to-end testing via Swagger UI:
- Create a project
- Create a team member
- Create a task in the project
- Assign the task to the team member
- Change task status through valid transitions
- Add comments and tags
- Test error cases (invalid status transitions, duplicate emails, etc.)
- ✅ Entities with identity and lifecycle
- ✅ Value Objects (immutable, equality by value)
- ✅ Aggregates (TaskEntity owns Comments)
- ✅ Domain Events (TaskAssigned, TaskCompleted)
- ✅ Domain Exceptions (InvalidStatusTransitionException)
- ✅ Dependency Inversion (interfaces in Domain, implementations in Infrastructure)
- ✅ Separation of Concerns (each layer has single responsibility)
- ✅ Testability (domain logic has zero dependencies)
- ✅ Commands (state changes, return Result)
- ✅ Queries (read-only, return Result)
- ✅ Handlers (one per use case, explicit dependencies)
- ✅ Fluent API (zero data annotations in domain)
- ✅ Value Conversions (value objects ↔ primitives)
- ✅ Owned Entities (DateRange, Tags)
- ✅ Backing Fields (encapsulation, read-only properties)
- ✅ Interceptors (automatic CreatedAt/UpdatedAt)
| Metric | Count |
|---|---|
| Total Endpoints | 20 |
| Domain Entities | 4 (Task, Project, TeamMember, Comment) |
| Value Objects | 9 (IDs, enums, DateRange, Tag) |
| Handlers | 20 (13 commands, 7 queries) |
| Database Tables | 5 (Projects, Tasks, TeamMembers, Comments, TaskTags) |
| Lines of Code | ~3,500 |
- Strict Layering — No shortcuts, proper dependency management
- Rich Domain Models — Business logic lives in entities, not services
- Value Objects — Type safety, immutability, validation
- Result Pattern — Explicit success/failure without exceptions
- CQRS — Separate read and write concerns
- Repository Pattern — Data access abstraction
- Unit of Work — Transaction coordination
- Global Exception Handling — Centralized error mapping
- Fluent API — Clean domain layer, configuration in Infrastructure
- Aggregate Boundaries — Comment can only be created through Task
- JWT Authentication & Authorization
- Unit Testing (xUnit + FluentAssertions)
- Integration Testing
- Soft Delete (IsDeleted flag)
- Domain Event Dispatching
- Pagination & Sorting
- FluentValidation
- Logging (Serilog)
- API Versioning
- Rate Limiting
This project is for educational purposes. Feel free to use it as a reference or template for your own projects.
Built as a hands-on learning project to master:
- Onion Architecture
- Domain-Driven Design
- CQRS Pattern
- EF Core as a state manager
- REST API best practices