Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

126 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TaskWorkflow

CI

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.

📖 Documentation

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

📌 Key Features

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

🚀 Quick Start

# 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:5003

See GETTING_STARTED.md for detailed instructions.

🏗️ Architecture Overview

┌─────────────────────────────────────────────────────────────────────┐
│                         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 IdentityController is added to REST API via AddApplicationPart(). Auth endpoints are available at POST /api/v1/identity/* on the REST API.

🔐 Authentication Architecture

The project implements Identity Bounded Context as the Single Source of Truth for authentication:

Bounded Contexts

BC Responsibility Schema
Identity BC Users, Sessions, Auth identity.*
TaskWorkflow BC Projects, Tasks public.*

Auth Flows

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

Auth Endpoints (REST API :5193)

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

Demo Credentials

Email Password Role
admin@test.com Password123! Admin

See AUTHENTICATION.md for details.

🧬 DDD Patterns

Domain Events

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);

Value Objects

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);
    }
}

State Machine

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;
}

⚙️ MediatR Pipeline Behaviors

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

🤖 CI/CD Pipeline

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 Release

Why Testcontainers?

dotnet 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

🐳 Docker

# 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

🩺 Health Checks

Endpoint Purpose
/health Full health report
/health/live Liveness probe
/health/ready Readiness probe (DB check)

💬 Interview Talking Points

Architecture

"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."

UI/UX

"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."

Authentication

"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."

API Design

"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."

DDD

"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."

Testing

"Our CI pipeline uses Testcontainers to run real integration tests against PostgreSQL without external dependencies."

🎯 Project Status

  • ✅ 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

⚠️ Demo Simplifications & Production Roadmap

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.

🟡 Simplified in Demo

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

🔴 Not Implemented (Needed for Production)

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

🟢 Production-Ready (Already Implemented)

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 SecurityHttpOnly, 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

📚 Technologies

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

📄 License

This project is licensed under the MIT License.

About

Modular task and process management system (Task/Workflow Management System). .NET 8 Clean Architecture reference with DDD, CQRS, Bounded Contexts, state machine, domain events. Five UI clients (REST API, gRPC, Blazor Server, WPF, Identity Admin). JWT + refresh token rotation, BCrypt, cookie auth. PostgreSQL, EF Core, Testcontainers, Docker, GitHub

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages