Compose the application in Program.cs
Indotalent’s Blazor CRM Source Code is built as one ASP.NET Core .NET 10 project. Program.cs registers the shared services, adds Blazor Server with MudBlazor, mounts Minimal API endpoints under /api, and applies middleware in a short, readable order. The UI framework is registered once for the whole app:
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddMudServices();
Razor pages are hosted from the /Features root directory, which keeps feature folders and route URLs aligned. Swagger is added for the API and, in the inspected setup, the swagger endpoint itself requires authorization.
Register services in dependency groups
Rather than hundreds of registrations in Program.cs, the app delegates to extension methods:
builder.Services.AddConfigBackEndDI();
builder.Services.AddInfrastructureDI(builder.Configuration);
builder.Services.AddConfigFrontEndDI();
builder.Services.AddFeaturesDI();
AddConfigBackEndDI registers MediatR with its pipeline behaviors and scans the assembly for FluentValidation validators. AddInfrastructureDI reads strongly typed settings sections such as DatabaseSettings and IdentitySettings and adds logging, database, authentication, file, email, auto-number, and background-job services. AddConfigFrontEndDI registers the shared Blazor client plumbing, and AddFeaturesDI registers the per-feature HTTP services used by the pages.
Choose a database provider at startup
The database service inspects DatabaseSettings and registers AppDbContext with the provider whose IsUsed flag is enabled. SQL Server is checked first, then PostgreSQL, then MySQL, and each provider package is referenced in the project file. Connection strings and command timeouts come from the same settings section.
AppDbContext itself centralizes CRM-relevant behavior: it registers the pipeline DbSets, applies the per-entity configurations from its own assembly, installs the soft-delete query filter for base entities, and stamps audit fields and document numbers during SaveChangesAsync. Because the context derives from IdentityDbContext, the ASP.NET Core Identity tables share the same database.
Authenticate the app and the API
Two authentication paths work side by side. Interactive Blazor Server uses the ASP.NET Core Identity cookie, with login and logout paths configured from settings. The Minimal API endpoints require the explicit JWT bearer scheme, and the login flow issues a bearer token that the browser keeps in an HttpOnly cookie used by the client services. Claims and refresh tokens are handled by account endpoints under /api/account.
The roles used by the UI, such as Admin and Member, come from the shared Identity role store. This split is worth understanding before you extend authorization: a page can be gated by a role attribute while an API endpoint is gated by bearer authentication alone.
Mount the HTTP boundary
Every feature endpoint maps under one root group that receives the current-user filter:
var apiGroup = app.MapGroup("/api").AddEndpointFilter<CurrentUserFilter>();
apiGroup.MapAccountEndpoints();
apiGroup.MapFeaturesEndpoint();
The filter resolves the user from the authenticated principal, or from the forwarded headers, and fills the scoped current-user service used for audit columns. Each feature then builds its own group, for example /lead, with JWT bearer authorization, so the UI and any external client cross the same HTTP boundary with the same envelope contract.
Handle errors and status pages
An exception middleware wraps unhandled errors before they reach the response. It maps typed exceptions to status codes and serializes an ApiResponse body with the failure message and errors, so API consumers always receive a consistent shape even on failure. Non-development builds enable the exception page and HSTS, and status-code pages handle missing routes such as the not-found page.
Initialize the database and seed demo data
At startup the app checks whether a database provider is active. If it is, it creates a scope, resolves AppDbContext, calls EnsureCreated, and runs DatabaseSeeder. The seeder creates the default administrator account, and the demo seeder folder under Infrastructure/Database/Demo populates CRM rows for campaigns, sales teams, representatives, leads, contacts, activities, budgets, and expenses so the pipeline screens are usable immediately after first run.
Using EnsureCreated means the schema is created from the model without migrations. If your deployment process requires migration-based schema evolution, that is a deliberate change from the inspected startup path.
Follow the request path through the app
A typical CRM request starts in a MudBlazor form, calls a typed service over HTTP to /api/lead, passes through the current-user filter and bearer authentication, reaches the endpoint, and is dispatched to a MediatR handler that reads and writes through AppDbContext. Shared database behavior, response envelopes, and authentication apply along the way. That path is traced step by step in CRM Vertical Slice Architecture. For the module inventory and data model behind the composition, start with the source-code evaluation and the schema article. See this architecture implemented in a complete Blazor CRM application.