A modern .NET 9 full-stack platform built with Vertical Slice Architecture (VSA), multi-tenancy, and Blazor WebAssembly β designed for efficient AI-assisted feature development.
- Install tools (once)
- dotnet-ef:
dotnet tool install --global dotnet-ef - Kiota CLI (optional for client regen):
dotnet tool install --global Microsoft.Kiota.Cli
- Run Aspire orchestration (starts PostgreSQL, dynamic ports)
dotnet run --project aspire/Krafter.Aspire.AppHost/Krafter.Aspire.AppHost.csproj
-
Copy DB connection string from Aspire Dashboard β PostgreSQL (format:
Host=localhost;Port=XXXXX;Username=postgres;Password=postgres;Database=krafterDb) -
Stop
krafter-apiservice in Aspire (unlocks Backend assembly) -
Set migration connection string
- EITHER update
src/Backend/appsettings.Local.jsonβConnectionStrings:KrafterDbMigration - OR set env var:
ConnectionStrings__KrafterDbMigration="Host=localhost;Port=XXXXX;Username=postgres;Password=postgres;Database=krafterDb"
- Apply migrations (once)
cd src/Backenddotnet ef database update --context KrafterContextdotnet ef database update --context TenantDbContextdotnet ef database update --context BackgroundJobsContext
- Restart
krafter-apiin Aspire and open URLs
- Aspire Dashboard: https://localhost:17285
- Backend API: https://localhost:5199
- Swagger UI: https://localhost:5199/swagger
- Blazor UI: https://localhost:7291
Note: Ports are dynamic per run; use the Aspire Dashboard values.
- TL;DR Quick Start
- Overview
- Architecture
- Key Features
- Technology Stack
- Getting Started
- Project Structure
- Development Guide
- Deployment
- Contributing
- License
Krafter is a production-ready, enterprise-grade full-stack platform built with .NET 9, combining modern architectural patterns with cutting-edge technologies. It provides a solid foundation for building scalable, multi-tenant SaaS applications with rich user interfaces.
- ποΈ Vertical Slice Architecture (VSA) - Backend organized by features, not layers
- π Hybrid Blazor - WebAssembly + Server rendering for optimal performance
- π’ Multi-Tenancy - Complete tenant isolation at the database level
- π Permission-Based Security - Fine-grained authorization with JWT
- β‘ Real-Time Updates - SignalR integration for live notifications
- π Observability - OpenTelemetry with Aspire orchestration
- π¨ Modern UI - Radzen components with theming support
- Vertical Slice Architecture (VSA) - Features organized by business capability
- Clean Code - Single Responsibility, DRY, SOLID principles
- Auto-Registration - Handlers, services, and routes discovered via markers
- Response Pattern - Consistent
Response<T>wrapper for all operations
- JWT Authentication - Secure token-based authentication
- Google OAuth - External authentication integration
- Permission-Based Authorization - Fine-grained access control
- Multi-Tenancy - Complete tenant isolation at DB level
- Token Refresh - Automatic token rotation
- Blazor Hybrid - WebAssembly + Server rendering
- Radzen Components - 70+ professional UI components
- Theme Support - Light/Dark/Auto modes with WCAG compliance
- Responsive Design - Mobile and desktop optimized
- Code-Behind Pattern - Clean separation of markup and logic
- Kiota Client - Type-safe, auto-generated API client
- EF Core - PostgreSQL & MySQL support
- Multi-Database - Separate contexts for tenants, jobs, and main data
- Migrations - Code-first database schema management
- Soft Delete - Recoverable data deletion
- Background Jobs - TickerQ for async processing
- SignalR - Real-time bi-directional communication
- Redis Cache - Distributed caching support
- Pagination - Efficient data loading
- Debouncing - Optimized search and filtering
- .NET Aspire - Orchestration and service discovery
- OpenTelemetry - Distributed tracing and metrics
- Structured Logging - Comprehensive application logs
- Health Checks - Service health monitoring
- NUKE Build - Automated build pipeline
- Docker Support - Containerized deployment
- GitHub Actions - CI/CD automation
- Auto Deployment - Webhook-triggered updates
- .NET 9 - Latest .NET framework
- ASP.NET Core - Minimal APIs
- Entity Framework Core 9 - ORM
- ASP.NET Core Identity - User management
- FluentValidation - Input validation
- TickerQ - Background job processing
- SignalR - Real-time communication
- Blazor WebAssembly - Client-side SPA
- Blazor Server - Server-side rendering
- Radzen Blazor - UI component library
- Microsoft Kiota - API client generation
- Blazored LocalStorage - Browser storage
- FluentValidation.Blazor - Client-side validation
- Mapster - Object mapping
- .NET Aspire - Cloud-native orchestration
- OpenTelemetry - Observability
- Redis - Caching (optional)
- PostgreSQL / MySQL - Database
- Docker - Containerization
- NUKE - Build automation
- .NET 9 SDK
- Docker Desktop (for Aspire/PostgreSQL)
- Visual Studio 2022 17.11+ or VS Code
- EF Core tools:
dotnet tool install --global dotnet-ef - Kiota CLI (optional, for API client regen):
dotnet tool install --global Microsoft.Kiota.Cli
-
Clone the repository
git clone https://github.com/AditiKraft/Krafter.git cd Krafter -
Restore packages
dotnet restore
-
Run with Aspire (starts DB and services)
dotnet run --project aspire/Krafter.Aspire.AppHost/Krafter.Aspire.AppHost.csproj
-
Apply database migrations (quick path)
- Open Aspire Dashboard β copy PostgreSQL connection string
- Stop
krafter-apiservice - Set
ConnectionStrings__KrafterDbMigration(or updatesrc/Backend/appsettings.Local.json) - Run:
cd src/Backend dotnet ef database update --context KrafterContext dotnet ef database update --context TenantDbContext dotnet ef database update --context BackgroundJobsContext - Restart
krafter-api
-
Configure secrets (see Secrets and Configuration)
-
Access the application
- Aspire Dashboard: https://localhost:17285
- Backend API: https://localhost:5199
- Swagger UI: https://localhost:5199/swagger
- Blazor UI: https://localhost:7291
-
Default Credentials
On first run, the application seeds a default admin account:
- Email:
admin@krafter.com - Password:
Admin@123
β οΈ Important: Change the default password immediately in production! - Email:
Before running the application for the first time, you need to set up the database and apply migrations.
- β Aspire orchestration must be running (provides PostgreSQL database)
- β Backend API must be stopped before running migration commands
- β
Connection string configured in
appsettings.Local.jsonor via environment variable
dotnet run --project aspire/Krafter.Aspire.AppHost/Krafter.Aspire.AppHost.csprojThis starts PostgreSQL and generates a dynamic connection string.
- Open the Aspire dashboard (typically
https://localhost:17285) - Navigate to the PostgreSQL resource
- Copy the connection string
Format:Host=localhost;Port=XXXXX;Username=postgres;Password=postgres;Database=krafterDb
- In the Aspire dashboard, stop the
krafter-apiservice - Why? The Backend assembly is locked when the API is running, preventing EF Core migration tools from accessing it
Open src/Backend/appsettings.Local.json and update the KrafterDbMigration connection string:
{
"ConnectionStrings": {
"KrafterDbMigration": "Host=localhost;Port=52961;Username=postgres;Password=postgres;Database=krafterDb"
}
}Important: Replace Port=52961 with the port from your Aspire dashboard (it changes on each run).
Tip: You can also set the environment variable ConnectionStrings__KrafterDbMigration instead of modifying the file.
Navigate to the Backend project directory and apply migrations:
cd src/Backend
# Apply all migrations
dotnet ef database update --context KrafterContext
dotnet ef database update --context TenantDbContext
dotnet ef database update --context BackgroundJobsContext- In the Aspire dashboard, restart the
krafter-apiservice - Or restart the entire Aspire orchestration
Your database is now ready! π
| Requirement | Reason |
|---|---|
| Aspire Running | Database connection strings are dynamically assigned by Aspire (ports change on each run) |
| Backend Stopped | EF Core migration tools compile and load the Backend assembly. If the API is running, the assembly is locked |
| appsettings.Local.json | With Backend stopped, we configure the connection string in appsettings or environment variables |
When you add new features that require database changes:
cd src/Backend
# Create a new migration
dotnet ef migrations add <MigrationName> --context KrafterContext
# Apply the migration
dotnet ef database update --context KrafterContext| Issue | Cause | Solution |
|---|---|---|
| "Unable to create DbContext" | Backend API is running | Stop the Backend API in Aspire dashboard |
| "Database connection failed" | Wrong port or Aspire not running | Verify Aspire is running and copy the correct connection string from dashboard |
| "Could not find DbContext" | Wrong working directory | Ensure you're in src/Backend directory |
| "Migration already exists" | Duplicate migration name | Use dotnet ef migrations remove --context <ContextName> |
dotnet-ef not found |
EF tools not installed | Run dotnet tool install --global dotnet-ef |
Before running the application, configure the following secrets and settings:
aspire/Krafter.Aspire.AppHost/appsettings.json
Parameters:postgresUsername={YOUR_POSTGRES_USERNAME}Parameters:postgresPassword={YOUR_POSTGRES_PASSWORD}
src/Backend/appsettings.json
TickerQBasicAuth:Username={TICKERQ_BASIC_AUTH_USERNAME}TickerQBasicAuth:Password={TICKERQ_BASIC_AUTH_PASSWORD}SecuritySettings:JwtSettings:Key={JWT_SIGNING_KEY}
src/UI/Krafter.UI.Web/appsettings.Development.json
Jwt:Key={JWT_SIGNING_KEY}(MUST be identical to backend JWT key)
src/UI/Krafter.UI.Web.Client/wwwroot/appsettings.json
RemoteHostUrl={YOUR_BACKEND_HOST}Authentication:Google:ClientId={YOUR_GOOGLE_CLIENT_ID}(optional, for Google OAuth)
β οΈ Important: The JWT signing key must be exactly the same for the Backend and the UI server host. This ensures tokens issued by the backend validate correctly during server-side rendering and prerendered Blazor scenarios.
From the Backend project (src/Backend):
# initialize once (if needed)
dotnet user-secrets init
# set JWT and TickerQ basic auth
dotnet user-secrets set "SecuritySettings:JwtSettings:Key" "<long-random-32+ chars>"
dotlet user-secrets set "TickerQBasicAuth:Username" "<username>"
dotlet user-secrets set "TickerQBasicAuth:Password" "<password>"From the Blazor Server host (src/UI/Krafter.UI.Web):
# initialize once (if needed)
dotnet user-secrets init
# JWT key must match Backend
dotnet user-secrets set "Jwt:Key" "<same-long-random-key-as-backend>"Krafter/
βββ aspire/ # Aspire orchestration
β βββ Krafter.Aspire.AppHost/ # Orchestration host
β βββ Krafter.Aspire.ServiceDefaults/ # Shared configuration
βββ src/
β βββ Backend/ # ASP.NET Core API (VSA)
β β βββ Features/ # Vertical slices (Auth, Users, Roles, Tenants)
β β βββ Infrastructure/ # Persistence, Jobs, Multi-tenancy
β β βββ Common/ # Shared utilities, permissions
β β βββ Api/ # API configuration, middleware
β β βββ Program.cs # Entry point
β βββ UI/
β βββ Krafter.UI.Web.Client/ # Blazor WebAssembly
β β βββ Features/ # Feature-based UI components
β β βββ Infrastructure/ # Services, Auth, API clients
β β βββ Common/ # Shared components, models
β β βββ Client/ # Auto-generated Kiota client
β βββ Krafter.UI.Web/ # Blazor Server host
βββ build/ # NUKE build project
βββ .github/ # GitHub Actions workflows
βββ README.md # This file
For detailed structure, see .github/copilot-instructions.md
Backend (VSA Pattern):
- Create feature folder:
Features/<Feature>/ - Add operation files (e.g.,
Create<Feature>.cs,Get<Feature>s.cs) - Add entity to
Features/<Feature>/_Shared/<Entity>.cs - Update
KrafterContext.cswith newDbSet - Create EF configuration in
Infrastructure/Persistence/Configurations/ - Run migration:
dotnet ef migrations add Add<Feature> - Add permissions to
Common/Auth/Permissions/KrafterPermissions.cs
UI (Blazor):
- Create feature folder:
Features/<Feature>/ - Add list page:
<Feature>s.razor+<Feature>s.razor.cs - Add form dialog:
CreateOrUpdate<Feature>.razor+.razor.cs - Add route constant to
Common/Constants/KrafterRoute.cs - Update permissions in
Common/Permissions/KrafterPermissions.cs - Update
Infrastructure/Services/MenuService.csfor navigation - Regenerate Kiota client:
kiota update
For complete guidelines, see Development Workflow
# Build solution
dotnet build
# Run tests
dotnet test
# Create migration
dotnet ef migrations add <Name> --project src/Backend --context KrafterContext
dotnet ef migrations add <Name> --project src/Backend --context BackgroundJobsContext
dotnet ef migrations add <Name> --project src/Backend --context TenantDbContext
# Update database
dotnet ef database update --project src/Backend --context KrafterContext
dotnet ef database update --project src/Backend --context BackgroundJobsContext
dotnet ef database update --project src/Backend --context TenantDbContext
# Regenerate Kiota API client (run from the Client folder containing kiota-lock.json)
cd src/UI/Krafter.UI.Web.Client/Client
kiota update
# Or from repo root:
# kiota update --lock-file-path src/UI/Krafter.UI.Web.Client/Client/kiota-lock.jsonBuild images:
dotnet publish src/Backend/Backend.csproj -c Release -p:PublishProfile=DefaultContainer
dotnet publish src/UI/Krafter.UI.Web/Krafter.UI.Web.csproj -c Release -p:PublishProfile=DefaultContainerThe project includes automated CI/CD pipelines that:
- Build and test on every push
- Create Docker images for
mainanddevbranches - Push images to GitHub Container Registry
- Trigger deployment webhooks
See .github/workflows for configuration.
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Follow the coding conventions in copilot-instructions.md
- Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Use Conventional Commits:
feat(scope): add new feature
fix(scope): fix bug
docs(scope): update documentation
refactor(scope): refactor code
test(scope): add tests
This project is licensed under the MIT License - see the LICENSE file for details.
- .NET Team - For the amazing .NET platform
- Radzen - For the excellent Blazor components
- Microsoft Kiota - For the API client generator
- NUKE Build - For the build automation framework
- Documentation: Copilot Instructions
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Built with β€οΈ by Aditi Kraft
β Star this repository if you find it helpful!