Self-hosted service
neozhu/CleanArchitectureWithBlazorServer avatar
neozhu/CleanArchitectureWithBlazorServer

A Blazor Server template with Clean Architecture layers and a dynamic fields model

This repository is designed to create an enterprise Blazor Server application that follows the principles of Clean Architecture and implements Blazor Clean Architecture best practices for scalability, maintainability, and testability.

1,234 stars311 forksC#MIT

At a glance

What is it?
This repository is an MIT licensed .NET 10 Blazor Server solution template that separates Domain, Application, Infrastructure and Server.UI, adds MediatR, FluentValidation, MudBlazor, multi-tenancy and a Visual Studio code generation extension. Its distinctive piece is a dynamic fields subsystem that adds template driven attributes to entities without adding a column per field.
Who is it for?
This template fits a team building an internal enterprise application on .NET 10 that wants the Clean Architecture layer boundaries, the code generation and the Docker and multi-tenancy plumbing already in place rather than argued from scratch, and it fits a team that needs custom attributes on business entities without a schema change per attribute.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Four layers, and an explicit note about what the diagram means

The repository provides a production-grade Blazor Server solution template designed in strict accordance with Clean Architecture principles and modern enterprise application standards, and it is stated to serve both as a reference implementation for Blazor Clean Architecture best practices and as a ready-to-use foundation.

The layer responsibilities are listed plainly.

- Domain: Core business entities and rules (no dependencies) - Application: Business logic, interfaces, and DTOs - Infrastructure: External concerns (database, email, file system) - Server.UI: Blazor components and user interface

And then there is a sentence in the architecture section that is worth pausing on, because it is the kind of clarification most architecture diagrams do not carry. The diagram shows runtime calls and domain model usage. Application calls Infrastructure implementations through Application-owned interfaces, and these arrows do not represent compile-time project references.

That is a precise and unusual admission. It means the picture is not a project reference graph. The application layer owns the interfaces and the infrastructure layer implements them, and the direction of the dependency at compile time is not what the arrow shows. In a strict Clean Architecture implementation the application layer would not reference the infrastructure project at all, and the composition root would wire them. The diagram is showing the runtime flow, and the note is there to stop a reader from concluding that the dependency rule has been violated.

Whether the implementation is strict in the compile-time sense is something the reader has to check in the project files rather than in the diagram, and the note is best read as a warning that the picture is not a dependency graph.

The diagram itself is shipped as a vector file at doc/architecture.svg, and there is an interactive version at doc/architecture.html built with what the README calls Archify, which you can open locally to inspect source references, switch themes and export images.

The dynamic fields model is the interesting part

The dynamic-fields subsystem is the piece of this template that is not a stock architecture pattern, and the README describes it as adding template-driven attributes to selected business entities without adding entity-specific columns for every custom field. Product is the reference implementation, and the same building blocks can be reused by future entities such as Contract or Asset.

The model is a small tree.

text
FieldGroupTemplate
└── FieldSection
    └── FieldSectionItem ──> FieldDefinition
                               (type, label, limits, regex, unit, picklist)

ExtensibleEntity
└── FieldGroupInstance ──> FieldGroupTemplate
    └── FieldValue ──────> FieldDefinition

The separation is between field metadata and record values, and the README names the pieces.

- `FieldDefinition` describes a reusable field and its validation metadata. - `FieldGroupTemplate` organizes definitions into ordered sections and determines which fields apply to a business record. - `FieldGroupInstance` binds one extensible business record to one template. - `FieldValue` stores one normalized string value for one definition. - `ExtensibleEntity` exposes the optional `DynamicFields` navigation used by supported business entities.

So a template says which fields apply, in which section, in which order, with what validation. A record points at a template. Each value is a normalised string against a definition. And an entity that opts in gets a DynamicFields navigation property.

The type metadata on a definition is worth noting, because it is what makes the values usable without a schema change. The type, the label, the limits, the regex, the unit and the picklist all live in the definition rather than in a column, so the validation rules travel with the template. A picklist in particular turns a custom field into a constrained one without needing a foreign key column.

The persistence detail is in the README too. ExtensibleEntity uses EF Core table-per-type, or TPT, mapping.

A full enterprise stack, and the list is long

The technology stack table is a good index of what the template covers.

| Layer | Technologies | |-------|-------------| | Frontend | Blazor Server, MudBlazor, SignalR | | Backend |.NET 10, ASP.NET Core, MediatR, FluentValidation | | Database | Entity Framework Core, MSSQL/PostgreSQL/SQLite | | Authentication | ASP.NET Core Identity, OAuth 2.0, JWT | | Caching | FusionCache, Redis | | Background Processing | Hosted services, in-memory queues | | Testing | xUnit, FluentAssertions, Moq | | DevOps | Docker, GitHub Actions |

