Files
DbFirst/docs/PROJEKTDOKUMENTATION.md
OlgunR cdf225bad1 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.
2026-04-09 15:45:32 +02:00

1158 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<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.
- `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<T> ? Generisches Basis-Interface
??? ICatalogRepository
??? IMassDataRepository
??? ILayoutRepository
```
#### Repository-Interfaces
**`IRepository<T>`** Generisches Basis-Interface:
```csharp
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:
```csharp
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
```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<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:
```csharp
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:
```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 "<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
```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<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_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<string?>("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<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`
```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/<Feature>/`
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<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 |