Skip to content
Β 
Β 

Latest commit

Β 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Krafter

Krafter logo

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.

.NET 9 Blazor License

⚑ TL;DR Quick Start

  1. Install tools (once)
  • dotnet-ef: dotnet tool install --global dotnet-ef
  • Kiota CLI (optional for client regen): dotnet tool install --global Microsoft.Kiota.Cli
  1. Run Aspire orchestration (starts PostgreSQL, dynamic ports)
  • dotnet run --project aspire/Krafter.Aspire.AppHost/Krafter.Aspire.AppHost.csproj
  1. Copy DB connection string from Aspire Dashboard β†’ PostgreSQL (format: Host=localhost;Port=XXXXX;Username=postgres;Password=postgres;Database=krafterDb)

  2. Stop krafter-api service in Aspire (unlocks Backend assembly)

  3. 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"
  1. Apply migrations (once)
  • cd src/Backend
  • dotnet ef database update --context KrafterContext
  • dotnet ef database update --context TenantDbContext
  • dotnet ef database update --context BackgroundJobsContext
  1. Restart krafter-api in Aspire and open URLs

Note: Ports are dynamic per run; use the Aspire Dashboard values.

πŸ“‹ Table of Contents

🎯 Overview

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.

What Makes Krafter Special?

  • πŸ—οΈ 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

πŸ›οΈ Architecture

Solution Architecture Diagram

Krafter Solution Architecture

Project Dependencies Diagram

Project Dependencies

✨ Key Features

πŸ—οΈ Architecture

  • 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

πŸ” Security

  • 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

🎨 User Interface

  • 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

πŸ“Š Data & Storage

  • 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

⚑ Performance & Scalability

  • 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

πŸ” Observability

  • .NET Aspire - Orchestration and service discovery
  • OpenTelemetry - Distributed tracing and metrics
  • Structured Logging - Comprehensive application logs
  • Health Checks - Service health monitoring

πŸš€ DevOps

  • NUKE Build - Automated build pipeline
  • Docker Support - Containerized deployment
  • GitHub Actions - CI/CD automation
  • Auto Deployment - Webhook-triggered updates

πŸ› οΈ Technology Stack

Backend

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

Frontend

  • 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

Infrastructure

  • .NET Aspire - Cloud-native orchestration
  • OpenTelemetry - Observability
  • Redis - Caching (optional)
  • PostgreSQL / MySQL - Database
  • Docker - Containerization
  • NUKE - Build automation

πŸš€ Getting Started

Prerequisites

Install Tools

  • EF Core tools: dotnet tool install --global dotnet-ef
  • Kiota CLI (optional, for API client regen):
    • dotnet tool install --global Microsoft.Kiota.Cli

Quick Start

  1. Clone the repository

    git clone https://github.com/AditiKraft/Krafter.git
    cd Krafter
  2. Restore packages

    dotnet restore
  3. Run with Aspire (starts DB and services)

    dotnet run --project aspire/Krafter.Aspire.AppHost/Krafter.Aspire.AppHost.csproj
  4. Apply database migrations (quick path)

    • Open Aspire Dashboard β†’ copy PostgreSQL connection string
    • Stop krafter-api service
    • Set ConnectionStrings__KrafterDbMigration (or update src/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
  5. Configure secrets (see Secrets and Configuration)

  6. Access the application

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


Database Migrations Workflow

Before running the application for the first time, you need to set up the database and apply migrations.

Prerequisites for Migrations

  • βœ… Aspire orchestration must be running (provides PostgreSQL database)
  • βœ… Backend API must be stopped before running migration commands
  • βœ… Connection string configured in appsettings.Local.json or via environment variable

Step-by-Step Migration Process

1. Start Aspire Orchestration
dotnet run --project aspire/Krafter.Aspire.AppHost/Krafter.Aspire.AppHost.csproj

This starts PostgreSQL and generates a dynamic connection string.

2. Copy Database 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
3. Stop the Backend API
  • In the Aspire dashboard, stop the krafter-api service
  • Why? The Backend assembly is locked when the API is running, preventing EF Core migration tools from accessing it
4. Update Migration Connection String

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.

5. Apply Migrations

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
6. Restart Backend API
  • In the Aspire dashboard, restart the krafter-api service
  • Or restart the entire Aspire orchestration

Your database is now ready! πŸŽ‰


Why This Workflow?

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

Creating New Migrations (For Development)

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

Troubleshooting

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

Secrets and Configuration

Before running the application, configure the following secrets and settings:

Required Configuration

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.

Quick commands via user-secrets (recommended for dev)

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

πŸ“ Project Structure

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

πŸ“– Development Guide

Adding a New Feature

Backend (VSA Pattern):

  1. Create feature folder: Features/<Feature>/
  2. Add operation files (e.g., Create<Feature>.cs, Get<Feature>s.cs)
  3. Add entity to Features/<Feature>/_Shared/<Entity>.cs
  4. Update KrafterContext.cs with new DbSet
  5. Create EF configuration in Infrastructure/Persistence/Configurations/
  6. Run migration: dotnet ef migrations add Add<Feature>
  7. Add permissions to Common/Auth/Permissions/KrafterPermissions.cs

UI (Blazor):

  1. Create feature folder: Features/<Feature>/
  2. Add list page: <Feature>s.razor + <Feature>s.razor.cs
  3. Add form dialog: CreateOrUpdate<Feature>.razor + .razor.cs
  4. Add route constant to Common/Constants/KrafterRoute.cs
  5. Update permissions in Common/Permissions/KrafterPermissions.cs
  6. Update Infrastructure/Services/MenuService.cs for navigation
  7. Regenerate Kiota client: kiota update

For complete guidelines, see Development Workflow

Key Commands

# 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.json

🐳 Deployment

Docker Deployment

Build 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=DefaultContainer

CI/CD with GitHub Actions

The project includes automated CI/CD pipelines that:

  • Build and test on every push
  • Create Docker images for main and dev branches
  • Push images to GitHub Container Registry
  • Trigger deployment webhooks

See .github/workflows for configuration.

🀝 Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Follow the coding conventions in copilot-instructions.md
  4. Commit your changes (git commit -m 'feat: add amazing feature')
  5. Push to the branch (git push origin feature/amazing-feature)
  6. Open a Pull Request

Commit Convention

Use Conventional Commits:

feat(scope): add new feature
fix(scope): fix bug
docs(scope): update documentation
refactor(scope): refactor code
test(scope): add tests

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

πŸ“ž Support


Built with ❀️ by Aditi Kraft

⭐ Star this repository if you find it helpful!

About

A modern .NET 9 full-stack platform with Vertical Slice Architecture, multi-tenancy, and Blazor WebAssembly

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages