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