From cdf225bad127fea1662995b02abbdba7fedcb9c7 Mon Sep 17 00:00:00 2001 From: OlgunR Date: Thu, 9 Apr 2026 15:45:32 +0200 Subject: [PATCH] Add full project documentation (PROJEKTDOKUMENTATION.md) 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. --- docs/PROJEKTDOKUMENTATION.md | 1157 ++++++++++++++++++++++++++++++++++ 1 file changed, 1157 insertions(+) create mode 100644 docs/PROJEKTDOKUMENTATION.md diff --git a/docs/PROJEKTDOKUMENTATION.md b/docs/PROJEKTDOKUMENTATION.md new file mode 100644 index 0000000..fcbc62e --- /dev/null +++ b/docs/PROJEKTDOKUMENTATION.md @@ -0,0 +1,1157 @@ +# DbFirst – Projektdokumentation + +> **Version:** 1.0 +> **Stand:** Juli 2025 +> **Repository:** `https://git.dd/AppStd/DbFirst` (Branch: `main`) +> **Zielgruppe:** Entwickler-Team + +--- + +## Inhaltsverzeichnis + +1. [Projektziel & Motivation](#1-projektziel--motivation) +2. [Technologie-Stack](#2-technologie-stack) +3. [Architekturübersicht (Clean Architecture)](#3-architekturübersicht-clean-architecture) +4. [Solution-Struktur](#4-solution-struktur) +5. [Projekt-Details](#5-projekt-details) + - 5.1 [DbFirst.Domain](#51-dbfirstdomain) + - 5.2 [DbFirst.Application](#52-dbfirstapplication) + - 5.3 [DbFirst.Infrastructure](#53-dbfirstinfrastructure) + - 5.4 [DbFirst.API](#54-dbfirstapi) + - 5.5 [DbFirst.BlazorWebApp](#55-dbfirstblazorwebapp) +6. [Abhängigkeiten zwischen Projekten](#6-abhängigkeiten-zwischen-projekten) +7. [Database-First-Ansatz & Scaffold-Befehl](#7-database-first-ansatz--scaffold-befehl) +8. [Datenbank-Architektur](#8-datenbank-architektur) +9. [CQRS-Pattern (MediatR)](#9-cqrs-pattern-mediatr) +10. [API-Endpunkte](#10-api-endpunkte) +11. [Blazor-Frontend & DevExpress](#11-blazor-frontend--devexpress) +12. [Dashboard-System (DevExpress Dashboards)](#12-dashboard-system-devexpress-dashboards) +13. [Layout-Persistierung](#13-layout-persistierung) +14. [Echtzeit-Kommunikation (SignalR)](#14-echtzeit-kommunikation-signalr) +15. [Konfiguration](#15-konfiguration) +16. [Lokale Entwicklungsumgebung](#16-lokale-entwicklungsumgebung) +17. [Architektur-Entscheidungen (ADRs)](#17-architektur-entscheidungen-adrs) +18. [Erweiterbarkeit & nächste Schritte](#18-erweiterbarkeit--nächste-schritte) +19. [Glossar](#19-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 +? ? ??? 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. +- `VwmyCatalog` ist `partial 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 ? Generisches Basis-Interface + ??? ICatalogRepository + ??? IMassDataRepository + ??? ILayoutRepository +``` + +#### Repository-Interfaces + +**`IRepository`** – Generisches Basis-Interface: +```csharp +public interface IRepository +{ + Task> GetAllAsync(CancellationToken ct); + Task GetByIdAsync(int id, CancellationToken ct); + Task InsertAsync(T entity, CancellationToken ct); + Task UpdateAsync(int id, T entity, CancellationToken ct); + Task DeleteAsync(int id, CancellationToken ct); +} +``` + +**`ICatalogRepository`** – Erweitert das generische Interface um spezifische Methoden: +```csharp +public interface ICatalogRepository : IRepository +{ + Task GetByTitleAsync(string title, CancellationToken ct); + Task UpdateAsync(int id, VwmyCatalog catalog, CatalogUpdateProcedure procedure, CancellationToken ct); +} +``` + +**`IMassDataRepository`** und **`ILayoutRepository`** nutzen bewusst **nicht** `IRepository`, da deren Operationen stark abweichen (Upsert statt Insert, Suche nach Composite Keys statt int-ID). + +#### DI-Registrierung + +```csharp +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: + +```json +"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 + +```csharp +public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) +{ + services.Configure(configuration.GetSection("TableConfigurations")); + services.AddDbContext(options => + options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"))); + services.AddDbContext(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()` registriert. Die `ApiBaseUrl` wird aus `appsettings.json` gelesen. + +#### Fehlerbehandlung im Frontend + +`CatalogApiClient` implementiert ein `ApiResult` Pattern: + +```csharp +public record ApiResult(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: + +```csharp +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) + +```powershell +dotnet ef dbcontext scaffold "" Microsoft.EntityFrameworkCore.SqlServer ` + --startup-project ` + --project ` + --context ` + --context-dir ` + --output-dir ` + --table ` + --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 + +```powershell +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 + +1. **Scaffold ausführen** ? Entities in `DbFirst.Domain/Entities/`, DbContext in `DbFirst.Infrastructure/`. +2. **DbContext anpassen**: Konfigurierbare Spaltennamen über `TableConfigurations` einbauen, ggf. `partial` Methoden verwenden. +3. **Entity prüfen**: Ggf. Properties umbenennen (PascalCase), das Mapping im DbContext entsprechend anpassen. +4. **Repository implementieren**: In `DbFirst.Infrastructure/Repositories/`. +5. **MediatR Handler erstellen**: In `DbFirst.Application/`. +6. **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` | +| 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` | +| 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` | +| `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` | +| `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_CONFIG` gespeichert. +- **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: + +```csharp +var layoutUser = await jsRuntime.InvokeAsync("localStorage.getItem", LayoutUserStorageKey); +if (string.IsNullOrWhiteSpace(layoutUser)) +{ + layoutUser = Guid.NewGuid().ToString("N"); + await jsRuntime.InvokeVoidAsync("localStorage.setItem", LayoutUserStorageKey, layoutUser); +} +``` + +### BandLayout-Modell + +```csharp +public class BandLayout +{ + public List Bands { get; set; } // Definierte Bänder + public List ColumnOrder { get; set; } // Spaltenreihenfolge + public Dictionary 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` + +```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` + +```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): + +1. Rechtsklick auf die Solution ? **„Startprojekte konfigurieren"** +2. `DbFirst.API` ? **Starten** +3. `DbFirst.BlazorWebApp` ? **Starten** +4. **F5** drücken + +Alternativ in zwei Terminals: + +```powershell +# 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) + +1. **Scaffold** der neuen Tabelle/View: + ```powershell + 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 + ``` +2. **Entity überprüfen/anpassen** in `DbFirst.Domain/Entities/` +3. **DbContext überprüfen** – ggf. `OnModelCreating` anpassen +4. **Repository-Interface** erstellen in `DbFirst.Application/Repositories/` +5. **Repository-Implementierung** erstellen in `DbFirst.Infrastructure/Repositories/` +6. **DTOs** erstellen in `DbFirst.Application//` +7. **AutoMapper Profile** erstellen +8. **MediatR Commands/Queries + Handler** erstellen +9. **Controller** erstellen in `DbFirst.API/Controllers/` +10. **DI registrieren** in `Program.cs` +11. **Frontend-DTOs** spiegeln in `DbFirst.BlazorWebApp/Models/` +12. **ApiClient** erstellen in `DbFirst.BlazorWebApp/Services/` +13. **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`, 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`) 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 |