Files
Services.DDDocumentOperator/DocumentOperator.API/ROADMAP.md
OlgunR fc79665241 Update ROADMAP and add STATUS_UPDATE for project status
Extensively updated `ROADMAP.md` to reflect the current
project status, including documentation of the DevExpress
Universal License, completion of Phases 1 and 2, and
progress in Phase 3. Clarified discrepancies in the
Application Layer and identified gaps in the Tests Layer
and Infrastructure Services.

Created `STATUS_UPDATE_17_01_2025.md` to summarize the
current status, key learnings, and next steps. Outlined
a TDD-driven approach for implementing the
`DevExpressPdfProcessor` and cleaning up the Application
Layer. Confirmed build success and updated documentation
to align with the latest roadmap.
2026-06-19 11:08:36 +02:00

1496 lines
41 KiB
Markdown

# ?? DocumentOperator - Project Roadmap (Pragmatic Edition)
> **Last Updated:** 17.01.2025 | **Status:** In Development | **Phase:** 2 (Domain Layer - Minimal)
---
## ?? TABLE OF CONTENTS
1. [Project Overview](#project-overview)
2. [Architecture & Design Decisions](#architecture--design-decisions)
3. [Development Philosophy](#development-philosophy)
4. [Technology Stack](#technology-stack)
5. [Project Structure](#project-structure)
6. [Development Roadmap](#development-roadmap)
7. [Testing Strategy](#testing-strategy)
8. [Current Status](#current-status)
---
## ?? PROJECT OVERVIEW
### Vision & Purpose
**DocumentOperator** ist ein zentralisierter REST API Service für PDF-Dokumenten-Operationen in einer Multi-Tenant DMS-Umgebung.
### Problem Statement
**Aktuell:**
- Verschiedene DMS-Kunden bei unterschiedlichen Mandanten
- Jede Anwendung implementiert PDF-Operationen redundant
- Keine zentrale Stelle für Dokumenten-Verarbeitung
- Wartungsaufwand multipliziert sich mit jeder Anwendung
**Lösung:**
- **Ein** zentraler Service für alle PDF-Operationen
- Wiederverwendbar über HTTP REST API
- Mandantenfähig (Multi-Tenancy)
- Wartbar an einer Stelle
---
### Core Features
Der Service bietet folgende PDF-Operationen:
#### 1. **PDF Validierung**
- Prüfung auf gültiges PDF-Format
- Korruptions-Erkennung
- Metadaten-Extraktion (Seitenzahl, Größe, Version, Anhänge)
#### 2. **Attachment-Extraktion**
- Erkennung von eingebetteten Anhängen
- Extraktion in temporären Ordner
- Rückgabe als Base64 oder Download-Link
#### 3. **PDF-Konkatenation**
- Zusammenführen mehrerer PDFs
- Reihenfolge konfigurierbar
- Seitenzahl-Optimierung
#### 4. **Stempel/Wasserzeichen**
- Aufbringen von Stamps (Logo, Text)
- Positions-Konfiguration
- Mandanten-spezifische Logos
#### 5. **Zertifikat-Einbettung**
- PFX-Zertifikate als Attachment einbetten
- Digitale Signatur-Vorbereitung
- Workflow-Integration (Ergebnisbericht ? Zertifikat ? Siegel)
---
### Business Workflow
```
Client Application
?
[HTTP Request] - JSON mit Base64-PDF
?
DocumentOperator API (Minimal API Endpoint)
?
[FluentValidation] ? [MediatR Handler] ? [DevExpress Service] ? [Ergebnis]
?
[HTTP Response] - JSON mit verarbeitetem PDF (Base64)
```
**Typischer Ablauf:**
1. Client sendet PDF als Base64 in JSON
2. API validiert Input (FluentValidation in MediatR Pipeline)
3. Handler konvertiert PDF ? Byte-Array
4. DevExpress Service führt Operation durch
5. Temporäre Dateien werden erstellt/bereinigt (falls nötig)
6. Ergebnis wird als Base64 zurückgegeben
---
## ??? ARCHITECTURE & DESIGN DECISIONS
### Clean Architecture (Pragmatisch!)
Wir verwenden **Clean Architecture** mit 4 Layers - **ABER: pragmatisch, nicht dogmatisch!**
```
???????????????????????????????????????
? API Layer (Endpoints) ? ? HTTP Entry Point
???????????????????????????????????????
? Application Layer (Use Cases) ? ? MediatR Handlers, DTOs
???????????????????????????????????????
? Infrastructure Layer (Tech Stack) ? ? DevExpress, File I/O
???????????????????????????????????????
? Domain Layer (MINIMAL!) ? ? Nur Enums + Value Objects
???????????????????????????????????????
```
#### Dependency Rule
**Abhängigkeiten zeigen immer nach innen:**
```
API ? Application ? Domain
API ? Infrastructure ? Domain
Infrastructure ? Application (für Interfaces)
Domain ? NICHTS! (No External Dependencies)
Application ? NUR Domain
```
**Warum Clean Architecture?**
- ? Testbarkeit (Application Layer kann Services mocken)
- ? Austauschbarkeit (DevExpress ? anderes PDF-Lib ohne Application zu ändern)
- ? Separation of Concerns (jede Schicht hat klare Verantwortung)
**ABER:**
- ? Kein Overengineering (nur was wir wirklich brauchen!)
- ? Keine spekulativen Abstraktionen (erst wenn 2. Use Case es braucht)
- ? Keine unnötigen Klassen (YAGNI - You Ain't Gonna Need It)
---
### Domain Layer - Warum so minimal?
**Was wir NICHT haben:**
- ? Keine Datenbank / EF Core
- ? Keine komplexen Entities mit Business-Logik
- ? Keine Aggregate Roots, Repositories, etc.
**Was wir SIND:**
- ? Ein **Service** (nicht eine Domain-lastige Business-Anwendung)
- ? PDF-Operationen = technische Operationen (nicht fachliche Geschäftslogik)
- ? Daten fließen durch (Input ? Verarbeitung ? Output)
**Deshalb: Domain Layer minimal!**
**Was bleibt in Domain:**
1. **Enums** (DocumentOperationType, ProcessingStatus)
- Pure Business-Konzepte
- Technologie-unabhängig
- Wiederverwendbar über alle Layer
2. **Value Objects** (Base64String, TenantId, PdfMetadata)
- Typsicherheit (Base64String statt string)
- Selbst-validierend (Fehler werfen im Constructor)
- Immutable (keine Änderungen nach Erstellung)
3. **Domain Exceptions** (DomainValidationException, PdfProcessingException, etc.)
- Für fachliche Fehler
- Exception Middleware mapped zu HTTP Status Codes
**Was wir NICHT in Domain haben:**
- ? Domain Models (PdfDocument, DocumentAttachment) ? DTOs in Application reichen!
- ? Constants (ErrorCodes) ? erst wenn wirklich mehrfach gebraucht (YAGNI)
- ? Services (? Infrastructure)
**Fazit:**
- Domain = **so viel wie nötig, so wenig wie möglich**
- Wenn wir später merken "das fehlt" ? dann erst hinzufügen (iterativ!)
---
### CQRS with MediatR
**Pattern:** Command Query Responsibility Segregation
**Warum MediatR?**
- ? Klare Trennung: 1 Command/Query = 1 Handler = 1 Verantwortung
- ? Testbarkeit (Handler kann isoliert getestet werden)
- ? Pipeline Behaviors (Validation, Logging zentral)
- ? Kein aufgeblähter Service mit 20 Methoden
**CQRS in unserem Kontext:**
- **Command:** Ändert Daten (ProcessDocument, ApplyStamp, etc.)
- **Query:** Liest Daten (ValidatePdf ? gibt nur Metadata zurück)
**Beispiel:**
```csharp
// Query (Read-Only)
public record ValidatePdfQuery(Base64String PdfContent) : IRequest<PdfMetadata>;
// Handler
public class ValidatePdfHandler : IRequestHandler<ValidatePdfQuery, PdfMetadata>
{
private readonly IPdfProcessor _processor;
public async Task<PdfMetadata> Handle(ValidatePdfQuery query, CancellationToken ct)
{
byte[] bytes = query.PdfContent.ToByteArray();
var metadata = await _processor.ValidateAsync(bytes);
return metadata;
}
}
```
**Warum Value Objects in Query/Command?**
- ? Typsicherheit (Base64String vs string)
- ? Validierung bereits beim Erstellen der Query (nicht im Handler)
- ? Handler bleibt schlank (keine Validierungs-Boilerplate)
---
### Vertical Slice Architecture
**Statt Horizontal Layers** (Commands/, Handlers/, Validators/):
```
? Horizontal (Schlecht für Wartung):
Application/
??? Commands/
? ??? ValidatePdfCommand.cs
? ??? ProcessDocumentCommand.cs
??? Handlers/
? ??? ValidatePdfHandler.cs
? ??? ProcessDocumentHandler.cs
??? Validators/
??? ValidatePdfValidator.cs
??? ProcessDocumentValidator.cs
```
**Nutzen wir Vertical Slices** (pro Feature alles zusammen):
```
? Vertical (Gut für Wartung):
Features/
??? ValidatePdf/
? ??? ValidatePdfQuery.cs
? ??? ValidatePdfHandler.cs
? ??? ValidatePdfValidator.cs
??? ProcessDocument/
??? ProcessDocumentCommand.cs
??? ProcessDocumentHandler.cs
??? ProcessDocumentValidator.cs
```
**Vorteile:**
- ? Zusammengehöriger Code ist zusammen (Cohesion)
- ? Einfacher zu finden ("Wo ist ValidatePdf?" ? ein Ordner!)
- ? Einfacher zu ändern (alle Dateien im gleichen Ordner)
- ? Weniger Merge-Konflikte im Team
---
### Exception-based Error Handling
**Entscheidung:** Keine Result Pattern Library (Ardalis.Result entfernt)
**Stattdessen:**
1. **FluentValidation** für Input-Validierung (DTO-Ebene)
2. **Domain Exceptions** für fachliche Fehler
3. **Zentrale Exception Handling Middleware** im API Layer
**Warum Exception-basiert?**
- ? Einfacherer Code (kein `if (result.IsSuccess)` überall)
- ? Weniger Boilerplate (kein Result<T> Wrapping)
- ? Standard .NET Exception-Flow (jeder kennt es)
- ? Zentrales Error Handling = wartbar an **einer** Stelle
- ? Ein Package weniger (keine Extra-Lib)
**Flow:**
```
HTTP Request
?
FluentValidation (MediatR ValidationBehavior)
? Bei Fehler: ValidationException ? Middleware ? HTTP 400
?
Handler
? Bei Fehler: DomainException ? Middleware ? HTTP 400/404/500
?
Middleware (Exception Handler)
? Mappt Exception Type ? HTTP Status Code
? Loggt Exception (Serilog)
? Gibt Problem Details (RFC 7807) zurück
?
HTTP Response (JSON)
```
**Exception Types:**
- `FluentValidation.ValidationException` ? HTTP 400 (Bad Request)
- `DomainValidationException` ? HTTP 400 (Bad Request)
- `NotFoundException` ? HTTP 404 (Not Found)
- `PdfProcessingException` ? HTTP 500 (Internal Server Error)
- `Exception` (Catch-All) ? HTTP 500
**Warum zentral?**
- Alle Fehler an **einer** Stelle behandelt
- Konsistente Error-Responses (Problem Details Format)
- Handler bleiben schlank (kein Try/Catch in jedem Handler)
- Logging zentral (Serilog)
---
### Minimal APIs (statt Controllers)
**Warum Minimal APIs?**
- ? .NET 8 Best Practice (Microsoft empfiehlt es)
- ? Weniger Boilerplate (keine Controller-Klassen)
- ? Direkte Endpoint-Definition (funktionaler Stil)
- ? Swagger funktioniert 1:1 (WithOpenApi())
- ? Bessere Performance (weniger Abstraktion)
**Beispiel:**
```csharp
app.MapPost("/api/v1/documents/validate", async (
ValidatePdfRequest request,
IMediator mediator,
CancellationToken ct) =>
{
// DTO ? Query (Value Objects erstellen)
var query = new ValidatePdfQuery(
Base64String.Create(request.Base64Pdf)
);
// MediatR Handler aufrufen
var result = await mediator.Send(query, ct);
// HTTP 200 + JSON Response
return Results.Ok(result);
})
.WithName("ValidatePdf")
.WithTags("Documents")
.WithOpenApi();
```
**Flow:**
1. HTTP Request kommt rein
2. ASP.NET Core deserialisiert JSON ? DTO
3. Endpoint ruft MediatR auf
4. MediatR Pipeline: Validation ? Handler ? Response
5. Endpoint gibt Result zurück (Results.Ok())
---
### Multi-Tenancy via API-Keys
**Konzept:**
- Jeder Mandant (Customer A, B, C...) hat eigenen API-Key
- API-Key wird in HTTP Header gesendet: `X-API-Key: customer-a-key-12345`
- Middleware resolved API-Key ? Tenant-Context
- Tenant-spezifische Einstellungen (Logo für Stamps, Zertifikat, etc.)
**Warum API-Keys?**
- ? Einfach für Service-to-Service Communication
- ? Security + Tenant-Identification kombiniert
- ? Swagger-kompatibel (für BB-Tests)
- ? Einfaches Rate-Limiting pro Tenant (später)
**Flow:**
```
HTTP Request mit Header "X-API-Key: abc123"
?
TenantResolutionMiddleware (wird später implementiert)
?
API-Key validieren (aus appsettings.json oder Redis)
?
Tenant-Info auflösen (TenantId, TenantName, IsActive)
?
ITenantContext setzen (Scoped Service)
?
Handler nutzt ITenantContext.TenantId
```
**Beispiel - Tenant-spezifischer Stamp:**
```csharp
public class ApplyStampHandler : IRequestHandler<ApplyStampCommand, byte[]>
{
private readonly ITenantContext _tenantContext;
public async Task<byte[]> Handle(ApplyStampCommand command, CancellationToken ct)
{
// Tenant-spezifisches Logo laden
var logoPath = $"logos/{_tenantContext.TenantId}/stamp.png";
// Stamp mit Logo anwenden
// ...
}
}
```
---
## ??? TECHNOLOGY STACK
### Core Framework
| Technology | Version | Purpose |
|------------|---------|---------|
| **.NET** | 8.0 | Runtime & Framework |
| **ASP.NET Core** | 8.0 | Web API |
| **C#** | 12 | Language (Primary Constructors, Record Types) |
---
### NuGet Packages
#### API Layer
| Package | Version | Purpose |
|---------|---------|---------|
| **Swashbuckle.AspNetCore** | 6.6.2 | Swagger/OpenAPI Documentation |
| **Serilog.AspNetCore** | 10.0.0 | Strukturiertes Logging |
| **Serilog.Sinks.File** | 7.0.0 | Log-Datei-Output |
| **Serilog.Enrichers.Environment** | 3.0.1 | Log-Enrichment (MachineName, etc.) |
| **Asp.Versioning.Http** | 8.1.1 | API Versioning (/api/v1/, /api/v2/) |
| **Microsoft.Extensions.Caching.StackExchangeRedis** | 8.0.28 | Redis Cache (später) |
#### Application Layer
| Package | Version | Purpose |
|---------|---------|---------|
| **MediatR** | 14.1.0 | CQRS Pattern Implementation |
| **FluentValidation** | 12.1.1 | Input Validation (DTOs) |
| **FluentValidation.DependencyInjectionExtensions** | 12.1.1 | DI Integration |
#### Infrastructure Layer
| Package | Version | Purpose |
|---------|---------|---------|
| **DevExpress.Pdf.Core** | 25.2.8 | PDF-Operationen (Merge, Extract, Sign, etc.) |
| **DevExpress Universal License** | ? Verfügbar | **Vollzugriff auf alle DevExpress Bibliotheken** |
| **Microsoft.Extensions.Options.ConfigurationExtensions** | 8.0.0 | Options Pattern |
**Hinweis zur DevExpress Lizenz:**
- ? Universal License vorhanden - wir können **ALLE** DevExpress Pakete nutzen
- Neben `DevExpress.Pdf.Core` können wir auch weitere Pakete integrieren:
- `DevExpress.Office.Core` (Word, Excel)
- `DevExpress.Document.Processor` (erweiterte Dokumenten-Verarbeitung)
- `DevExpress.Blazor` (falls UI später benötigt wird)
- Alle weiteren DevExpress Produkte nach Bedarf
#### Domain Layer
| Package | Version | Purpose |
|---------|---------|---------|
| - | - | **Keine Dependencies!** (Clean Architecture) |
#### Tests (neu!)
| Package | Version | Purpose |
|---------|---------|---------|
| **xUnit** | 2.9.3 | Test Framework |
| **FluentAssertions** | 7.0.0 | Assertions (result.Should().Be(expected)) |
| **Moq** | 4.20.72 | Mocking (für Services) |
| **Microsoft.NET.Test.Sdk** | 17.11.1 | Test SDK |
| **xunit.runner.visualstudio** | 2.8.2 | Visual Studio Test Runner |
---
## ?? PROJECT STRUCTURE
### Solution Overview
```
DocumentOperator/
??? DocumentOperator.API/ ? HTTP Entry Point
??? DocumentOperator.Application/ ? Use Cases (MediatR Handlers)
??? DocumentOperator.Infrastructure/ ? Technical Implementations
??? DocumentOperator.Domain/ ? Business Logic (MINIMAL!)
??? DocumentOperator.Tests/ ? Unit & Integration Tests (NEU!)
??? ROADMAP.md ? This file
```
---
### ?? API Layer (DocumentOperator.API)
**Purpose:** HTTP Entry Point, Routing, Middleware
**References:**
- ? Application
- ? Infrastructure
- ? Domain
**Folder Structure:**
```
DocumentOperator.API/
??? Endpoints/
? ??? v1/
? ??? DocumentEndpoints.cs ? Minimal API Endpoints
??? Middleware/
? ??? ExceptionHandlingMiddleware.cs ? Zentrale Exception Handling ?
??? Configuration/
? ??? SwaggerConfiguration.cs ? Swagger Setup (API-Key Support)
??? appsettings.json ? Base Configuration
??? appsettings.Development.json ? Dev Overrides
??? Program.cs ? Application Entry Point
```
**Was gehört hierher:**
- ? HTTP Routing (Minimal APIs)
- ? Middleware (Exception, Logging)
- ? Swagger Configuration
- ? Dependency Injection Setup
- ? appsettings.json
**Was NICHT hierher gehört:**
- ? Business Logic (? Application)
- ? PDF-Verarbeitung (? Infrastructure)
- ? Validierung (? Application: FluentValidation)
---
### ?? Application Layer (DocumentOperator.Application)
**Purpose:** Use Cases, Business Logic Orchestration
**References:**
- ? Domain (ONLY!)
**Folder Structure:**
```
DocumentOperator.Application/
??? Features/ ? Vertical Slices ?
? ??? Documents/
? ??? ValidatePdf/
? ? ??? ValidatePdfQuery.cs
? ? ??? ValidatePdfHandler.cs
? ? ??? ValidatePdfValidator.cs
? ??? ExtractAttachments/
? ? ??? ExtractAttachmentsCommand.cs
? ? ??? ExtractAttachmentsHandler.cs
? ? ??? ExtractAttachmentsValidator.cs
? ??? ... (weitere Features iterativ)
??? Common/
? ??? Interfaces/ ? Abstractions für Infrastructure
? ? ??? IPdfProcessor.cs
? ??? Behaviors/ ? MediatR Pipeline Behaviors
? ? ??? ValidationBehavior.cs ? FluentValidation Integration
? ??? DTOs/ ? Data Transfer Objects (API Contracts)
? ??? ValidatePdfRequest.cs
? ??? ValidatePdfResponse.cs
??? DependencyInjection.cs ? Service Registration
```
**Was gehört hierher:**
- ? MediatR Commands & Queries (pro Feature)
- ? Handlers (orchestrieren Domain + Infrastructure)
- ? FluentValidation Validators
- ? DTOs (API Contracts)
- ? Interfaces für Infrastructure (Dependency Inversion!)
- ? Pipeline Behaviors (Validation, Logging)
**Was NICHT hierher gehört:**
- ? DevExpress-spezifischer Code (? Infrastructure)
- ? File I/O (? Infrastructure)
- ? HTTP-spezifisches (? API)
**Warum keine Infrastructure-Referenz?**
- Application kennt nur **Interfaces** (`IPdfProcessor`)
- Infrastructure **implementiert** die Interfaces (`DevExpressPdfProcessor`)
- API injiziert die Implementierung via DI
- ? Application bleibt technologie-unabhängig!
---
### ?? Infrastructure Layer (DocumentOperator.Infrastructure)
**Purpose:** Technische Implementierungen
**References:**
- ? Application (für Interfaces)
- ? Domain
**Folder Structure:**
```
DocumentOperator.Infrastructure/
??? Services/
? ??? PdfProcessing/
? ??? DevExpressPdfProcessor.cs ? IPdfProcessor Implementation
??? Configuration/
? ??? DocumentOperatorSettings.cs ? Options Pattern Class
? ??? ApiKeySettings.cs
? ??? TenantInfo.cs
??? DependencyInjection.cs ? Service Registration
```
**Was gehört hierher:**
- ? DevExpress Integration
- ? File System Zugriffe (Temp-Files)
- ? Options Pattern Classes (Settings)
**Was NICHT hierher gehört:**
- ? Business Logic (? Application)
- ? HTTP Handling (? API)
---
### ??? Domain Layer (DocumentOperator.Domain) - MINIMAL!
**Purpose:** Business Rules (nur was wirklich gebraucht wird!)
**References:**
- ? **KEINE!** (wichtigste Clean Architecture Regel)
**Folder Structure:**
```
DocumentOperator.Domain/
??? ValueObjects/ ? Immutable, selbst-validierend
? ??? Base64String.cs
? ??? TenantId.cs
? ??? PdfMetadata.cs
??? Enums/
? ??? DocumentOperationType.cs
? ??? ProcessingStatus.cs
??? Exceptions/ ? Domain-spezifische Exceptions
??? DomainException.cs
??? DomainValidationException.cs
??? NotFoundException.cs
??? PdfProcessingException.cs
```
**Was gehört hierher:**
- ? Value Objects (Base64String, TenantId, PdfMetadata)
- ? Enums (DocumentOperationType, ProcessingStatus)
- ? Domain Exceptions
**Was NICHT hierher gehört:**
- ? Domain Models (PdfDocument, etc.) ? YAGNI! DTOs reichen!
- ? Constants (ErrorCodes) ? erst wenn mehrfach gebraucht
- ? Services (? Infrastructure)
- ? MediatR (? Application)
- ? JEGLICHE externe Library!
---
### ?? Tests Layer (DocumentOperator.Tests) - NEU!
**Purpose:** Unit & Integration Tests
**References:**
- ? Alle Projekte (API, Application, Infrastructure, Domain)
**Folder Structure:**
```
DocumentOperator.Tests/
??? Unit/
? ??? Application/
? ? ??? Features/
? ? ??? ValidatePdf/
? ? ??? ValidatePdfHandlerTests.cs
? ??? Infrastructure/
? ? ??? Services/
? ? ??? DevExpressPdfProcessorTests.cs
? ??? Domain/
? ??? ValueObjects/
? ??? Base64StringTests.cs
??? Integration/
??? API/
??? ValidatePdfEndpointTests.cs
```
**Test-Strategie:**
- ? TDD (Test-Driven Development)
- ? Unit Tests für Handler (Application Layer)
- ? Unit Tests für Services (Infrastructure Layer)
- ? Unit Tests für Value Objects (Domain Layer)
- ? Integration Tests für Endpoints (API Layer)
---
## ?? DEVELOPMENT PHILOSOPHY
### Pragmatisch, nicht dogmatisch!
**Prinzipien:**
1. **YAGNI (You Ain't Gonna Need It)**
- ? Keine spekulativen Abstraktionen
- ? Keine Klassen "für später"
- ? Erst wenn 2. Use Case es braucht ? dann Abstrahieren
2. **KISS (Keep It Simple, Stupid)**
- ? Kein Overengineering
- ? Keine unnötigen Design Patterns
- ? Einfachster Code der funktioniert
3. **Clean Architecture JA, aber pragmatisch**
- ? Dependency Rule einhalten (wichtig!)
- ? Separation of Concerns (wichtig!)
- ? ABER: Nur Abstraktionen die wir wirklich brauchen
4. **Test-Driven Development (TDD)**
- ? Tests schreiben **bevor** Code (Red ? Green ? Refactor)
- ? Tests als Dokumentation (wie wird es genutzt?)
- ? Tests als Safety Net (Refactoring ohne Angst)
5. **Outside-In Development**
- ? Von außen nach innen bauen (API ? Service ? Domain)
- ? Wir sehen sofort was funktioniert (kein "spekulatives" Code)
- ? Feedback-Loop schneller
**Konkret für unser Projekt:**
- Domain Layer **minimal** (nur Enums + Value Objects + Exceptions)
- Keine Domain Models (DTOs in Application reichen!)
- Keine Constants (erst wenn mehrfach gebraucht)
- Iterativ entwickeln (Feature für Feature)
- TDD (Test ? Code ? Refactor)
---
## ??? DEVELOPMENT ROADMAP
### ? PHASE 1: Foundation - **COMPLETED**
**Bereits erledigt:**
- [x] Solution erstellt (4 Projekte)
- [x] Dependencies korrekt (Clean Architecture Dependency Rule)
- [x] NuGet Packages installiert
- [x] Folder-Struktur erstellt
- [x] appsettings.json konfiguriert
- [x] Options Pattern Classes erstellt
- [x] Serilog Setup (Program.cs)
---
### ? PHASE 2: Domain Layer (Minimal) - **COMPLETED**
**Ziel:** Nur was wirklich gebraucht wird!
**Status:** ? **Alle Steps abgeschlossen!**
---
#### ? Step 2.1: Domain Exceptions erstellen - **COMPLETED**
**Bereits erstellt:**
1. [x] `DomainException.cs` (Basis-Exception)
2. [x] `DomainValidationException.cs` (Value Object Validierung)
3. [x] `NotFoundException.cs` (Resource nicht gefunden)
4. [x] `PdfProcessingException.cs` (PDF-spezifische Fehler)
**Wo:** `Domain/Common/Exceptions/`
---
#### ? Step 2.2: Enums erstellen - **COMPLETED**
**Aufgabe:** Aufzählungen für Business-Konzepte
**Warum JETZT (vor Value Objects)?**
- Enums haben keine Dependencies
- Werden in Value Objects gebraucht (z.B. PdfMetadata)
- Schneller Erfolg (5 Minuten Arbeit)
**Was du tun wirst:**
1. **DocumentOperationType.cs** erstellen
- **Wo:** `Domain/Models/Enums/DocumentOperationType.cs`
- **Inhalt:**
```csharp
namespace DocumentOperator.Domain.Models.Enums;
public enum DocumentOperationType
{
Validate,
ExtractAttachments,
Concatenate,
ApplyStamp,
EmbedCertificate
}
```
- **Warum:** Definiert welche Operationen unser Service kann
- **Wo gebraucht:** Später in Commands/DTOs
2. **ProcessingStatus.cs** erstellen
- **Wo:** `Domain/Models/Enums/ProcessingStatus.cs`
- **Inhalt:**
```csharp
namespace DocumentOperator.Domain.Models.Enums;
public enum ProcessingStatus
{
Pending,
Processing,
Success,
Failed
}
```
- **Warum:** Status für asynchrone Operationen (später: Queue)
- **Wo gebraucht:** Response DTOs
**Nach diesem Step:**
- Ich prüfe deine Dateien
- Wir haken Step 2.2 ab in ROADMAP.md
- Weiter zu Step 2.3 (Value Objects)
**Status:** ? **COMPLETED** (17.01.2025)
- ? DocumentOperationType.cs erstellt
- ? ProcessingStatus.cs erstellt
- ? Build erfolgreich
---
#### ? Step 2.3: Value Objects erstellen - **COMPLETED**
**Aufgabe:** Typsichere, selbst-validierende Wert-Objekte
**Warum Value Objects?**
- ? Typsicherheit: `Base64String` statt `string`
- ? Validierung an **einer** Stelle (Constructor)
- ? Immutable (keine Änderungen nach Erstellung)
- ? Wiederverwendbar (in Domain, Application, Infrastructure)
**Was du erstellt hast:**
1. **Base64String.cs** ?
- Factory Method: `Create(string value)`
- Validierung: Gültiges Base64-Format
- Konvertierung: `ToByteArray()`, `FromByteArray(byte[])`
- Wirft `DomainValidationException` bei Fehler
2. **TenantId.cs** ?
- Factory Method: `Create(string value)`
- Validierung: Nicht leer, Max 100 Zeichen
- Normalisierung: `.ToLowerInvariant()`
- Wirft `DomainValidationException` bei Fehler
3. **PdfMetadata.cs** ?
- Properties: PageCount, FileSizeBytes, PdfVersion, HasAttachments, AttachmentCount
- Computed Property: `FileSizeMB`
- Keine Validierung (nur Daten-Container)
**Wo:** `Domain/Models/ValueObjects/`
**Status:** ? **COMPLETED** (17.01.2025)
- ? Base64String.cs erstellt (sealed, Factory Methods, Validierung, Equality)
- ? TenantId.cs erstellt (sealed, Normalisierung, Validierung, Equality)
- ? PdfMetadata.cs erstellt (sealed, Computed Property, ToString())
- ? Build erfolgreich
**?? Phase 2 (Domain Layer) komplett abgeschlossen!**
---
### ? PHASE 3: Infrastructure Layer (Outside-In!) - **NEXT**
**Ziel:** DevExpress Services implementieren (wir sehen **echten** Code!)
---
#### ? Step 3.1: IPdfProcessor Interface erstellen - **COMPLETED**
**Aufgabe:** Abstraction für PDF-Operationen
**Was du erstellt hast:**
- **Wo:** `Application/Common/Interfaces/IPdfProcessor.cs` ?
- **Inhalt:**
```csharp
using DocumentOperator.Domain.Models.ValueObjects;
namespace DocumentOperator.Application.Common.Interfaces;
public interface IPdfProcessor
{
Task<PdfMetadata> ValidateAsync(byte[] pdfBytes);
}
```
**Warum Interface ERST?**
- Application kennt nur Interface (Dependency Inversion)
- Infrastructure implementiert
- TDD: Test ? Interface ? Implementation
**Status:** ? **COMPLETED** (17.01.2025)
- ? Interface erstellt mit XML Comments
- ? Using Statement korrekt
- ? Namespace korrekt
- ? Build erfolgreich
---
#### ?? Step 3.2: DevExpressPdfProcessor implementieren (mit TDD!) - **NEXT**
**Aufgabe:** DevExpress Integration
**Flow:**
1. **Test schreiben** (Red)
```csharp
[Fact]
public async Task ValidateAsync_ValidPdf_ReturnMetadata()
{
// Arrange
var processor = new DevExpressPdfProcessor();
byte[] validPdf = CreateDummyPdf();
// Act
var metadata = await processor.ValidateAsync(validPdf);
// Assert
metadata.PageCount.Should().BeGreaterThan(0);
}
```
2. **Implementation schreiben** (Green)
```csharp
public class DevExpressPdfProcessor : IPdfProcessor
{
public async Task<PdfMetadata> ValidateAsync(byte[] pdfBytes)
{
using var processor = new PdfDocumentProcessor();
processor.LoadDocument(pdfBytes);
return new PdfMetadata(
PageCount: processor.Document.Pages.Count,
FileSizeBytes: pdfBytes.Length,
// ...
);
}
}
```
3. **Test grün machen**
4. **Refactoring** (falls nötig)
**Wo:**
- Test: `Tests/Unit/Infrastructure/Services/DevExpressPdfProcessorTests.cs`
- Code: `Infrastructure/Services/PdfProcessing/DevExpressPdfProcessor.cs`
**Nach diesem Step:**
- Wir haben **echten** Code der mit DevExpress arbeitet!
- Wir wissen welche Exceptions geworfen werden können
- Wir können Exception Middleware bauen
---
### ? PHASE 4: Application Layer (erste Feature)
**Ziel:** ValidatePdf Feature komplett (Query ? Handler ? Validator)
---
#### ? Step 4.1: MediatR Setup
**Aufgabe:** MediatR + FluentValidation + ValidationBehavior
**Was du erstellen wirst:**
1. `DependencyInjection.cs` (Application Layer)
2. `ValidationBehavior.cs` (MediatR Pipeline)
**Warum jetzt?**
- Wir brauchen MediatR für Handler
- ValidationBehavior = zentrale FluentValidation Ausführung
---
#### ? Step 4.2: ValidatePdf Feature (mit TDD!)
**Aufgabe:** Erste komplette Feature-Implementierung
**Was du erstellen wirst:**
1. **ValidatePdfQuery.cs**
```csharp
public record ValidatePdfQuery(Base64String PdfContent) : IRequest<PdfMetadata>;
```
2. **ValidatePdfHandler.cs**
```csharp
public class ValidatePdfHandler : IRequestHandler<ValidatePdfQuery, PdfMetadata>
{
private readonly IPdfProcessor _processor;
public async Task<PdfMetadata> Handle(ValidatePdfQuery query, CancellationToken ct)
{
byte[] bytes = query.PdfContent.ToByteArray();
return await _processor.ValidateAsync(bytes);
}
}
```
3. **ValidatePdfValidator.cs** (FluentValidation)
```csharp
public class ValidatePdfValidator : AbstractValidator<ValidatePdfQuery>
{
public ValidatePdfValidator()
{
RuleFor(x => x.PdfContent).NotNull();
}
}
```
4. **ValidatePdfHandlerTests.cs** (Unit Test)
**Wo:** `Application/Features/Documents/ValidatePdf/`
**Flow:**
```
DTO ? Query (Value Objects) ? ValidationBehavior (FluentValidation)
? Handler ? IPdfProcessor ? PdfMetadata
```
---
### ? PHASE 5: API Layer
**Ziel:** HTTP Endpoint + Exception Middleware
---
#### ? Step 5.1: Exception Handling Middleware
**Aufgabe:** Zentrale Exception ? HTTP Response Mapping
**Was du erstellen wirst:**
- **Wo:** `API/Middleware/ExceptionHandlingMiddleware.cs`
- **Inhalt:**
```csharp
public class ExceptionHandlingMiddleware
{
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (DomainValidationException ex)
{
await HandleDomainValidationExceptionAsync(context, ex);
}
catch (PdfProcessingException ex)
{
await HandlePdfProcessingExceptionAsync(context, ex);
}
// ... weitere Exceptions
}
private static Task HandleDomainValidationExceptionAsync(...)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
var problemDetails = new ProblemDetails
{
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
Title = "Validation Error",
Status = 400,
Detail = ex.Message
};
return context.Response.WriteAsJsonAsync(problemDetails);
}
}
```
**Warum jetzt?**
- Wir kennen jetzt alle Exceptions (aus Infrastructure Step)
- Wir können sie zu HTTP Status Codes mappen
---
#### ? Step 5.2: Minimal API Endpoint
**Aufgabe:** HTTP Endpoint für ValidatePdf
**Was du erstellen wirst:**
- **Wo:** `API/Endpoints/v1/DocumentEndpoints.cs`
- **Inhalt:**
```csharp
public static class DocumentEndpoints
{
public static void MapDocumentEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/api/v1/documents")
.WithTags("Documents")
.WithOpenApi();
group.MapPost("/validate", ValidatePdf);
}
private static async Task<IResult> ValidatePdf(
ValidatePdfRequest request,
IMediator mediator,
CancellationToken ct)
{
var query = new ValidatePdfQuery(
Base64String.Create(request.Base64Pdf)
);
var result = await mediator.Send(query, ct);
return Results.Ok(result);
}
}
```
**DTOs:**
- `ValidatePdfRequest` (Input)
- `ValidatePdfResponse` (Output) ? oder direkt PdfMetadata?
**In Program.cs registrieren:**
```csharp
app.MapDocumentEndpoints();
```
---
#### ? Step 5.3: Integration Test
**Aufgabe:** End-to-End Test (HTTP ? Handler ? Service)
**Was du erstellen wirst:**
- **Wo:** `Tests/Integration/API/ValidatePdfEndpointTests.cs`
- **Inhalt:**
```csharp
public class ValidatePdfEndpointTests : IClassFixture<WebApplicationFactory<Program>>
{
[Fact]
public async Task POST_ValidatePdf_ValidPdf_Returns200()
{
// Arrange
var client = _factory.CreateClient();
var request = new ValidatePdfRequest(Base64Pdf: "...");
// Act
var response = await client.PostAsJsonAsync("/api/v1/documents/validate", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var metadata = await response.Content.ReadFromJsonAsync<PdfMetadata>();
metadata.PageCount.Should().BeGreaterThan(0);
}
}
```
**Warum Integration Test?**
- Testet kompletten Flow (HTTP ? MediatR ? Service ? Response)
- Testet Exception Middleware
- Testet Swagger/OpenAPI
---
### ? PHASE 6: Weitere Features (iterativ)
**Nach ValidatePdf (als Referenz):**
Jedes Feature folgt dem gleichen Pattern:
1. Interface erweitern (IPdfProcessor)
2. Service implementieren (DevExpressPdfProcessor) + Test
3. Command/Query + Handler + Validator
4. Endpoint erstellen
5. Integration Test
**Features:**
- [ ] ExtractAttachments
- [ ] ConcatenatePdfs
- [ ] ApplyStamp
- [ ] EmbedCertificate
---
### ? PHASE 7: Swagger & API Documentation
**Ziel:** Produktionsreife API-Dokumentation
**Steps:**
- [ ] Swagger Configuration (API-Key Support)
- [ ] XML Comments für Endpoints
- [ ] Response Examples (Swashbuckle)
---
### ? PHASE 8: Multi-Tenancy (später)
**Ziel:** Mandantenfähigkeit implementieren
**Steps:**
- [ ] ITenantContext Interface (Application)
- [ ] TenantContext Implementation (Infrastructure)
- [ ] TenantResolutionMiddleware (API)
- [ ] Tenant-spezifische Settings (Logo, Zertifikat)
---
### ? PHASE 9: Production-Ready
**Ziel:** Deployment vorbereiten
**Steps:**
- [ ] Health Checks
- [ ] appsettings.Production.json
- [ ] IIS Deployment (Web.config)
- [ ] Redis Integration (Distributed Cache)
---
## ?? TESTING STRATEGY
### Test-Driven Development (TDD)
**Flow:**
1. **Red:** Test schreiben (schlägt fehl, weil Code noch nicht existiert)
2. **Green:** Code schreiben (Test wird grün)
3. **Refactor:** Code verbessern (Test bleibt grün)
**Warum TDD?**
- ? Tests als Dokumentation (wie wird es genutzt?)
- ? Tests als Safety Net (Refactoring ohne Angst)
- ? Besseres Design (testbarer Code = guter Code)
- ? Keine "vergessenen" Tests (Test kommt ZUERST)
---
### Test-Pyramide
```
/\
/ \ E2E Tests (wenige)
/ \
/------\ Integration Tests (einige)
/ \
/----------\ Unit Tests (viele)
/ \
```
**Konkret:**
- **Unit Tests (viele):**
- Value Objects (Base64String.Create() wirft Exception?)
- Handlers (ValidatePdfHandler ruft IPdfProcessor auf?)
- Services (DevExpressPdfProcessor gibt Metadata zurück?)
- **Integration Tests (einige):**
- Endpoints (HTTP POST ? 200 OK + JSON?)
- MediatR Pipeline (ValidationBehavior funktioniert?)
- **E2E Tests (wenige/keine):**
- Haben wir nicht (API ist selbst der "Top-Level")
---
### Test-Abdeckung
**Ziel:** >80% Code Coverage (aber nicht um jeden Preis!)
**Was testen:**
- ? Value Objects (Validierung)
- ? Handlers (Business Logic)
- ? Services (DevExpress Integration)
- ? Endpoints (HTTP Responses)
- ? Exception Middleware (Error Mapping)
**Was NICHT testen:**
- ? DTOs (keine Logik)
- ? Enums (keine Logik)
- ? Program.cs (Startup Code)
---
### Test-Naming Convention
**Pattern:** `MethodName_Scenario_ExpectedResult`
**Beispiele:**
```csharp
// Value Object Tests
[Fact]
public void Create_EmptyString_ThrowsDomainValidationException() { }
[Fact]
public void Create_ValidBase64_ReturnsBase64String() { }
// Handler Tests
[Fact]
public async Task Handle_ValidPdf_ReturnsPdfMetadata() { }
[Fact]
public async Task Handle_InvalidPdf_ThrowsPdfProcessingException() { }
// Endpoint Tests
[Fact]
public async Task POST_ValidatePdf_ValidPdf_Returns200() { }
[Fact]
public async Task POST_ValidatePdf_InvalidPdf_Returns400() { }
```
---
## ?? CURRENT STATUS
### ? Completed
- **Phase 1:** Foundation & Clean Architecture Setup ?
- Solution Structure ?
- Dependencies ?
- NuGet Packages ?
- Folder Structure ?
- Configuration (appsettings.json) ?
- Serilog Setup ?
- Program.cs Setup ?
- **Phase 2:** Domain Layer (Minimal) ?
- ? Step 2.1 - Domain Exceptions (4 Exceptions erstellt)
- `DomainException.cs`
- `DomainValidationException.cs`
- `NotFoundException.cs`
- `PdfProcessingException.cs`
- ? Step 2.2 - Enums (DocumentOperationType, ProcessingStatus)
- ? Step 2.3 - Value Objects (Base64String, TenantId, PdfMetadata)
- **Phase 3:** Infrastructure Layer (Outside-In!)
- ? Step 3.1 - IPdfProcessor Interface erstellt
### ?? In Progress
- **Phase 3:** Infrastructure Layer
- **NEXT:** Step 3.2 - DevExpressPdfProcessor implementieren (mit TDD!)
- ?? **Wichtig:** Ordnerstruktur existiert, aber Services noch nicht implementiert
- ?? **Wichtig:** Application Layer hat ProcessDocument-Dateien (leer), sollte ValidatePdf sein
### ? Pending
- **Phase 3:** Infrastructure Layer
- Step 3.2 - DevExpressPdfProcessor Implementation
- **Phase 4:** Application Layer
- Step 4.1 - MediatR Setup (DependencyInjection.cs, ValidationBehavior.cs)
- Step 4.2 - ValidatePdf Feature (Query, Handler, Validator)
- **Phase 5:** API Layer
- Step 5.1 - Exception Handling Middleware
- Step 5.2 - Minimal API Endpoint
- Step 5.3 - Integration Test
- **Phase 6-9:** Weitere Features, Swagger, Multi-Tenancy, Production
### ?? Hinweise zum aktuellen Stand
1. **Application Layer - ProcessDocument vs ValidatePdf:**
- Aktuell: `Features/Documents/ProcessDocument/` (leer)
- Roadmap: `Features/Documents/ValidatePdf/` (geplant)
- ?? **Action:** ProcessDocument-Dateien sollten gelöscht oder umbenannt werden
2. **Tests Layer:**
- Aktuell: Nur `UnitTest1.cs` (Dummy-Test)
- Roadmap: Ordnerstruktur für Unit/Integration Tests
- ?? **Action:** Ordnerstruktur erstellen, UnitTest1.cs löschen
3. **Infrastructure Services:**
- Ordner existieren (PdfProcessing, FileStorage, DocumentValidation)
- **Aber:** Alle leer
- ?? **Action:** DevExpressPdfProcessor.cs implementieren (Step 3.2)
4. **DevExpress Universal License:**
- ? **Verfügbar!** Wir können alle DevExpress Pakete nutzen
- Aktuell nur: `DevExpress.Pdf.Core`
- Bei Bedarf können weitere Pakete hinzugefügt werden
---
## ?? KEY LEARNINGS & DECISIONS
### 1. Domain Layer minimal halten
**Entscheidung:** Nur Enums + Value Objects + Exceptions
**Warum:**
- Kein EF Core / Entities
- Service-Anwendung (nicht Domain-lastig)
- YAGNI (You Ain't Gonna Need It)
**Alternative wäre gewesen:**
- Volle Domain Models (PdfDocument, DocumentAttachment, etc.)
- **Nachteile:** Overengineering, unnötige Komplexität
---
### 2. Outside-In Development
**Entscheidung:** Infrastructure ? Application ? API
**Warum:**
- Wir sehen **echten** Code sofort (DevExpress Integration)
- Keine Spekulation (wir wissen welche Exceptions geworfen werden)
- Schnellerer Feedback-Loop
**Alternative wäre gewesen:**
- Domain ? Application ? Infrastructure ? API
- **Nachteile:** Viel "spekulativer" Code ohne echte Implementation
---
### 3. Exception-based Error Handling
**Entscheidung:** Keine Result Pattern Library
**Warum:**
- Einfacherer Code (kein Result<T> Boilerplate)
- Zentrales Error Handling (Middleware)
- Standard .NET Exception-Flow
**Alternative wäre gewesen:**
- Ardalis.Result oder FluentResults
- **Nachteile:** Extra Package, mehr Boilerplate
---
### 4. TDD (Test-Driven Development)
**Entscheidung:** Test ? Code ? Refactor
**Warum:**
- Besseres Design (testbarer Code)
- Tests als Dokumentation
- Safety Net für Refactoring
**Alternative wäre gewesen:**
- Code ? Test (Test-After)
- **Nachteile:** Tests werden oft vergessen, schlechteres Design
---
### 5. Vertical Slice Architecture
**Entscheidung:** Pro Feature alles zusammen
**Warum:**
- Zusammengehöriger Code ist zusammen
- Einfacher zu finden und zu ändern
- Besser für Teams (weniger Merge-Konflikte)
**Alternative wäre gewesen:**
- Horizontal Layers (Commands/, Handlers/, Validators/)
- **Nachteile:** Code über viele Ordner verteilt
---
## ?? REFERENCES & BEST PRACTICES
### Documentation
- [Clean Architecture (Uncle Bob)](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
- [MediatR Documentation](https://github.com/jbogard/MediatR)
- [FluentValidation Docs](https://docs.fluentvalidation.net/)
- [DevExpress PDF API](https://docs.devexpress.com/OfficeFileAPI/114877/pdf-document-api)
- [ASP.NET Core Minimal APIs](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis)
- [RFC 7807 Problem Details](https://datatracker.ietf.org/doc/html/rfc7807)
- [xUnit Documentation](https://xunit.net/)
- [FluentAssertions Documentation](https://fluentassertions.com/)
### Best Practices Applied
- ? Clean Architecture (pragmatisch!)
- ? CQRS with MediatR
- ? Vertical Slice Architecture
- ? Value Objects (DDD)
- ? Exception-based Error Handling
- ? Minimal APIs (.NET 8)
- ? TDD (Test-Driven Development)
- ? Options Pattern für Configuration
- ? Dependency Injection
- ? Async/Await überall
- ? Nullable Reference Types
- ? Record Types für DTOs (C# 12)
- ? Primary Constructors (.NET 8)
- ? Structured Logging (Serilog)
---
## ?? UPDATE LOG
| Date | Phase | Changes |
|------|-------|---------|
| 2024-XX-XX | Phase 1 | Project setup, dependencies, folder structure |
| 2024-XX-XX | Phase 1 | Configuration, Serilog, Options Pattern |
| 2024-XX-XX | Phase 1 | ? Phase 1 completed |
| 2024-XX-XX | Phase 2 | ? Step 2.1 completed - Domain Exceptions created |
| 17.01.2025 | Roadmap | ?? **ROADMAP komplett überarbeitet** (Pragmatisch, Outside-In, TDD) |
| 17.01.2025 | Phase 2 | ? Step 2.2 completed - Enums erstellt |
| 17.01.2025 | Phase 2 | ? Step 2.3 completed - Value Objects erstellt |
| 17.01.2025 | Phase 2 | ? **Phase 2 (Domain Layer) komplett abgeschlossen!** |
| 17.01.2025 | Phase 3 | ? Step 3.1 completed - IPdfProcessor Interface erstellt |
| 17.01.2025 | Roadmap | ?? **ROADMAP Status-Update** - Aktueller Projektstand dokumentiert |
| 17.01.2025 | Infrastructure | ?? **DevExpress Universal License** hinzugefügt - Vollzugriff auf alle Pakete |
---
**END OF ROADMAP**
*This document is a living document and will be updated as development progresses.*