A modern task management system built with Clean Architecture, Domain-Driven Design (DDD), and CQRS patterns.
Production-like architecture designed to demonstrate enterprise-grade practices in .NET 8. Suitable for portfolio, interviews, and as a reference implementation.
| Document | Description |
|---|---|
| README.md | Project overview and architecture |
| GETTING_STARTED.md | Step-by-step setup instructions |
| GETTING_STARTED.ru.md | Инструкция по запуску (RU) |
| DATABASE.md | Database configuration guide |
| AUTHENTICATION.md | Authentication architecture |
| REFRESH_TOKENS.md | Refresh tokens implementation |
| TARGET_AUTH_ARCHITECTURE.md | Identity BC as Single Source |
| PATTERNS.md | All patterns & interview prep |
| PATTERNS_FULL_LIST.md | 📋 Full patterns list (25 patterns) |
| TODO.md | Roadmap and future tasks |
| Category | Technologies & Patterns |
|---|---|
| Framework | .NET 8, ASP.NET Core, gRPC, WPF, Blazor Server |
| UI | MudBlazor (Material Design), Dark Mode, Responsive |
| Architecture | Clean Architecture, DDD, CQRS, MVVM, Bounded Contexts |
| Authentication | Identity BC (Single Source), JWT + Refresh Tokens, Cookie Auth, BCrypt |
| Data | PostgreSQL / SQL Server, EF Core 8, Migrations, Multi-Schema |
| API | REST + gRPC (shared use cases), API Versioning, Swagger |
| Error Handling | Result Pattern, RFC 7807 ProblemDetails, Unified Error Normalization |
| Resilience | Polly (Retry + Circuit Breaker), HttpClientFactory |
| Testing | xUnit, Testcontainers, Respawn |
| DevOps | Docker, GitHub Actions CI, Health Checks |
# Clone repository
git clone https://github.com/i-nedbaylo/TaskWorkflow.git
cd TaskWorkflow
# Start PostgreSQL
docker compose up -d postgres
# Run REST API (database created automatically)
dotnet run --project TaskWorkflow.Api
# Open Swagger UI: http://localhost:5193/swagger
# Identity endpoints: POST /api/v1/identity/login
# Run Blazor Server (main UI)
dotnet run --project TaskWorkflow.BlazorServer
# Open: http://localhost:5024
# Run Identity Admin Portal
dotnet run --project TaskWorkflow.Identity.Admin
# Open: https://localhost:5003See GETTING_STARTED.md for detailed instructions.
┌─────────────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
├─────────────┬─────────────┬─────────────┬─────────────┬─────────────┤
│ REST API │ gRPC API │BlazorServer │ WPF Client │Identity.Admin│
│ :5193 │ :5287 │ :5024 │ │ :5003 │
│ (JWT) │ (JWT) │ (Cookie) │ (JWT) │ (Cookie) │
│ +Identity │ │ │ │ │
│ endpoints │ │ │ │ │
└──────┬──────┴──────┬──────┴──────┬──────┴──────┬──────┴──────┬──────┘
│ │ │ │ │
└─────────────┴──────┬──────┴─────────────┴─────────────┘
│
┌─────────────┴─────────────┐
│ │
▼ ▼
┌──────────────────────┐ ┌───────────────────────┐
│ APPLICATION LAYER │ │ IDENTITY BC │
│ ────────────────── │ │ (Auth Single Source) │
│ TaskWorkflow.App │ │ ────────────────── │
│ • Projects │ │ • LoginCommand │
│ • Tasks │ │ • AuthenticateCmd │
│ • Users (profile) │ │ • RefreshTokenCmd │
│ │ │ • RegisterUserCmd │
└──────────┬───────────┘ └───────────┬───────────┘
│ │
┌──────────▼───────────┐ ┌───────────▼───────────┐
│ DOMAIN LAYER │ │ IDENTITY.CORE │
│ ────────────────── │ │ ────────────────── │
│ TaskWorkflow.Core │ │ • IdentityUser │
│ • Project │ │ • RefreshSession │
│ • TaskItem │ │ • Email VO │
│ • User │ │ • PasswordHash VO │
└──────────┬───────────┘ └───────────┬───────────┘
│ │
┌──────────▼───────────┐ ┌───────────▼───────────┐
│ INFRASTRUCTURE LAYER │ │ IDENTITY.INFRA │
│ ────────────────── │ │ ────────────────── │
│ public schema │ │ identity schema │
│ • projects │ │ • users │
│ • task_items │ │ • refresh_sessions │
│ • users (legacy) │ │ • JWT services │
└──────────┬───────────┘ └───────────┬───────────┘
│ │
└─────────────┬─────────────┘
│
┌─────────▼─────────┐
│ PostgreSQL │
│ ─────────────── │
│ public schema │
│ identity schema │
└───────────────────┘
Note: Identity BC and TaskWorkflow BC are independent bounded contexts. They do not call each other directly. Presentation layer (REST API, Blazor, etc.) orchestrates calls to both contexts as needed.
Identity.Api is a class library, not a standalone service. Its
IdentityControlleris added to REST API viaAddApplicationPart(). Auth endpoints are available atPOST /api/v1/identity/*on the REST API.
The project implements Identity Bounded Context as the Single Source of Truth for authentication:
| BC | Responsibility | Schema |
|---|---|---|
| Identity BC | Users, Sessions, Auth | identity.* |
| TaskWorkflow BC | Projects, Tasks | public.* |
| Client | Flow | Description |
|---|---|---|
| WPF Client | JWT | POST /api/v1/identity/login on REST API → tokens |
| REST API | JWT Validation | Shared secret, no DB call |
| gRPC | JWT Validation | Shared secret, no DB call |
| Blazor Server | Cookie | AuthenticateCommand (in-process) → cookie |
| Identity.Admin | Cookie | AuthenticateCommand (in-process) → cookie + Admin role |
Identity endpoints are hosted on the REST API (not a separate service):
POST /api/v1/identity/login # Get JWT tokens
POST /api/v1/identity/refresh # Refresh tokens (rotation)
POST /api/v1/identity/logout # Revoke current session
POST /api/v1/identity/logout-all # Revoke all sessions [Authorize]
POST /api/v1/identity/register # Register new user
| Password | Role | |
|---|---|---|
admin@test.com |
Password123! |
Admin |
See AUTHENTICATION.md for details.
Domain events are raised by entities and published after successful persistence:
// Entity raises event
public void Complete()
{
State = TaskState.Done;
RaiseDomainEvent(new TaskCompletedEvent(Id));
}
// UnitOfWork publishes after save
await _dbContext.SaveChangesAsync();
await PublishDomainEventsAsync(domainEvents);Self-validating value objects ensure data integrity:
public sealed class Email : ValueObject
{
public static Email Create(string email)
{
if (!EmailRegex().IsMatch(email))
throw new InvalidEmailException(email);
return new Email(email);
}
}Task state transitions are enforced in the domain:
New ──► InProgress ──► Done
│
└── Only valid transitions allowed
public void Start()
{
if (State != TaskState.New)
throw new TaskStateException("Only new tasks can be started");
State = TaskState.InProgress;
}Requests flow through a pipeline of behaviors:
Request → Validation → Logging → Handler → UnitOfWork → Response
│ │ │
▼ ▼ ▼
FluentValidation Timing Auto-SaveChanges
throws if invalid metrics (commands only)
| Behavior | Purpose |
|---|---|
ValidationBehavior |
Validates requests with FluentValidation |
LoggingBehavior |
Logs request name and execution time |
UnitOfWorkBehavior |
Auto-saves changes after successful commands |
The project includes a production-ready CI pipeline using GitHub Actions:
# .github/workflows/ci.yml
on:
push:
branches: [ main, master, develop ]
pull_request:
branches: [ main, master, develop ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotenv@v4
- run: dotnet build --configuration Release
- run: dotnet test --configuration Releasedotnet test
│
▼
Testcontainers starts PostgreSQL container
│
▼
EF Core migrations applied
│
▼
REST + gRPC tests run against real database
│
▼
Container destroyed
- ✅ Real PostgreSQL (not InMemory)
- ✅ No external dependencies
- ✅ CI-ready (Docker available on GitHub runners)
- ✅ Fast cleanup with Respawn
# Start full stack
docker compose -f docker-compose.yml -f docker-compose.app.yml up -d
# Endpoints:
# REST API: http://localhost:8080/swagger
# gRPC: localhost:8081
# Health: http://localhost:8080/health| Endpoint | Purpose |
|---|---|
/health |
Full health report |
/health/live |
Liveness probe |
/health/ready |
Readiness probe (DB check) |
"We use Clean Architecture with CQRS — commands and queries are separate concerns handled by MediatR."
"Domain events are collected during the transaction and published after successful persistence, ensuring consistency."
"The Blazor Server UI uses MudBlazor for Material Design components with built-in dark mode and responsive layout."
"Blazor Server shares the same Application layer as REST/gRPC — no duplicate business logic."
"We use refresh token rotation — each refresh invalidates the old token and issues a new one, limiting damage from token theft."
"Refresh tokens are stored as SHA256 hashes. Even if the database is compromised, tokens cannot be used directly."
"Blazor Server uses cookie auth because it runs in-process — no need for JWT overhead."
"REST and gRPC are alternative transport layers reusing the same application use cases."
"We normalize REST ProblemDetails and gRPC RpcExceptions into a unified UI error model."
"Value objects like Email are self-validating — invalid state is impossible by construction."
"TaskItem implements a state machine — only valid transitions (New→InProgress→Done) are allowed."
"Our CI pipeline uses Testcontainers to run real integration tests against PostgreSQL without external dependencies."
- ✅ Clean Architecture + DDD + CQRS
- ✅ REST API + gRPC API + Blazor Server (MudBlazor)
- ✅ JWT Authentication + Refresh Tokens
- ✅ WPF Client (MVVM + Polly)
- ✅ PostgreSQL / SQL Server
- ✅ Integration Tests (Testcontainers)
- ✅ Docker deployment
- ✅ GitHub Actions CI
This is an educational/portfolio project. The architecture is production-grade, but some operational concerns are simplified for clarity. Below is a transparent list of what's simplified and what's needed for a real production deployment.
| Area | Current (Demo) | Production Requirement |
|---|---|---|
| Secrets | JWT key in appsettings.Development.json, hardcoded test fallbacks in Program.cs |
Azure Key Vault / AWS Secrets Manager / HashiCorp Vault. No secrets in source control |
| Seed Data | Admin user admin@test.com / Password123! seeded on every startup via IdentitySeedService |
Seed only in CI/staging. Production users created via admin API or migration scripts |
| DB Migrations | AutoMigrate=true applies migrations at startup; UseDevContainer starts PostgreSQL in Docker |
Migrations applied via CI/CD pipeline (dotnet ef migrations bundle). Never auto-migrate in production |
| CORS | AllowAll policy in Development (AllowAnyOrigin + AllowAnyMethod) |
Strict origin whitelist per environment. No wildcards |
| Logging | Default Microsoft.Extensions.Logging (console) |
Serilog / OpenTelemetry with structured logging to ELK / Seq / Application Insights |
| Domain Events | Published in-process via MediatR after SaveChanges — if publish fails, the transaction is already committed |
Outbox Pattern with OutboxMessage table — events saved in the same DB transaction, published by a background worker |
| Rate Limiting | Global fixed window (100 req/min), no per-endpoint tuning | Per-endpoint policies (stricter for /login, /register), distributed rate limiting via Redis |
| HTTPS | SameAsRequest cookie policy in Blazor, HTTP in development |
CookieSecurePolicy.Always, HSTS headers, TLS termination at load balancer |
| Antiforgery | DisableAntiforgery() on Blazor auth endpoints (required for form POST before SignalR connects) |
Acceptable for Blazor's form-POST pattern, but should be audited in context |
| Error Details | Full exception details exposed in Development/Testing environments | Verify IsDevelopment() / IsEnvironment("Testing") guards are never true in production |
| Dockerfiles | No non-root user, missing .dockerignore |
Run as non-root user, add .dockerignore, scan images for vulnerabilities |
| CI Pipeline | Build + Test + Docker build. No deployment step | Add staging/production deployment, SAST/DAST scanning, image push to registry |
| Area | What's Missing | Why It Matters |
|---|---|---|
| Observability | No distributed tracing (OpenTelemetry), no metrics (Prometheus), no centralized logging | Cannot diagnose issues in distributed system. traceId is set in ProblemDetails but not connected to a tracing backend |
| Audit Logging | No audit trail for security events (login, logout, permission changes) | Compliance requirement (SOC 2, GDPR). Table AuditLogs not implemented |
| Email Service | No IEmailService — password reset, email verification, notifications not possible |
Users cannot recover accounts. No email verification on registration |
| Login Brute Force Protection | No per-IP/per-user login attempt limiting | Vulnerable to credential stuffing. Need account lockout after N failed attempts |
| 2FA / MFA | No TOTP, no email-based second factor | Security gap for sensitive operations |
| Password Reset | No ForgotPasswordCommand / ResetPasswordCommand flow |
Users cannot recover forgotten passwords |
| Session Cleanup | Expired/revoked RefreshSession records accumulate in DB |
Need a background job (IHostedService) to periodically purge expired sessions |
| Idempotency Keys | No idempotency for POST/PUT operations | Retry of failed requests may create duplicate resources |
| Outbox Pattern | Documented in architecture, but domain events are published in-process after save (no outbox table) | If the process crashes between SaveChanges and Publish, events are lost |
| Data Encryption at Rest | No column-level encryption for PII | May be required for GDPR/HIPAA compliance |
| Backup & Recovery | No database backup strategy documented | Need automated backups with tested restore procedures |
These concerns are handled at production quality:
- ✅ Clean Architecture — proper layer separation, dependency inversion
- ✅ Result Pattern — no exceptions for business errors, consistent error flow
- ✅ RFC 7807 ProblemDetails — standardized API error format with
traceId - ✅ JWT + Refresh Token Rotation — short-lived access tokens (15 min), one-time refresh tokens
- ✅ gRPC + REST JWT Auth — both transports validate JWT with shared secret,
FallbackPolicy.RequireAuthenticatedUser() - ✅ BCrypt Password Hashing — work factor 12, SHA256 for token storage
- ✅ Cookie Security —
HttpOnly,SameSite, secure flags - ✅ Bounded Contexts — separate DB schemas (
public,identity), no direct cross-BC calls - ✅ Security Headers — CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy
- ✅ Health Checks — liveness/readiness probes for container orchestration
- ✅ API Versioning — URL path versioning with Swagger per version
- ✅ Resilience (WPF) — Polly retry + circuit breaker + timeout pipeline
- ✅ Integration Tests — real PostgreSQL via Testcontainers, Respawn for cleanup
- ✅ CI Pipeline — automated build, test, Docker image verification
- ✅ Domain-Driven Design — rich domain model, value objects, state machine, domain events
- ✅ CQRS + Pipeline Behaviors — Validation → Logging → UnitOfWork → Handler
| Category | Technology |
|---|---|
| Framework | .NET 8 |
| Web | ASP.NET Core, Blazor Server, MudBlazor 7 |
| ORM | Entity Framework Core 8 |
| Database | PostgreSQL 16 / SQL Server 2019+ |
| CQRS/Mediator | MediatR 12 |
| Validation | FluentValidation 11 |
| API | REST, gRPC |
| Auth | JWT, BCrypt, Cookie Auth |
| Resilience | Polly 8 |
| Testing | xUnit, Testcontainers, Respawn |
| Desktop | WPF (MVVM) |
| CI/CD | GitHub Actions |
This project is licensed under the MIT License.