A few of those choices carry weight. MediatR is the mediator pattern, which is how the application layer's use cases are dispatched, and it is what makes the Application layer testable without the Infrastructure layer, since the tests assert on the request and the handler. FluentValidation sits alongside it for request validation.

Three database providers is a deliberate choice for a template, since it forces the persistence code to stay behind the Entity Framework abstraction rather than leaking provider specific SQL into the application layer, and the compose file picks one at deploy time through a DB_PROVIDER variable.

The authentication row is a combination rather than a choice. ASP.NET Core Identity gives local accounts, OAuth 2.0 with JWT covers external providers, and the compose file shows Microsoft and Google client id and secret pairs in the environment, so the template ships with the plumbing for both.

The key features list adds the ones that are not visible in a stack table. Multi-tenancy with built-in tenant isolation and management. An advanced data grid with sorting, filtering, pagination and export. A Visual Studio extension for code generation. Docker containerization support. And a progressive web app capability for mobile.

The release history shows the project moving from a versioned 1.1.x line to v1.2 on 2026-09-03, with the last push on 2026-10-05.

The compose file shows what a deployment actually needs

The docker-compose.yml is the most useful file for understanding the operational surface, because it lists every credential the application expects.

The dashboard service builds from the repository Dockerfile and reads a long list of environment variables, and the naming pattern is the ASP.NET Core configuration convention with double underscores as the section separator. AppConfigurationSettings__ApplicationUrl and AppConfigurationSettings__AppName for the application, DatabaseSettings__DBProvider and DatabaseSettings__ConnectionString for the database, AISettings__GeminiApiKey and AISettings__OpenAIApiKey for the AI features, and a block of SmtpClientOptions__ settings for mail, with UserName, Port, Host, Password and DefaultFromEmail.

The authentication settings are Authentication__Microsoft__ClientId, Authentication__Microsoft__ClientSecret, Authentication__Google__ClientId and Authentication__Google__ClientSecret, which confirms the OAuth providers that are wired in. Then Minio__Endpoint, Minio__AccessKey, Minio__SecretKey and Minio__BucketName for object storage, and MaxMind__AccountId and MaxMind__LicenseKey for what is presumably a GeoIP or location service.

The two ports published are 5005 to 80 and 5006 to 443, and the service depends on the sqldb service with a restart policy of on-failure, on a network called shared_bridge.

The Dockerfile shows the .NET 10 base and a few decisions that are more than boilerplate. The base image is mcr.microsoft.com/dotnet/aspnet:10.0, and the apt configuration handles both the DEB822 sources format used on trixie and the traditional sources.list used on bookworm, because the contrib component is needed for ttf-mscorefonts-installer. The fonts installed are the Microsoft core fonts and fonts-noto-cjk, and fontconfig is there to make them usable.

The certificate handling is worth noting for a local deployment. The image generates a self-signed certificate at build time with openssl, exporting a PKCS#12 file, and then points Kestrel at it with ASPNETCORE_Kestrel__Certificates__Default__Path and a password placeholder. So the container serves HTTPS out of the box with a certificate that is valid for localhost and nothing else, which is exactly right for a development compose file and exactly wrong for a production deployment, and the fact that the password is a literal CREDENTIAL_PLACEHOLDER makes that boundary obvious rather than hidden.

Where this template is the wrong choice

The first wrong case is a small application. A team building a single-feature tool or a simple internal page does not need four layers, MediatR, a dynamic fields subsystem, multi-tenancy and a code generation extension. The layering is the point of the template and also the reason it is heavy, and a small project that adopts it inherits a structure it will spend its life working around.

The second wrong case is a hosting model that is not Blazor Server. The template is built on SignalR, so the server holds a circuit per connected user. That is a good model for an internal application with a modest number of concurrent users on a corporate network, and a poor one for a public site with thousands of concurrent users, where the model is usually Blazor WebAssembly with static hosting or a separate JavaScript front end. Choosing this template is choosing the connection model with it.

The third wrong case is a team that has already settled its architecture. Clean Architecture has more than one interpretation, and the note about the diagram not representing compile-time project references is a hint that the implementation may not be the strictest reading. A team with its own conventions may spend more time reconciling than they save, and the template is presented as a reference implementation as much as a starting point, so a team that reads it as a standard to conform to will find the deviations.

