A task management API for teams — workspaces, projects and tasks, built with Clean Architecture on .NET.
TaskFlow is a REST API for team task management, in the spirit of a simplified Linear/Trello "behind the scenes." A user creates a Workspace, invites members, organizes work into Projects, and tracks work as Tasks with status, priority and an assignee.
It's built as a portfolio piece to demonstrate production-grade backend patterns in .NET: layered architecture, real authentication/authorization, domain modeling with enforced business rules, automated testing and CI/CD — not just CRUD.
The MVP is deployed on Render: https://taskflow-api-np2z.onrender.com
GET /health— liveness check- Explore the endpoints with the Postman collection (set
baseUrlto the URL above)
Demo GIF of the API flow (Swagger/Postman): coming soon.
The full entity model and business rules live in docs/DOMAIN_RULES.md —
the single source of truth kept in sync with the code across phases. The roadmap is in
docs/MVP.md.
The choices here are deliberate, not defaults:
- Minimal APIs + Clean Architecture — endpoints stay thin (bind + send to MediatR), and every
business rule lives in
ApplicationorDomain, where it is unit-testable without HTTP or a database. TheApiproject has almost no logic. - MediatR CQRS + pipeline behaviors — commands and queries are separated, and cross-cutting
concerns are centralized as pipeline behaviors:
ValidationBehavior(FluentValidation) andWorkspaceAuthorizationBehavior(membership checks) run before every handler, so the isolation rule is written once, not repeated in each handler. - Testcontainers + real Postgres — integration tests run against a real database in Docker (or a provided service in CI), reaching 97.3% line coverage of the codebase.
Api → Infrastructure → Application → Domain
Domain references nothing. Application references only Domain. Infrastructure implements the
interfaces defined in Application. Api wires everything via dependency injection.
src/
├── TaskFlow.Domain/ # Entities, enums, pure domain rules
├── TaskFlow.Application/ # Commands, queries, handlers (MediatR), validators, interfaces
├── TaskFlow.Infrastructure/ # EF Core, DbContext, repositories, token/password services
└── TaskFlow.Api/ # Minimal API endpoints, middlewares, DI
tests/
├── TaskFlow.UnitTests/ # Handlers, behaviors and validators in isolation (Moq)
└── TaskFlow.IntegrationTests/# Full HTTP flows against real Postgres (Testcontainers)
- One authorization pipeline, three scopes — the
WorkspaceAuthorizationBehaviorresolves the workspace for any operation markedIWorkspaceScoped,IProjectScopedorITaskScopedand checks membership before the handler runs. A user who is not a member gets403even if they know the resource ID. - Last Admin protection — a workspace can never be left without an Admin: removing or demoting the last Admin is rejected (409), at the application level, with the database as a safety net.
- The database is a safety net, never a silent destructor — deleting a workspace cascades
(explicit, Admin-only), while deleting a user is blocked by
Restrict/NO ACTIONFKs until the application resolves their workspaces, memberships and assignments. TaskItem, notTask— avoids the collision withSystem.Threading.Tasks.Task.- Fail fast — JWT configuration is validated at startup (
ValidateOnStart), migrations apply automatically on boot, and every error returns a standardized{ type, message, errors, traceId }.
- Docker
- .NET 10 SDK
git clone https://github.com/vvasconceloss/taskflow-api.git
cd taskflow-apiStart PostgreSQL (Docker):
docker compose up -dRestore and build:
dotnet restore
dotnet buildConfigure local secrets (outside version control):
dotnet user-secrets set "ConnectionStrings:DefaultConnection" \
"Host=localhost;Port=5433;Database=taskflow;Username=taskflow;Password=taskflow" \
--project src/TaskFlow.Api
dotnet user-secrets set "Jwt:Secret" "dev-secret-0123456789abcdef0123456789abcdef" \
--project src/TaskFlow.ApiRun the API:
dotnet run --project src/TaskFlow.ApiThe API will be available at http://localhost:5183 (check /health for a liveness check, and
/swagger for the OpenAPI UI in development).
dotnet test- Unit tests run in isolation with mocked dependencies (handlers, behaviors, validators).
- Integration tests run full HTTP flows against a real PostgreSQL — either a container spun up
by Testcontainers, or the
POSTGRES_CONNECTION_STRINGenvironment variable when provided (how CI runs them against the workflow's Postgres service).
Current suite: 101 tests, 100% passing, 97.3% line coverage on the integration side.
- Refresh tokens — short-lived access tokens + rotation (the MVP uses a single long-lived JWT).
- Task comments and file attachments — the natural next vertical slice.
- Notifications (email/push) for assignments and mentions.
- Real-time updates via SignalR when tasks change.
- A frontend — the API is fully REST; a web client (React/Blazor) would complete the product.
This project is licensed under the MIT License.