Comprehensive documentation for the DbFirst solution, detailing project goals, architecture (Clean Architecture), technology stack, solution/project structure, Database-First approach, CQRS with MediatR, API endpoints, Blazor/DevExpress frontend, dashboard and layout persistence, SignalR integration, configuration, development setup, architecture decisions (ADRs), extensibility, and glossary. Includes diagrams, code snippets, and workflow guidance for the developer team.
47 KiB
DbFirst – Projektdokumentation
Version: 1.0
Stand: Juli 2025
Repository:https://git.dd/AppStd/DbFirst(Branch:main)
Zielgruppe: Entwickler-Team
Inhaltsverzeichnis
- Projektziel & Motivation
- Technologie-Stack
- Architekturübersicht (Clean Architecture)
- Solution-Struktur
- Projekt-Details
- 5.1 DbFirst.Domain
- 5.2 DbFirst.Application
- 5.3 DbFirst.Infrastructure
- 5.4 DbFirst.API
- 5.5 DbFirst.BlazorWebApp
- Abhängigkeiten zwischen Projekten
- Database-First-Ansatz & Scaffold-Befehl
- Datenbank-Architektur
- CQRS-Pattern (MediatR)
- API-Endpunkte
- Blazor-Frontend & DevExpress
- Dashboard-System (DevExpress Dashboards)
- Layout-Persistierung
- Echtzeit-Kommunikation (SignalR)
- Konfiguration
- Lokale Entwicklungsumgebung
- Architektur-Entscheidungen (ADRs)
- Erweiterbarkeit & nächste Schritte
- Glossar
1. Projektziel & Motivation
Ausgangslage
Es existiert eine bestehende Produktions-Datenbank (DD_ECM) mit etablierten Tabellen, Views und Stored Procedures. Die bisherige Software, die auf diese Datenbank zugreift, soll durch eine moderne Webanwendung abgelöst werden.
Ziele
| # | Ziel | Beschreibung |
|---|---|---|
| 1 | Backend-Modernisierung | EF Core im Database-First-Ansatz auf einer bestehenden Produktions-DB einsetzen. Kein Code-First, keine Migrationen – die Datenbank bleibt die „Source of Truth". |
| 2 | Clean Architecture & DDD | Strukturierung des Backends nach Prinzipien der Clean Architecture und Domain-Driven Design mit klar getrennten Schichten. |
| 3 | Frontend-Evaluation | Blazor Web App (.NET 8) in Kombination mit DevExpress Blazor evaluieren – Grids, Dashboards, Theming – um den Fit für den produktiven Einsatz zu testen. |
| 4 | Best Practices | CQRS via MediatR, AutoMapper, Repository-Pattern, globales Exception Handling, CORS-Konfiguration, Echtzeit-Updates via SignalR. |
Nicht-Ziele (bewusst ausgeklammert)
- EF Core Migrations / Code-First
- Authentifizierung & Autorisierung (noch nicht implementiert)
- Deployment / CI/CD Pipeline (noch nicht konfiguriert)
2. Technologie-Stack
Backend
| Technologie | Version | Zweck |
|---|---|---|
| .NET | 8.0 | Runtime & SDK |
| ASP.NET Core Web API | 8.0 | REST-API |
| Entity Framework Core | 8.0.22 | ORM (Database-First) |
| Microsoft.Data.SqlClient | 5.2.1 | SQL Server Zugriff (Stored Procedures) |
| MediatR | 11.1.0 | CQRS-Pattern |
| AutoMapper | 12.0.1 | Entity ? DTO Mapping |
| Swashbuckle (Swagger) | 6.6.2 | API-Dokumentation |
| DevExpress Dashboard (ASP.NET Core) | 25.2.3 | Server-seitiges Dashboard-Backend |
| SignalR | 8.0.22 | Echtzeit-Benachrichtigungen |
Frontend
| Technologie | Version | Zweck |
|---|---|---|
| Blazor Web App | .NET 8 | UI-Framework (Interactive Server Rendering) |
| DevExpress Blazor | 25.2.3 | UI-Komponentenbibliothek (Grid, Forms, etc.) |
| DevExpress Blazor Dashboard | 25.2.3 | Dashboard-Viewer & -Designer im Frontend |
| DevExpress Blazor Themes (Fluent) | 25.2.3 | Theming (Light/Dark Mode) |
| SignalR Client | 8.0.22 | Echtzeit-Empfang |
Datenbank
| Technologie | Zweck |
|---|---|
| Microsoft SQL Server | Produktionsdatenbank (DD_ECM) |
Views (VWMY_CATALOG) |
Leseoperationen |
Stored Procedures (PRTBMY_CATALOG_*) |
Schreiboperationen (Insert, Update, Delete) |
3. Architekturübersicht (Clean Architecture)
????????????????????????????????????????????????????????????????
? Präsentationsschicht ?
? ??????????????????????? ???????????????????????????? ?
? ? DbFirst.BlazorWebApp? ? DbFirst.API ? ?
? ? (Blazor Frontend) ?????? (ASP.NET Core Web API) ? ?
? ? Port: 7191 ?HTTP? Port: 7204 ? ?
? ??????????????????????? ???????????????????????????? ?
? ? ?
????????????????????????????????????????????????????????????????
? Anwendungsschicht ? ?
? ?????????????????????????????????????????????????????? ?
? ? DbFirst.Application ? ?
? ? Commands, Queries, Handlers (MediatR/CQRS) ? ?
? ? DTOs, AutoMapper Profiles, Repository-Interfaces ? ?
? ?????????????????????????????????????????????????????? ?
? ? ?
????????????????????????????????????????????????????????????????
? Domänenschicht ? ?
? ?????????????????????????????????????????????????????? ?
? ? DbFirst.Domain ? ?
? ? Entities, Enums (keine Abhängigkeiten!) ? ?
? ?????????????????????????????????????????????????????? ?
? ?
????????????????????????????????????????????????????????????????
? Infrastrukturschicht ?
? ?????????????????????????????????????????????????????? ?
? ? DbFirst.Infrastructure ? ?
? ? DbContext, Repository-Implementierungen, ? ?
? ? EF Core Konfiguration, DB-Zugriff ? ?
? ?????????????????????????????????????????????????????? ?
? ? ?
? ??????????? ?
? ? SQL ? ?
? ? Server ? ?
? ? (DD_ECM)? ?
? ??????????? ?
????????????????????????????????????????????????????????????????
Dependency Rule (Abhängigkeitsregel)
Die zentrale Regel der Clean Architecture wird eingehalten:
- Domain hat keine Abhängigkeiten zu anderen Projekten.
- Application kennt nur Domain (und definiert Interfaces).
- Infrastructure implementiert die Interfaces aus Application und referenziert Domain + Application.
- API orchestriert alles und referenziert Application, Infrastructure und Domain.
- BlazorWebApp kommuniziert ausschließlich via HTTP mit der API – kein direkter Zugriff auf Domain, Application oder Infrastructure.
4. Solution-Struktur
DbFirst/ ? Solution-Root
?
??? docs/
? ??? PROJEKTDOKUMENTATION.md ? Diese Datei
?
??? DbFirst.Domain/ ? Domänenschicht (Kern)
? ??? Entities/
? ? ??? VwmyCatalog.cs ? Katalog-Entity (aus VWMY_CATALOG View)
? ? ??? SmfLayout.cs ? Layout-Entity (TBDD_SMF_LAYOUT Tabelle)
? ? ??? Massdata.cs ? MassData-Entity (MASSDATA Tabelle)
? ??? CatalogUpdateProcedure.cs ? Enum: Update vs. Save
? ??? DbFirst.Domain.csproj
?
??? DbFirst.Application/ ? Anwendungsschicht
? ??? Catalogs/
? ? ??? CatalogReadDto.cs ? Read-DTO
? ? ??? CatalogWriteDto.cs ? Write-DTO (inkl. UpdateProcedure)
? ? ??? CatalogProfile.cs ? AutoMapper-Profil
? ? ??? Commands/
? ? ? ??? CreateCatalogCommand.cs ? MediatR Command
? ? ? ??? CreateCatalogHandler.cs ? MediatR Handler
? ? ? ??? UpdateCatalogCommand.cs
? ? ? ??? UpdateCatalogHandler.cs
? ? ? ??? DeleteCatalogCommand.cs
? ? ? ??? DeleteCatalogHandler.cs
? ? ??? Queries/
? ? ??? GetAllCatalogsQuery.cs
? ? ??? GetAllCatalogsHandler.cs
? ? ??? GetCatalogByIdQuery.cs
? ? ??? GetCatalogByIdHandler.cs
? ??? MassData/
? ? ??? MassDataReadDto.cs
? ? ??? MassDataWriteDto.cs
? ? ??? MassDataProfile.cs
? ? ??? Commands/
? ? ? ??? UpsertMassDataByCustomerNameCommand.cs
? ? ? ??? UpsertMassDataByCustomerNameHandler.cs
? ? ??? Queries/
? ? ??? GetAllMassDataQuery.cs ? Paginierung (Skip/Take)
? ? ??? GetAllMassDataHandler.cs
? ? ??? GetMassDataCountQuery.cs
? ? ??? GetMassDataCountHandler.cs
? ? ??? GetMassDataByCustomerNameQuery.cs
? ? ??? GetMassDataByCustomerNameHandler.cs
? ??? Repositories/
? ? ??? IRepository.cs ? Generisches Basis-Interface
? ? ??? ICatalogRepository.cs ? Erweitert IRepository<VwmyCatalog>
? ? ??? IMassDataRepository.cs ? Eigenes Interface (kein IRepository)
? ? ??? ILayoutRepository.cs ? Eigenes Interface
? ??? DependencyInjection.cs ? AddApplication() Extension
? ??? DbFirst.Application.csproj
?
??? DbFirst.Infrastructure/ ? Infrastrukturschicht
? ??? ApplicationDbContext.cs ? EF Core DbContext (Catalog, Layout)
? ??? MassDataDbContext.cs ? EF Core DbContext (MassData)
? ??? TableConfigurations.cs ? Konfigurierbare Tabellen-/Spaltennamen
? ??? Repositories/
? ? ??? CatalogRepository.cs ? Stored Procedure basiert
? ? ??? MassDataRepository.cs ? Stored Procedure basiert
? ? ??? LayoutRepository.cs ? EF Core basiert (CRUD)
? ??? DependencyInjection.cs ? AddInfrastructure() Extension
? ??? DbFirst.Infrastructure.csproj
?
??? DbFirst.API/ ? Web API (Startprojekt Backend)
? ??? Controllers/
? ? ??? CatalogsController.cs ? CRUD via MediatR
? ? ??? MassDataController.cs ? Paginiertes Lesen + Upsert
? ? ??? LayoutsController.cs ? Grid-Layout CRUD
? ? ??? DefaultDashboardController.cs ? DevExpress Dashboard-Controller
? ??? Dashboards/
? ? ??? SqlDashboardStorage.cs ? Dashboard-Persistierung in SQL
? ? ??? IDashboardChangeNotifier.cs ? Interface für SignalR-Benachrichtigung
? ? ??? DashboardChangeNotifier.cs ? SignalR-basierte Implementierung
? ??? Hubs/
? ? ??? DashboardsHub.cs ? SignalR Hub für Dashboard-Updates
? ??? Middleware/
? ? ??? ExceptionHandlingMiddleware.cs ? Globales Exception Handling
? ??? Program.cs ? DI, Middleware, Routing
? ??? appsettings.json ? Connection Strings, CORS, etc.
? ??? DbFirst.API.csproj
?
??? DbFirst.BlazorWebApp/ ? Blazor Frontend (Startprojekt Frontend)
? ??? Components/
? ? ??? App.razor ? Root-Komponente, DevExpress Themes
? ? ??? Routes.razor ? Routing
? ? ??? _Imports.razor ? Globale Using-Direktiven
? ? ??? Layout/
? ? ? ??? MainLayout.razor ? Hauptlayout (Sidebar + Content)
? ? ? ??? MainLayout.razor.css
? ? ? ??? NavMenu.razor ? Navigation (Home, Catalogs, etc.)
? ? ? ??? NavMenu.razor.css
? ? ??? Pages/
? ? ? ??? Home.razor ? Startseite mit DxCarousel
? ? ? ??? Catalogs.razor ? Katalog-Seite
? ? ? ??? Massdata.razor ? MassData-Seite
? ? ? ??? Dashboard.razor ? Dashboard Viewer/Designer
? ? ? ??? Dashboard.razor.css
? ? ? ??? Error.razor ? Fehlerseite
? ? ??? CatalogsGrid.razor ? Katalog-Grid-Komponente
? ? ??? CatalogsGrid.razor.css
? ? ??? MassDataGrid.razor ? MassData-Grid-Komponente
? ? ??? MassDataGrid.razor.css
? ??? Models/
? ? ??? CatalogReadDto.cs ? Frontend-DTO (Spiegelung der API)
? ? ??? CatalogWriteDto.cs
? ? ??? MassDataReadDto.cs
? ? ??? MassDataWriteDto.cs
? ? ??? LayoutDto.cs
? ? ??? DashboardInfoDto.cs
? ? ??? Grid/
? ? ??? BandLayoutModels.cs ? Band-/Spalten-Layout-Modelle
? ??? Services/
? ? ??? CatalogApiClient.cs ? Typisierter HttpClient (Catalog)
? ? ??? MassDataApiClient.cs ? Typisierter HttpClient (MassData)
? ? ??? DashboardApiClient.cs ? Typisierter HttpClient (Dashboard)
? ? ??? LayoutApiClient.cs ? Typisierter HttpClient (Layout)
? ? ??? BandLayoutService.cs ? Band-Layout Logik (Laden/Speichern)
? ? ??? ThemeState.cs ? Light/Dark Mode State
? ??? Program.cs ? DI, Middleware, Routing
? ??? appsettings.json ? ApiBaseUrl
? ??? DbFirst.BlazorWebApp.csproj
?
??? DbFirst.sln ? Solution-Datei
5. Projekt-Details
5.1 DbFirst.Domain
Zweck: Enthält die Domänen-Entities und Enums – der innerste Kern der Architektur.
Abhängigkeiten: ? Keine (reines .NET 8 Class Library)
Entities
| Entity | Datenbank-Objekt | Beschreibung |
|---|---|---|
VwmyCatalog |
View VWMY_CATALOG |
Katalog-Einträge mit Titel, Kennung, Audit-Feldern |
SmfLayout |
Tabelle TBDD_SMF_LAYOUT |
Persistierte Grid-Layouts pro User |
Massdata |
Tabelle MASSDATA |
Massendaten mit Kundenname, Betrag, Kategorie, Status |
Enums
| Enum | Werte | Beschreibung |
|---|---|---|
CatalogUpdateProcedure |
Update = 0, Save = 1 |
Steuert, welche Stored Procedure beim Update aufgerufen wird |
Design-Entscheidungen
- Entities werden durch den EF Core Scaffold generiert und bei Bedarf manuell angepasst.
VwmyCatalogistpartial class– dadurch kann der Scaffold wiederholt werden, ohne manuelle Erweiterungen zu verlieren (sofern diese in einer separaten Partial-Datei liegen).- Entity-Properties verwenden die C#-Namen (PascalCase), das Mapping auf DB-Spaltennamen (UPPER_SNAKE_CASE) erfolgt im
DbContext.
5.2 DbFirst.Application
Zweck: Enthält die Geschäftslogik in Form von CQRS Commands/Queries (MediatR), DTOs und AutoMapper-Profile. Definiert die Repository-Interfaces (Dependency Inversion).
Abhängigkeiten: DbFirst.Domain
Packages
| Package | Zweck |
|---|---|
| MediatR.Extensions.Microsoft.DependencyInjection | CQRS Handler-Registrierung |
| AutoMapper / AutoMapper.Extensions.Microsoft.DependencyInjection | Entity ? DTO Mapping |
Struktur nach Feature-Ordner
Application/
??? Catalogs/ ? Feature: Katalogverwaltung
? ??? Commands/ ? Schreiboperationen (Create, Update, Delete)
? ??? Queries/ ? Leseoperationen (GetAll, GetById)
? ??? CatalogReadDto ? Ausgabe-DTO
? ??? CatalogWriteDto ? Eingabe-DTO
? ??? CatalogProfile ? AutoMapper
??? MassData/ ? Feature: Massendaten
? ??? Commands/ ? Upsert
? ??? Queries/ ? GetAll (paginiert), GetCount, GetByCustomerName
? ??? MassDataReadDto
? ??? MassDataWriteDto
? ??? MassDataProfile
??? Repositories/ ? Interface-Definitionen
??? IRepository<T> ? Generisches Basis-Interface
??? ICatalogRepository
??? IMassDataRepository
??? ILayoutRepository
Repository-Interfaces
IRepository<T> – Generisches Basis-Interface:
public interface IRepository<T>
{
Task<List<T>> GetAllAsync(CancellationToken ct);
Task<T?> GetByIdAsync(int id, CancellationToken ct);
Task<T> InsertAsync(T entity, CancellationToken ct);
Task<T?> UpdateAsync(int id, T entity, CancellationToken ct);
Task<bool> DeleteAsync(int id, CancellationToken ct);
}
ICatalogRepository – Erweitert das generische Interface um spezifische Methoden:
public interface ICatalogRepository : IRepository<VwmyCatalog>
{
Task<VwmyCatalog?> GetByTitleAsync(string title, CancellationToken ct);
Task<VwmyCatalog?> UpdateAsync(int id, VwmyCatalog catalog, CatalogUpdateProcedure procedure, CancellationToken ct);
}
IMassDataRepository und ILayoutRepository nutzen bewusst nicht IRepository<T>, da deren Operationen stark abweichen (Upsert statt Insert, Suche nach Composite Keys statt int-ID).
DI-Registrierung
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddAutoMapper(typeof(DependencyInjection).Assembly);
services.AddMediatR(typeof(DependencyInjection).Assembly);
return services;
}
5.3 DbFirst.Infrastructure
Zweck: Implementiert die Repository-Interfaces, enthält die EF Core DbContexts und das Datenbank-Mapping.
Abhängigkeiten: DbFirst.Domain, DbFirst.Application
DbContexts
Das Projekt verwendet zwei separate DbContexts:
| DbContext | Datenbank | Verbindung | Entities |
|---|---|---|---|
ApplicationDbContext |
DD_ECM (SQL17) |
DefaultConnection |
VwmyCatalog, SmfLayout |
MassDataDbContext |
DD_ECM (SQL19) |
MassDataConnection |
Massdata |
Begründung für zwei DbContexts: Die Entities liegen auf verschiedenen SQL-Server-Instanzen (SQL17 vs. SQL19).
Konfigurierbare Tabellen-/Spaltennamen (TableConfigurations)
Die Spaltennamen der VwmyCatalog-View sind über appsettings.json konfigurierbar:
"TableConfigurations": {
"VwmyCatalog": {
"ViewName": "VWMY_CATALOG",
"GuidColumnName": "GUID",
"CatTitleColumnName": "CAT_TITLE",
...
}
}
Dies erlaubt es, die Anwendung ohne Code-Änderung auf andere DB-Schemata zu zeigen.
Repository-Implementierungen
| Repository | Lese-Strategie | Schreib-Strategie |
|---|---|---|
CatalogRepository |
EF Core via View (AsNoTracking) |
Stored Procedures (PRTBMY_CATALOG_INSERT, _UPDATE, _SAVE, _DELETE) |
MassDataRepository |
EF Core via Tabelle (AsNoTracking) mit Paginierung |
Stored Procedure (PRMassdata_UpsertByCustomerName) |
LayoutRepository |
EF Core via Tabelle | EF Core direkt (Add, SaveChanges, Remove) |
Wichtig: Bei CatalogRepository und MassDataRepository werden Stored Procedures mit Output-Parametern verwendet. Nach der SP-Ausführung wird der geänderte Datensatz erneut aus der View/Tabelle geladen, um konsistente Daten zurückzugeben.
DI-Registrierung
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<TableConfigurations>(configuration.GetSection("TableConfigurations"));
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<MassDataDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("MassDataConnection")));
return services;
}
5.4 DbFirst.API
Zweck: REST-API, die als Backend für das Blazor-Frontend dient. Orchestriert DI, Middleware, Routing.
Abhängigkeiten: DbFirst.Domain, DbFirst.Application, DbFirst.Infrastructure
Controller-Übersicht
| Controller | Route | Beschreibung |
|---|---|---|
CatalogsController |
api/catalogs |
CRUD für Kataloge via MediatR |
MassDataController |
api/massdata |
Paginiertes Lesen + Upsert via MediatR |
LayoutsController |
api/layouts |
Grid-Layout-Verwaltung (direkt via Repository) |
DefaultDashboardController |
api/dashboard |
DevExpress Dashboard-Backend |
Middleware
ExceptionHandlingMiddleware: Fängt alle unbehandelten Exceptions, loggt sie und gibt ein standardisiertes JSON-Problem-Details-Objekt zurück (RFC 9110).
CORS-Konfiguration
Development: AllowAnyOrigin, AllowAnyHeader, AllowAnyMethod
Production: Nur explizit konfigurierte Origins aus appsettings.json
Wenn keine Origins konfiguriert ? alles blockiert
Dashboard-System
Die API hostet das DevExpress Dashboard Backend und bietet:
SqlDashboardStorage: Speichert Dashboards als XML-Blob in einer SQL-Tabelle (TBDD_SMF_CONFIG).- Default-Dashboards: Werden beim Start automatisch erstellt, wenn sie nicht in der DB existieren (
DefaultDashboard,CatalogsGrid). - JSON Data Sources: API-Endpunkte werden als Dashboard-Datenquellen registriert.
- SignalR Hub (
/hubs/dashboards): Benachrichtigt alle Clients bei Dashboard-Änderungen.
5.5 DbFirst.BlazorWebApp
Zweck: Blazor Web App als Frontend. Kommuniziert ausschließlich via HTTP mit der API. Kein direkter DB-Zugriff.
Abhängigkeiten: Keine Projekt-Referenzen! Nur NuGet-Packages. Kommunikation mit dem Backend über HttpClient.
Render-Modus
Die Anwendung nutzt Interactive Server Rendering (@rendermode InteractiveServer). Der gesamte UI-State lebt auf dem Server, der Browser erhält Updates via SignalR (Blazor-intern).
Seiten
| Seite | Route | Beschreibung |
|---|---|---|
Home.razor |
/ |
Startseite mit DevExpress Carousel (Architektur-Diagramme) |
Catalogs.razor |
/catalogs |
Einbettung der CatalogsGrid-Komponente |
Massdata.razor |
/massdata |
Einbettung der MassDataGrid-Komponente |
Dashboard.razor |
/dashboards/{Id?} |
DevExpress Dashboard Viewer/Designer |
Error.razor |
/Error |
Fehlerseite |
Grid-Komponenten
Beide Grid-Komponenten (CatalogsGrid.razor, MassDataGrid.razor) bieten:
| Feature | Beschreibung |
|---|---|
| DevExpress DxGrid | Sortierung, Gruppierung, Filterung, Spalten-Resize, Spalten-Reorder |
| CRUD via Popup-EditForm | Inline-Bearbeitung mit Validierung |
| Band-Columns | Spalten können dynamisch in Bänder gruppiert werden |
| Layout-Persistierung | Spaltenbreiten, Reihenfolge, Bänder, Grid-Layout werden pro User in der DB gespeichert |
| Size-Mode-Wechsel | Klein/Mittel/Groß via Toolbar-Dropdown |
| Paginierung (MassData) | Serverseitige Paginierung mit konfigurierbarer Seitengröße (100, 1.000, 10.000, 100.000, Alle) |
Typisierte HTTP-Clients
| Client | API-Endpunkt | Beschreibung |
|---|---|---|
CatalogApiClient |
api/catalogs |
Vollständiges CRUD mit Fehlerbehandlung |
MassDataApiClient |
api/massdata |
Paginiertes Laden, Upsert, Suche |
DashboardApiClient |
api/dashboard/dashboards |
Dashboard-Liste laden |
LayoutApiClient |
api/layouts |
Grid-Layouts laden/speichern/löschen |
Alle Clients verwenden die IHttpClientFactory und werden via AddHttpClient<T>() registriert. Die ApiBaseUrl wird aus appsettings.json gelesen.
Fehlerbehandlung im Frontend
CatalogApiClient implementiert ein ApiResult<T> Pattern:
public record ApiResult<T>(bool Success, T? Value, string? Error);
HTTP-Fehler werden in benutzerfreundliche deutsche Meldungen übersetzt (z.B. 409 ? „Datensatz existiert bereits").
Theming
ThemeState verwaltet Light/Dark Mode mit DevExpress Fluent Theme:
var theme = Themes.Fluent.Clone(properties =>
{
properties.Mode = isDarkMode ? ThemeMode.Dark : ThemeMode.Light;
properties.ApplyToPageElements = true;
});
themeChangeService.SetTheme(theme);
Der Toggle-Button befindet sich im MainLayout.razor in der Top-Bar.
6. Abhängigkeiten zwischen Projekten
????????????????????
? DbFirst.Domain ?
? (keine Deps) ?
????????????????????
?
????????????????????
? DbFirst. ?
? Application ?
? (? Domain) ?
????????????????????
?
???????????????????????????????
? ? ?
???????????????? ?????????????? ?
? DbFirst. ? ? DbFirst. ? ?
? Infrastructure? ? API ?????????
? (? Domain, ? ? (? Domain, ?
? Application)? ? Application,?
???????????????? ? Infra) ?
??????????????
????????????????????
? DbFirst. ?
? BlazorWebApp ?
? (KEINE Projekt- ? HTTP
? Referenzen) ? ???????????? DbFirst.API
????????????????????
Wichtig: Das BlazorWebApp-Projekt hat bewusst keine Projekt-Referenz zu irgendeinem anderen Projekt. Es kommuniziert ausschließlich über HTTP mit der API. Dadurch können Frontend und Backend unabhängig deployed werden.
7. Database-First-Ansatz & Scaffold-Befehl
Philosophie
Die bestehende Produktionsdatenbank ist die Source of Truth. Es werden keine EF Core Migrations verwendet. Stattdessen wird der dotnet ef dbcontext scaffold Befehl verwendet, um Entities und DbContext aus der bestehenden Datenbank zu generieren.
Scaffold-Befehl (Allgemein)
dotnet ef dbcontext scaffold "<ConnectionString>" Microsoft.EntityFrameworkCore.SqlServer `
--startup-project <StartupProjekt/Startup.csproj> `
--project <Projektpfad/Projektname.csproj> `
--context <DbContextName> `
--context-dir <KontextOrdner> `
--output-dir <EntitiesOrdner> `
--table <Tabellenname> `
--use-database-names `
--no-onconfiguring `
--force
Parameter-Erklärung
| Parameter | Beschreibung |
|---|---|
--startup-project |
Das Startprojekt (muss die Microsoft.EntityFrameworkCore.Design-Tools enthalten) – hier: DbFirst.API |
--project |
Das Zielprojekt, in dem der DbContext generiert wird – hier: DbFirst.Infrastructure |
--context |
Name der generierten DbContext-Klasse |
--context-dir |
Zielordner für den DbContext relativ zum --project |
--output-dir |
Zielordner für die Entities relativ zum --project (hier: ../DbFirst.Domain/Entities ? Domain-Projekt!) |
--table |
Optional: Nur bestimmte Tabelle(n) scaffolden, sonst alle |
--use-database-names |
Spaltennamen exakt aus der DB übernehmen (kein PascalCase-Mapping) |
--no-onconfiguring |
Kein Connection String in der OnConfiguring-Methode generieren (wir nutzen DI) |
--force |
Bestehende Dateien überschreiben (ab dem 2. Scaffold-Lauf) |
Konkretes Beispiel
dotnet ef dbcontext scaffold "Server=SDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;TrustServerCertificate=True;" Microsoft.EntityFrameworkCore.SqlServer `
--startup-project DbFirst.API `
--project DbFirst.Infrastructure `
--context ApplicationDbContext `
--context-dir . `
--output-dir ../DbFirst.Domain/Entities `
--table dbo.VWMY_CATALOG `
--use-database-names `
--no-onconfiguring `
--force
Workflow nach dem Scaffold
- Scaffold ausführen ? Entities in
DbFirst.Domain/Entities/, DbContext inDbFirst.Infrastructure/. - DbContext anpassen: Konfigurierbare Spaltennamen über
TableConfigurationseinbauen, ggf.partialMethoden verwenden. - Entity prüfen: Ggf. Properties umbenennen (PascalCase), das Mapping im DbContext entsprechend anpassen.
- Repository implementieren: In
DbFirst.Infrastructure/Repositories/. - MediatR Handler erstellen: In
DbFirst.Application/. - Controller erstellen: In
DbFirst.API/Controllers/.
Hinweis zu --use-database-names
Dieser Parameter sorgt dafür, dass die DB-Spaltennamen (z.B. CAT_TITLE, ADDED_WHO) als Property-Namen in den Entities übernommen werden. In diesem Projekt wurden die Entities nach dem Scaffold manuell auf PascalCase umbenannt und das Mapping auf die Original-Spaltennamen erfolgt in der OnModelCreating-Methode des DbContext via .HasColumnName().
8. Datenbank-Architektur
Verwendete Datenbank-Objekte
Auf SQL17 (DefaultConnection)
| Objekt-Typ | Name | Beschreibung |
|---|---|---|
| View | VWMY_CATALOG |
Lese-View für Katalogeinträge |
| Stored Procedure | PRTBMY_CATALOG_INSERT |
Neuen Katalog anlegen (Output: @GUID) |
| Stored Procedure | PRTBMY_CATALOG_UPDATE |
Katalog aktualisieren (Titel bleibt, nur String + Audit) |
| Stored Procedure | PRTBMY_CATALOG_SAVE |
Katalog speichern (Titel + String ändern erlaubt) |
| Stored Procedure | PRTBMY_CATALOG_DELETE |
Katalog löschen |
| Tabelle | TBDD_SMF_LAYOUT |
Grid-Layouts speichern (hat DB-Trigger: TBDD_SMF_LAYOUT_AFT_UPD) |
| Tabelle | TBDD_SMF_CONFIG |
Dashboard-Konfigurationen (XML als VARBINARY) |
Auf SQL19 (MassDataConnection)
| Objekt-Typ | Name | Beschreibung |
|---|---|---|
| Tabelle | MASSDATA |
Massendaten |
| Stored Procedure | PRMassdata_UpsertByCustomerName |
Insert oder Update basierend auf CustomerName |
Lese-/Schreib-Trennung
Lesen: EF Core ? View/Tabelle (AsNoTracking)
Schreiben: EF Core ? ExecuteSqlRawAsync ? Stored Procedure
?
Anschließend Reload des Datensatzes aus View/Tabelle
Grund: Die bestehende DB-Logik (Trigger, Berechnungen, Audit-Felder) in den Stored Procedures soll weiterhin greifen. EF Core wird primär als Lese-ORM verwendet.
9. CQRS-Pattern (MediatR)
Übersicht
Die Anwendung nutzt das CQRS-Pattern (Command Query Responsibility Segregation) über MediatR:
Controller ? MediatR.Send(Query/Command) ? Handler ? Repository ? DB
Alle Commands & Queries
Catalogs
| Typ | Klasse | Request | Response |
|---|---|---|---|
| Query | GetAllCatalogsQuery |
– | List<CatalogReadDto> |
| Query | GetCatalogByIdQuery |
int Id |
CatalogReadDto? |
| Command | CreateCatalogCommand |
CatalogWriteDto |
CatalogReadDto? |
| Command | UpdateCatalogCommand |
int Id, CatalogWriteDto |
CatalogReadDto? |
| Command | DeleteCatalogCommand |
int Id |
bool |
MassData
| Typ | Klasse | Request | Response |
|---|---|---|---|
| Query | GetAllMassDataQuery |
int? Skip, int? Take |
List<MassDataReadDto> |
| Query | GetMassDataCountQuery |
– | int |
| Query | GetMassDataByCustomerNameQuery |
string CustomerName |
MassDataReadDto? |
| Command | UpsertMassDataByCustomerNameCommand |
MassDataWriteDto |
MassDataReadDto |
Datenfluss-Beispiel: Katalog anlegen
1. Blazor: Benutzer füllt Formular aus, klickt "Speichern"
2. CatalogsGrid.razor ? CatalogApiClient.CreateAsync(dto)
3. HTTP POST ? api/catalogs
4. CatalogsController.Create() ? _mediator.Send(new CreateCatalogCommand(dto))
5. CreateCatalogHandler:
a. Prüft Duplikat via GetByTitleAsync()
b. Mappt DTO ? Entity (AutoMapper)
c. Ruft repository.InsertAsync() auf
6. CatalogRepository.InsertAsync():
a. EXEC PRTBMY_CATALOG_INSERT mit Output-Parameter @GUID
b. Lädt den Datensatz aus View: SELECT FROM VWMY_CATALOG WHERE GUID = @output
7. Response zurück durch die Schichten ? 201 Created
8. Blazor: Grid wird aktualisiert, neuer Eintrag fokussiert
10. API-Endpunkte
Catalogs (api/catalogs)
| Methode | Route | Beschreibung | Response |
|---|---|---|---|
GET |
/api/catalogs |
Alle Kataloge laden | 200 + List<CatalogReadDto> |
GET |
/api/catalogs/{id} |
Katalog nach ID | 200 / 404 |
POST |
/api/catalogs |
Neuen Katalog anlegen | 201 / 409 (Duplikat) |
PUT |
/api/catalogs/{id} |
Katalog aktualisieren | 200 / 400 / 404 |
DELETE |
/api/catalogs/{id} |
Katalog löschen | 204 / 404 |
Besonderheit PUT: Die UpdateProcedure im DTO steuert, ob PRTBMY_CATALOG_UPDATE (Titel unveränderbar) oder PRTBMY_CATALOG_SAVE (Titel änderbar) aufgerufen wird.
MassData (api/massdata)
| Methode | Route | Beschreibung | Response |
|---|---|---|---|
GET |
/api/massdata/count |
Gesamtanzahl | 200 + int |
GET |
/api/massdata?skip=0&take=100 |
Paginiert laden | 200 + List<MassDataReadDto> |
GET |
/api/massdata/{customerName} |
Nach Kundenname suchen | 200 / 404 |
POST |
/api/massdata/upsert |
Insert oder Update | 200 + MassDataReadDto |
Layouts (api/layouts)
| Methode | Route | Beschreibung | Response |
|---|---|---|---|
GET |
/api/layouts?layoutType=...&layoutKey=...&userName=... |
Layout laden | 200 / 404 |
POST |
/api/layouts |
Layout speichern/aktualisieren | 200 + LayoutDto |
DELETE |
/api/layouts?layoutType=...&layoutKey=...&userName=... |
Layout löschen | 204 / 404 |
Dashboard (api/dashboard)
| Route | Beschreibung |
|---|---|
api/dashboard/* |
DevExpress Dashboard Backend (automatisch gemappt via MapDashboardRoute) |
SignalR Hub
| Route | Event | Beschreibung |
|---|---|---|
/hubs/dashboards |
DashboardsChanged |
Wird gefeuert, wenn ein Dashboard erstellt, geändert oder gelöscht wird |
11. Blazor-Frontend & DevExpress
Komponentenhierarchie
App.razor
??? Routes.razor
??? MainLayout.razor ? Sidebar + Content + Dark-Mode-Toggle
??? NavMenu.razor ? Navigation
??? @Body
??? Home.razor ? DxCarousel
??? Catalogs.razor
? ??? CatalogsGrid.razor ? DxGrid + Band-Editor
??? Massdata.razor
? ??? MassDataGrid.razor ? DxGrid + Band-Editor + Paginierung
??? Dashboard.razor ? DxDashboard (Viewer/Designer)
Verwendete DevExpress Blazor Komponenten
| Komponente | Einsatzort | Funktionen |
|---|---|---|
DxGrid |
CatalogsGrid, MassDataGrid | Daten anzeigen, sortieren, filtern, gruppieren, editieren |
DxGridDataColumn |
Grid-Spalten | Datenspalten |
DxGridBandColumn |
Grid-Bänder | Spalten-Gruppierung in Bänder |
DxGridCommandColumn |
Grid-Aktionen | Edit/Delete-Buttons |
DxFormLayout |
Edit-Popups, Band-Editor | Formular-Layout |
DxTextBox |
Überall | Text-Eingabe |
DxComboBox |
Band-Zuweisung, Prozedur-Auswahl | Dropdown |
DxCheckBox |
MassData Edit | Boolean-Eingabe |
DxButton |
Überall | Aktionen |
DxDropDownButton |
Grid-Toolbar | Size-Mode Auswahl |
DxToolbar |
Grid-Toolbar | Toolbar über dem Grid |
DxPager |
MassDataGrid | Seitennavigation |
DxCarousel |
Home | Bilder-Karussell |
DxDashboard |
Dashboard | Dashboard Viewer & Designer |
DevExpress Grid-Features im Detail
| Feature | CatalogsGrid | MassDataGrid |
|---|---|---|
| Sortierung | ? | ? |
| Gruppierung (Drag & Drop) | ? | ? |
| Spaltenfilter | ? | ? |
| Spalten-Resize | ? | ? |
| Spalten-Reorder | ? | ? |
| Popup-Bearbeitung | ? | ? |
| Inline-Validierung | ? | ? |
| Fokussierte Zeile | ? | ? |
| Band-Spalten (dynamisch) | ? | ? |
| Layout-Persistierung | ? | ? |
| Size-Mode Toggle | ? | ? |
| Serverseitige Paginierung | ? | ? |
| Pager-Komponente | ? | ? |
| Löschfunktion | ? | ? (deaktiviert) |
12. Dashboard-System (DevExpress Dashboards)
Architektur
Blazor (DxDashboard) ??HTTP??? API (DefaultDashboardController)
?
DashboardConfigurator
?
??????????????
? ?
SqlDashboard DataSource
Storage InMemoryStorage
? ?
SQL Tabelle JSON APIs
(TBDD_SMF_CONFIG) (api/catalogs)
Features
- Viewer-Modus: Dashboard anzeigen (read-only).
- Designer-Modus: Dashboard interaktiv bearbeiten (Datenquellen, Charts, Grids etc.).
- Persistierung: Dashboards werden als XML in der SQL-Tabelle
TBDD_SMF_CONFIGgespeichert. - Auto-Seed: Beim Start werden Default-Dashboards erstellt, wenn sie fehlen.
- Echtzeit-Sync: Dashboard-Änderungen werden via SignalR an alle verbundenen Clients gepusht.
- Navigation: Sidebar zeigt alle verfügbaren Dashboards. URL-basierte Navigation mit optionalem Mode-Parameter (
?mode=designer/?mode=viewer).
Dashboard-Datenquellen
| Name | Typ | Quelle |
|---|---|---|
| Catalogs (API) | DashboardJsonDataSource |
{ApiBaseUrl}/api/catalogs |
| JSON Data Source (URL) | DashboardJsonDataSource |
Externe URL (DevExpress-Beispieldaten) |
13. Layout-Persistierung
Konzept
Benutzer können das Grid-Layout (Spaltenbreiten, Reihenfolge, Bänder, Gruppierung, Sortierung, Filterzustände, Size-Mode) pro Grid und pro User speichern.
Datenfluss
1. User ändert Grid-Layout (Spalten verschieben, Bänder definieren, etc.)
2. User klickt "Layout speichern"
3. CatalogsGrid/MassDataGrid ? BandLayoutService.SaveBandLayoutAsync()
4. BandLayoutService serialisiert BandLayout als JSON
5. LayoutApiClient.UpsertAsync() ? HTTP POST api/layouts
6. LayoutsController ? LayoutRepository.UpsertAsync()
7. EF Core ? INSERT/UPDATE in TBDD_SMF_LAYOUT
User-Identifikation
Da noch keine Authentifizierung implementiert ist, wird pro Browser ein zufälliger User-Key im localStorage generiert:
var layoutUser = await jsRuntime.InvokeAsync<string?>("localStorage.getItem", LayoutUserStorageKey);
if (string.IsNullOrWhiteSpace(layoutUser))
{
layoutUser = Guid.NewGuid().ToString("N");
await jsRuntime.InvokeVoidAsync("localStorage.setItem", LayoutUserStorageKey, layoutUser);
}
BandLayout-Modell
public class BandLayout
{
public List<BandDefinition> Bands { get; set; } // Definierte Bänder
public List<string> ColumnOrder { get; set; } // Spaltenreihenfolge
public Dictionary<string, string?> ColumnWidths { get; set; } // Spaltenbreiten
public GridPersistentLayout? GridLayout { get; set; } // DevExpress Grid-State
public SizeMode SizeMode { get; set; } // Klein/Mittel/Groß
}
Speicherort in DB
| Spalte | Inhalt |
|---|---|
LAYOUT_TYPE |
"GRID_BANDS" |
LAYOUT_KEY |
"CatalogsGrid" oder "MassDataGrid" |
USER_NAME |
Generierter User-Key |
LAYOUT_DATA |
JSON (UTF-8 Bytes) |
14. Echtzeit-Kommunikation (SignalR)
Verwendung
SignalR wird an zwei Stellen eingesetzt:
| Stelle | Zweck |
|---|---|
| Blazor-intern | Blazor Server nutzt SignalR automatisch für die UI-Synchronisation (kein eigenes Setup nötig) |
| Dashboard-Updates | Eigener SignalR Hub (/hubs/dashboards) für Dashboard-Änderungs-Benachrichtigungen |
Dashboard SignalR Flow
1. User A ändert Dashboard im Designer ? SaveDashboard()
2. SqlDashboardStorage.SaveDashboard() speichert in DB
3. SqlDashboardStorage ruft _notifier.NotifyChanged() auf
4. DashboardChangeNotifier sendet "DashboardsChanged" via HubContext
5. Alle verbundenen Clients (User B, C, ...) empfangen das Event
6. Dashboard.razor empfängt Event ? RefreshDashboards()
7. Dashboard-Liste wird aktualisiert (ohne Page Reload)
15. Konfiguration
DbFirst.API – appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=...;Database=DD_ECM;...", // SQL17 (Catalogs, Layouts, Dashboards)
"MassDataConnection": "Server=...;Database=DD_ECM;..." // SQL19 (MassData)
},
"Dashboard": {
"BaseUrl": "https://localhost:7204" // API-URL für Dashboard-Datenquellen
},
"Cors": {
"AllowedOrigins": ["https://localhost:7276", "http://localhost:5101"]
},
"TableConfigurations": {
"VwmyCatalog": {
"ViewName": "VWMY_CATALOG",
"GuidColumnName": "GUID",
...
}
}
}
DbFirst.BlazorWebApp – appsettings.json
{
"ApiBaseUrl": "https://localhost:7204/" // Backend-API-Adresse
}
URLs (Entwicklungsumgebung)
| Anwendung | HTTPS | HTTP |
|---|---|---|
| DbFirst.API | https://localhost:7204 |
http://localhost:5131 |
| DbFirst.BlazorWebApp | https://localhost:7191 |
http://localhost:5096 |
16. Lokale Entwicklungsumgebung
Voraussetzungen
| Software | Version |
|---|---|
| .NET SDK | 8.0+ |
| Visual Studio | 2022 (17.8+) empfohlen |
| SQL Server | Zugang zu den konfigurierten Instanzen (SQL17, SQL19) |
| DevExpress Blazor Lizenz | 25.2.3 (NuGet-Feed konfiguriert) |
Projekt starten
Beide Projekte müssen gleichzeitig gestartet werden (Multi-Startup in Visual Studio):
- Rechtsklick auf die Solution ? „Startprojekte konfigurieren"
DbFirst.API? StartenDbFirst.BlazorWebApp? Starten- F5 drücken
Alternativ in zwei Terminals:
# Terminal 1: API
cd DbFirst.API
dotnet run --launch-profile https
# Terminal 2: Blazor
cd DbFirst.BlazorWebApp
dotnet run --launch-profile https
Swagger UI
Die API-Dokumentation ist im Development-Modus unter https://localhost:7204/swagger erreichbar.
Neue Tabelle/View hinzufügen (Workflow)
- Scaffold der neuen Tabelle/View:
dotnet ef dbcontext scaffold "..." Microsoft.EntityFrameworkCore.SqlServer ` --startup-project DbFirst.API ` --project DbFirst.Infrastructure ` --context ApplicationDbContext ` --context-dir . ` --output-dir ../DbFirst.Domain/Entities ` --table dbo.NEUE_TABELLE ` --use-database-names ` --no-onconfiguring ` --force - Entity überprüfen/anpassen in
DbFirst.Domain/Entities/ - DbContext überprüfen – ggf.
OnModelCreatinganpassen - Repository-Interface erstellen in
DbFirst.Application/Repositories/ - Repository-Implementierung erstellen in
DbFirst.Infrastructure/Repositories/ - DTOs erstellen in
DbFirst.Application/<Feature>/ - AutoMapper Profile erstellen
- MediatR Commands/Queries + Handler erstellen
- Controller erstellen in
DbFirst.API/Controllers/ - DI registrieren in
Program.cs - Frontend-DTOs spiegeln in
DbFirst.BlazorWebApp/Models/ - ApiClient erstellen in
DbFirst.BlazorWebApp/Services/ - Blazor-Komponente erstellen
17. Architektur-Entscheidungen (ADRs)
ADR-001: Database-First statt Code-First
Kontext: Die Datenbank existiert bereits in Produktion mit komplexer Logik in SPs und Triggern.
Entscheidung: Database-First via dotnet ef dbcontext scaffold.
Begründung: Keine Migrationen nötig, bestehende DB-Logik bleibt intakt, kein Risiko durch Code-First-Migrationen auf Prod-DB.
ADR-002: Stored Procedures für Schreiboperationen
Kontext: Die bestehende DB nutzt SPs für Insert/Update/Delete mit spezieller Logik (Audit, Output-Parameter, Trigger).
Entscheidung: EF Core ExecuteSqlRawAsync für SP-Aufrufe, EF Core nur für Leseoperationen.
Begründung: Bestehende Business-Logik in SPs wird beibehalten. EF Core wird nicht als vollständiges CRUD-ORM genutzt, sondern primär als Lese-Mapper.
ADR-003: Kein generisches Repository für alle Entities
Kontext: Es existiert ein IRepository<T>, aber nicht alle Repositories implementieren es.
Entscheidung: Spezialisierte Repository-Interfaces pro Feature.
Begründung: Die Operationen sind zu asymmetrisch (SP-basierte CRUD vs. EF Core CRUD, Composite Keys vs. int-ID, Upsert statt Insert+Update). Ein erzwungenes generisches Pattern würde zu viel Komplexität erzeugen (siehe Kommentar im Code von CatalogRepository).
ADR-004: Zwei separate DbContexts
Kontext: Entities liegen auf verschiedenen SQL-Server-Instanzen.
Entscheidung: ApplicationDbContext (SQL17) und MassDataDbContext (SQL19).
Begründung: Unterschiedliche Connection Strings erfordern separate DbContexts.
ADR-005: Blazor ohne Projekt-Referenz zum Backend
Kontext: Frontend soll unabhängig vom Backend sein.
Entscheidung: BlazorWebApp hat keine Projekt-Referenz zu Domain/Application/Infrastructure. Eigene DTOs im Frontend.
Begründung: Saubere Trennung, Frontend und Backend können unabhängig deployed werden. DTO-Duplikation ist bewusst in Kauf genommen.
ADR-006: MediatR für CQRS
Kontext: Klare Trennung von Lese- und Schreiboperationen gewünscht.
Entscheidung: MediatR mit expliziten Command- und Query-Records.
Begründung: Jeder Use Case hat seinen eigenen Handler – gut testbar, erweiterbar (Pipeline Behaviors für Logging, Validation etc.).
ADR-007: Layout-Persistierung in DB statt im Browser
Kontext: Grid-Layouts sollen über Sessions und Geräte hinweg erhalten bleiben.
Entscheidung: Layouts als JSON in DB-Tabelle TBDD_SMF_LAYOUT speichern.
Begründung: localStorage wäre browserspezifisch. DB-Persistierung ermöglicht geräteübergreifende Layouts.
18. Erweiterbarkeit & nächste Schritte
Offene TODOs (aus dem Code)
| Stelle | TODO |
|---|---|
Program.cs (API) |
CORS-Origins aus appsettings konfigurierbar machen für Produktion |
CatalogRepository.cs |
Prüfung ob generisches Repository sinnvoll (Fazit: nein, bewusst entschieden) |
| MassDataGrid | Löschfunktion noch nicht implementiert |
Empfohlene nächste Schritte
| # | Thema | Beschreibung |
|---|---|---|
| 1 | Authentifizierung | Azure AD / Windows Auth einführen – den generierten Layout-User-Key durch echte Benutzeridentität ersetzen |
| 2 | Validierung | FluentValidation in die MediatR Pipeline einbauen (Pipeline Behavior) |
| 3 | Logging | Strukturiertes Logging mit Serilog, ggf. MediatR Logging-Behavior |
| 4 | Unit Tests | Test-Projekt für Handler, Repositories (mit InMemory-DB oder Mocks) |
| 5 | CI/CD | Pipeline für Build, Test, Deployment |
| 6 | Error Handling | Result-Pattern (Result<T>) statt null-Returns in Handlern |
| 7 | Caching | Response-Caching oder In-Memory-Cache für häufige Leseanfragen |
| 8 | Shared DTOs | Ggf. gemeinsames DTO-Projekt für Frontend und Backend, um Duplikation zu reduzieren |
19. Glossar
| Begriff | Bedeutung |
|---|---|
| Clean Architecture | Architekturstil mit konzentrischen Schichten, bei dem Abhängigkeiten nur nach innen zeigen |
| CQRS | Command Query Responsibility Segregation – Trennung von Lese- und Schreiboperationen |
| Database-First | EF Core generiert Code aus bestehender Datenbank (Gegenteil: Code-First) |
| DDD | Domain-Driven Design – Domäne steht im Zentrum der Architektur |
| DTO | Data Transfer Object – Objekt für den Datentransport zwischen Schichten |
| Entity | Domänenobjekt, das einer DB-Tabelle/View entspricht |
| MediatR | .NET-Library für das Mediator-Pattern (in-process Messaging) |
| Scaffold | Automatische Code-Generierung aus bestehender DB-Struktur |
| SP (Stored Procedure) | Gespeicherte SQL-Prozedur in der Datenbank |
| Upsert | Insert + Update in einer Operation |
| Band-Column | DevExpress Grid-Feature: Spalten unter einer gemeinsamen Überschrift gruppieren |
| Interactive Server Rendering | Blazor Render-Modus, bei dem der UI-State auf dem Server lebt |