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