The fourth wrong case is a project that does not want the AI and object storage integrations in the credential surface. The compose file expects Gemini and OpenAI keys, Minio credentials, MaxMind keys, and SMTP settings before the application starts. That is a lot of configuration for a team that only needs a database.

How it differs from starting from the Blazor template

The comparison worth making is with dotnet new blazor, and the difference in approach is what arrives pre-decided.

The stock template gives you a single project with a component or two, a minimal API or an interactive server component. From there, a team adds a data layer, a service layer, authentication, and the rest, and the structure that emerges is whatever the team happened to build. The layering is an emergent property, and it is usually consistent within a codebase and usually different between codebases.

This template makes the layering a given. The domain layer has no dependencies, the application layer owns the interfaces and the DTOs, infrastructure holds the database, the email and the file system, and the UI holds the Blazor components. The MediatR in the application layer means every use case is a request and a handler, which is a convention you can enforce in a code review and which a test can exercise without any infrastructure at all.

The second difference is the enterprise surface. Multi-tenancy, an advanced data grid with export, a Visual Studio code generation extension, Docker, PWA support, an event log, hosted background services with in-memory queues, and a caching layer with Redis. None of that is hard, and all of it is work you would otherwise do, and the work is the same on every project.

The third difference is the dynamic fields subsystem, which is the one genuinely project specific idea. Adding a column to a table for every field a customer wants is the standard failure of a product that has per customer customisation, and the template's answer is to keep the field definitions in data and the values in a normalised set of rows. The cost is that every query against an entity with dynamic fields has to join, and that validation moves from the schema into code that interprets the definitions, and the README's own modelling effort shows that is a real cost rather than a free win.

The fourth difference is the reference framing. The README says the solution is intended to serve both as a reference implementation and as a ready-to-use foundation, and those are two different products. A reference implementation is most useful when it is read, and a foundation is most useful when it is deleted and replaced. The template is arguably strongest in the first role.

Editorial conclusion

This template fits a team building an internal enterprise application on .NET 10 that wants the Clean Architecture layer boundaries, the code generation and the Docker and multi-tenancy plumbing already in place rather than argued from scratch, and it fits a team that needs custom attributes on business entities without a schema change per attribute. It does not fit a small CRUD application, since the layering and the enterprise features are the majority of the code, and it does not fit a team that wants a WebAssembly or static hosting model, because the template is Blazor Server with SignalR. Before adopting it, read the architecture diagram note about runtime calls versus compile-time references, confirm the target database provider in the compose file, and plan for the fact that the AI settings and the code generation extension are wired to specific providers.

Frequently asked questions

What layers does the Clean Architecture Blazor Server template use?

Four. Domain holds core business entities and rules with no dependencies, Application holds business logic, interfaces and DTOs, Infrastructure holds external concerns such as the database, email and file system, and Server.UI holds the Blazor components and the user interface.

What is the dynamic fields subsystem?

It adds template-driven attributes to selected business entities without adding a column for every custom field. FieldDefinition describes a reusable field and its validation metadata, FieldGroupTemplate organizes definitions into ordered sections, FieldGroupInstance binds a record to a template, FieldValue stores one normalised string value, and ExtensibleEntity exposes the DynamicFields navigation.

What technology stack does the template use?

Blazor Server with MudBlazor and SignalR on the front end, .NET 10 with ASP.NET Core, MediatR and FluentValidation on the back end, Entity Framework Core against MSSQL, PostgreSQL or SQLite, ASP.NET Core Identity with OAuth 2.0 and JWT, FusionCache with Redis, hosted services with in-memory queues, xUnit with FluentAssertions and Moq for testing, and Docker with GitHub Actions for DevOps.

How do I run the template with Docker?

The compose file defines a dashboard service built from the repository Dockerfile and a sqldb service. The dashboard publishes ports 5005 and 5006, and reads its configuration from environment variables using the double underscore convention, including DatabaseSettings__ConnectionString, AISettings__OpenAIApiKey, SmtpClientOptions__Host and Authentication__Google__ClientId.

What is the latest release of the Clean Architecture Blazor Server template?

The tagged release is v1.2 from 2026-09-03, preceded by 1.1.47 from 2024-11-13 and 1.1.44 from 2024-11-07. The last push to main was on 2026-10-05, and the project is MIT licensed with a live demo at architecture.blazorserver.com.

Official sources

  1. License: MIT
  2. neozhu/CleanArchitectureWithBlazorServer on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/neozhu-cleanarchitecturewithblazorserver.svg)](https://hysenlabs.com/projects/neozhu-cleanarchitecturewithblazorserver)

Was this useful?