Updated PROJECT_STATUS.md, ROADMAP.md, and PHASENPLAN.md to adopt a feature-driven development approach. Replaced the layer-by-layer methodology with a focus on delivering complete, testable features. Key changes: - PROJECT_STATUS.md: Added detailed status updates, milestones, and removed Azure dependencies in favor of local solutions (e.g., temp folders, in-memory queues). - ROADMAP.md: Restructured tasks by feature (e.g., ValidatePDF, ExtractAttachments) with detailed steps and acceptance criteria. Introduced cross-cutting concerns like multi-tenancy and resilience. - PHASENPLAN.md: Introduced a week-by-week breakdown, progress tracking, and next steps for each feature. These changes improve clarity, align tasks with deliverables, and provide a clear roadmap for project completion.
1004 lines
28 KiB
Markdown
1004 lines
28 KiB
Markdown
# ?? DocumentOperator - Project Roadmap (Feature-Driven Development)
|
|
|
|
> **Last Updated:** 17.01.2025 | **Status:** In Development | **Current Feature:** Feature 1 - ValidatePDF (NEXT)
|
|
|
|
---
|
|
|
|
## ?? NEW APPROACH: Feature-by-Feature Development
|
|
|
|
**Was hat sich geändert?**
|
|
|
|
? **Feature-Driven Development** statt Layer-by-Layer
|
|
- Jedes Feature wird KOMPLETT umgesetzt (Domain ? Infrastructure ? Application ? API ? Tests ? Swagger)
|
|
- Feature ist erst "DONE" wenn es im Swagger testbar ist
|
|
- Dann nächstes Feature
|
|
|
|
? **Kleine Schritte** (1 Layer pro Step)
|
|
- Besser überschaubar
|
|
- Weniger Merge-Konflikte
|
|
- Schnelleres Feedback
|
|
|
|
? **Multi-Tenancy & Cross-Cutting Concerns später**
|
|
- Erst alle synchronen Features implementieren
|
|
- Dann Multi-Tenancy für ALLE Endpoints (kein Wiederholungsaufwand!)
|
|
- Dann Health Checks, Polly, Logging (einmal für alle!)
|
|
|
|
---
|
|
|
|
## ?? TABLE OF CONTENTS
|
|
|
|
1. [Feature Roadmap](#feature-roadmap)
|
|
2. [Project Overview](#project-overview)
|
|
3. [Architecture & Design Decisions](#architecture--design-decisions)
|
|
4. [Technology Stack](#technology-stack)
|
|
5. [Project Structure](#project-structure)
|
|
6. [Testing Strategy](#testing-strategy)
|
|
7. [Current Status](#current-status)
|
|
8. [Key Learnings & Decisions](#key-learnings--decisions)
|
|
|
|
---
|
|
|
|
## ?? FEATURE ROADMAP
|
|
|
|
### ?? Feature Overview
|
|
|
|
| Feature | Type | Status | Swagger Testbar? |
|
|
|---------|------|--------|------------------|
|
|
| **1. ValidatePDF** | Synchron | ?? NEXT | ? |
|
|
| **2. ExtractAttachments** | Synchron | ? Pending | ? |
|
|
| **3. ApplyStamp** | Synchron | ? Pending | ? |
|
|
| **4. EmbedCertificate** | Synchron | ? Pending | ? |
|
|
| **5. ConcatenatePDFs** | Asynchron | ? Pending | ? |
|
|
|
|
### ?? Cross-Cutting Concerns (nach Features 1-4)
|
|
|
|
| Concern | Status |
|
|
|---------|--------|
|
|
| **Multi-Tenancy** (X-API-Key Header) | ? Pending |
|
|
| **Health Checks** (/health Endpoint) | ? Pending |
|
|
| **Polly Resilience** (Retry, Circuit Breaker) | ? Pending |
|
|
| **Logging & Monitoring** (Correlation IDs, Seq) | ? Pending |
|
|
|
|
---
|
|
|
|
## ?? FEATURE 1: ValidatePDF (Synchron) - **NEXT**
|
|
|
|
**Was macht dieses Feature?**
|
|
- Client sendet PDF als Base64 (JSON)
|
|
- API validiert PDF
|
|
- API gibt Metadaten zurück (Seitenzahl, Dateigröße, PDF-Version, Anhänge)
|
|
|
|
**Endpoint:**
|
|
```
|
|
POST /api/v1/documents/validate
|
|
Request: { "base64Pdf": "JVBERi0xLjQK..." }
|
|
Response: { "pageCount": 5, "fileSizeBytes": 1024, "pdfVersion": "1.4", "hasAttachments": false }
|
|
```
|
|
|
|
---
|
|
|
|
### ? Step 1.0: Foundation (bereits erledigt!)
|
|
|
|
**Was wurde bereits erstellt:**
|
|
- ? Domain Layer (Exceptions, Enums, Value Objects)
|
|
- ? Infrastructure Layer (`DevExpressPdfProcessor.ValidateAsync` - FERTIG!)
|
|
- ? Tests für Infrastructure (`DevExpressPdfProcessorTests.cs` - 6 Tests)
|
|
|
|
**Was wir wiederverwenden:**
|
|
- `Base64String` Value Object (Domain)
|
|
- `PdfMetadata` Value Object (Domain)
|
|
- `IPdfProcessor` Interface (Application)
|
|
- `DevExpressPdfProcessor.ValidateAsync` (Infrastructure)
|
|
|
|
---
|
|
|
|
### ?? Step 1.1: Application Layer (MediatR Setup + ValidatePDF Feature) - **NEXT**
|
|
|
|
**Ziel:** MediatR + FluentValidation + ValidatePDF Handler
|
|
|
|
**Was wird erstellt:**
|
|
|
|
#### 1.1.1: MediatR Setup
|
|
- **Datei:** `Application/DependencyInjection.cs`
|
|
- Registriert MediatR
|
|
- Registriert FluentValidation
|
|
- Registriert Pipeline Behaviors (Validation + Logging)
|
|
|
|
#### 1.1.2: Pipeline Behaviors
|
|
- **Datei:** `Application/Common/Behaviors/ValidationBehavior.cs`
|
|
- Führt FluentValidation automatisch aus (vor jedem Handler)
|
|
- Wirft `ValidationException` bei Fehler
|
|
|
|
- **Datei:** `Application/Common/Behaviors/LoggingBehavior.cs`
|
|
- Loggt jeden Request (mit Performance-Tracking)
|
|
- Nutzt Serilog
|
|
|
|
#### 1.1.3: ValidatePDF Feature (Vertical Slice)
|
|
- **Ordner:** `Application/Features/Documents/ValidatePdf/`
|
|
|
|
- **Datei:** `ValidatePdfQuery.cs`
|
|
```csharp
|
|
public record ValidatePdfQuery(Base64String PdfContent) : IRequest<PdfMetadata>;
|
|
```
|
|
|
|
- **Datei:** `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);
|
|
}
|
|
}
|
|
```
|
|
|
|
- **Datei:** `ValidatePdfValidator.cs`
|
|
```csharp
|
|
public class ValidatePdfValidator : AbstractValidator<ValidatePdfQuery>
|
|
{
|
|
public ValidatePdfValidator()
|
|
{
|
|
RuleFor(x => x.PdfContent).NotNull().WithMessage("PDF content is required");
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 1.1.4: DTOs
|
|
- **Ordner:** `Application/Common/DTOs/`
|
|
|
|
- **Datei:** `ValidatePdfRequest.cs`
|
|
```csharp
|
|
public record ValidatePdfRequest(string Base64Pdf);
|
|
```
|
|
|
|
- **Datei:** `ValidatePdfResponse.cs`
|
|
```csharp
|
|
public record ValidatePdfResponse(
|
|
int PageCount,
|
|
long FileSizeBytes,
|
|
double FileSizeMB,
|
|
string PdfVersion,
|
|
bool HasAttachments,
|
|
int AttachmentCount);
|
|
```
|
|
|
|
#### 1.1.5: Tests
|
|
- **Datei:** `Tests/Unit/Application/Features/ValidatePdf/ValidatePdfHandlerTests.cs`
|
|
- Test: `Handle_ValidPdf_ReturnsPdfMetadata`
|
|
- Test: `Handle_NullPdfContent_ThrowsValidationException`
|
|
|
|
**Akzeptanzkriterien:**
|
|
- ? Build erfolgreich
|
|
- ? Tests grün (alle 2 Tests)
|
|
- ? MediatR Pipeline funktioniert (Validation + Logging)
|
|
|
|
---
|
|
|
|
### ?? Step 1.2: API Layer (Endpoint + Exception Middleware)
|
|
|
|
**Ziel:** HTTP Endpoint + zentrale Exception Handling
|
|
|
|
**Was wird erstellt:**
|
|
|
|
#### 1.2.1: Exception Handling Middleware
|
|
- **Datei:** `API/Middleware/ExceptionHandlingMiddleware.cs`
|
|
- Fängt alle Exceptions
|
|
- Mappt zu HTTP Status Codes (400, 404, 500)
|
|
- Gibt RFC 7807 Problem Details zurück
|
|
|
|
#### 1.2.2: Minimal API Endpoint
|
|
- **Datei:** `API/Endpoints/v1/DocumentEndpoints.cs`
|
|
```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)
|
|
.WithName("ValidatePdf")
|
|
.WithSummary("Validates a PDF document and returns metadata");
|
|
}
|
|
|
|
private static async Task<IResult> ValidatePdf(
|
|
ValidatePdfRequest request,
|
|
IMediator mediator,
|
|
CancellationToken ct)
|
|
{
|
|
var query = new ValidatePdfQuery(Base64String.Create(request.Base64Pdf));
|
|
var metadata = await mediator.Send(query, ct);
|
|
|
|
var response = new ValidatePdfResponse(
|
|
metadata.PageCount,
|
|
metadata.FileSizeBytes,
|
|
metadata.FileSizeMB,
|
|
metadata.PdfVersion,
|
|
metadata.HasAttachments,
|
|
metadata.AttachmentCount
|
|
);
|
|
|
|
return Results.Ok(response);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 1.2.3: Program.cs Updates
|
|
- Registriert Exception Middleware
|
|
- Registriert DocumentEndpoints
|
|
- Registriert Application + Infrastructure Services
|
|
|
|
#### 1.2.4: Integration Tests
|
|
- **Datei:** `Tests/Integration/API/DocumentEndpointsTests.cs`
|
|
- Test: `POST_ValidatePdf_ValidPdf_Returns200`
|
|
- Test: `POST_ValidatePdf_InvalidBase64_Returns400`
|
|
- Test: `POST_ValidatePdf_CorruptedPdf_Returns500`
|
|
|
|
**Akzeptanzkriterien:**
|
|
- ? Build erfolgreich
|
|
- ? Integration Tests grün (alle 3 Tests)
|
|
- ? Exception Middleware funktioniert
|
|
- ? Endpoint gibt korrekte HTTP Status Codes zurück
|
|
|
|
---
|
|
|
|
### ?? Step 1.3: Swagger Dokumentation
|
|
|
|
**Ziel:** API-Dokumentation + Swagger UI testbar
|
|
|
|
**Was wird erstellt:**
|
|
|
|
#### 1.3.1: Swagger Configuration
|
|
- **Datei:** `API/Configuration/SwaggerConfiguration.cs`
|
|
- AddSwaggerGen mit XML Comments
|
|
- Konfiguriert API-Versioning
|
|
- Fügt Beispiel-Schemas hinzu
|
|
|
|
#### 1.3.2: XML Comments
|
|
- Aktivieren in `API/DocumentOperator.API.csproj`:
|
|
```xml
|
|
<PropertyGroup>
|
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
|
<NoWarn>$(NoWarn);1591</NoWarn>
|
|
</PropertyGroup>
|
|
```
|
|
|
|
- XML Comments für `ValidatePdf` Endpoint:
|
|
```csharp
|
|
/// <summary>
|
|
/// Validates a PDF document and returns metadata
|
|
/// </summary>
|
|
/// <param name="request">PDF as Base64 string</param>
|
|
/// <returns>PDF metadata (page count, file size, etc.)</returns>
|
|
/// <response code="200">PDF is valid, metadata returned</response>
|
|
/// <response code="400">Invalid PDF or Base64 format</response>
|
|
/// <response code="500">Internal server error during validation</response>
|
|
```
|
|
|
|
**Akzeptanzkriterien:**
|
|
- ? Swagger UI läuft unter `/swagger`
|
|
- ? Endpoint `/api/v1/documents/validate` ist sichtbar
|
|
- ? Request/Response Schemas sind dokumentiert
|
|
- ? Endpoint ist im Swagger UI testbar (manuelle Verifikation!)
|
|
|
|
---
|
|
|
|
### ? Feature 1 ABGESCHLOSSEN!
|
|
|
|
**Was haben wir erreicht?**
|
|
- ? ValidatePDF Feature komplett implementiert
|
|
- ? Domain ? Infrastructure ? Application ? API ? Tests ? Swagger
|
|
- ? Endpoint ist im Swagger UI testbar
|
|
- ? Unit Tests + Integration Tests grün
|
|
- ? Clean Architecture eingehalten
|
|
- ? TDD angewendet
|
|
|
|
**Nächstes Feature:**
|
|
? **Feature 2: ExtractAttachments**
|
|
|
|
---
|
|
|
|
## ?? FEATURE 2: ExtractAttachments (Synchron) - **PENDING**
|
|
|
|
**Was macht dieses Feature?**
|
|
- Client sendet PDF als Base64 (JSON)
|
|
- API extrahiert eingebettete Anhänge
|
|
- API gibt Anhänge als Base64 zurück (oder Download-Links)
|
|
|
|
**Endpoint:**
|
|
```
|
|
POST /api/v1/documents/extract-attachments
|
|
Request: { "base64Pdf": "JVBERi0xLjQK..." }
|
|
Response: { "attachments": [{ "name": "invoice.xml", "base64Content": "..." }] }
|
|
```
|
|
|
|
**Steps:**
|
|
- ?? Step 2.1: Infrastructure Layer (DevExpressPdfProcessor.ExtractAttachmentsAsync)
|
|
- ?? Step 2.2: Application Layer (ExtractAttachmentsCommand + Handler + Validator)
|
|
- ?? Step 2.3: API Layer (Endpoint)
|
|
- ?? Step 2.4: Swagger Dokumentation
|
|
|
|
---
|
|
|
|
## ?? FEATURE 3: ApplyStamp (Synchron) - **PENDING**
|
|
|
|
**Was macht dieses Feature?**
|
|
- Client sendet PDF + Stamp-Konfiguration (Text, Position)
|
|
- API fügt Stamp hinzu (Logo, Text, Wasserzeichen)
|
|
- API gibt gestempeltes PDF zurück
|
|
|
|
**Endpoint:**
|
|
```
|
|
POST /api/v1/documents/apply-stamp
|
|
Request: { "base64Pdf": "...", "text": "CONFIDENTIAL", "position": "TopRight" }
|
|
Response: { "base64Pdf": "JVBERi0xLjQK..." }
|
|
```
|
|
|
|
**Steps:**
|
|
- ?? Step 3.1: Infrastructure Layer (DevExpressPdfProcessor.ApplyStampAsync)
|
|
- ?? Step 3.2: Application Layer (ApplyStampCommand + Handler + Validator)
|
|
- ?? Step 3.3: API Layer (Endpoint)
|
|
- ?? Step 3.4: Swagger Dokumentation
|
|
|
|
---
|
|
|
|
## ?? FEATURE 4: EmbedCertificate (Synchron) - **PENDING**
|
|
|
|
**Was macht dieses Feature?**
|
|
- Client sendet PDF + Zertifikat (PFX als Base64)
|
|
- API bettet Zertifikat als Attachment ein
|
|
- API gibt PDF mit Zertifikat zurück
|
|
|
|
**Endpoint:**
|
|
```
|
|
POST /api/v1/documents/embed-certificate
|
|
Request: { "base64Pdf": "...", "base64Certificate": "..." }
|
|
Response: { "base64Pdf": "JVBERi0xLjQK..." }
|
|
```
|
|
|
|
**Steps:**
|
|
- ?? Step 4.1: Infrastructure Layer (DevExpressPdfProcessor.EmbedCertificateAsync)
|
|
- ?? Step 4.2: Application Layer (EmbedCertificateCommand + Handler + Validator)
|
|
- ?? Step 4.3: API Layer (Endpoint)
|
|
- ?? Step 4.4: Swagger Dokumentation
|
|
|
|
---
|
|
|
|
## ?? FEATURE 5: ConcatenatePDFs (Asynchron) - **PENDING**
|
|
|
|
**Was macht dieses Feature?**
|
|
- Client sendet mehrere PDFs (Array von Base64)
|
|
- API startet asynchronen Job (gibt JobId zurück)
|
|
- Client pollt Job-Status
|
|
- Wenn fertig: Client lädt Ergebnis herunter
|
|
|
|
**Endpoints:**
|
|
```
|
|
POST /api/v1/documents/concatenate (Async)
|
|
Request: { "pdfFiles": ["JVBERi0x...", "JVBERi0y..."] }
|
|
Response: { "jobId": "abc-123", "status": "Pending" }
|
|
|
|
GET /api/v1/jobs/{jobId}
|
|
Response: { "jobId": "abc-123", "status": "Processing", "progress": 50 }
|
|
|
|
GET /api/v1/jobs/{jobId}/download
|
|
Response: PDF-Datei (Binary)
|
|
```
|
|
|
|
**Steps:**
|
|
- ?? Step 5.1: Infrastructure Layer (In-Memory Queue + Background Worker)
|
|
- ?? Step 5.2: Application Layer (SubmitConcatenateJobCommand + GetJobStatusQuery)
|
|
- ?? Step 5.3: API Layer (Async Endpoints)
|
|
- ?? Step 5.4: Swagger Dokumentation
|
|
|
|
---
|
|
|
|
## ?? CROSS-CUTTING CONCERNS
|
|
|
|
**Nach Features 1-4 abgeschlossen:**
|
|
|
|
### ?? Multi-Tenancy (X-API-Key Header)
|
|
|
|
**Was wird gebaut:**
|
|
- EF Core + SQLite (Tenant-Datenbank)
|
|
- Redis Cache (API-Key Lookups)
|
|
- TenantResolutionMiddleware (X-API-Key ? Tenant)
|
|
- BCrypt API-Key Hashing
|
|
- Admin API (Tenant CRUD)
|
|
|
|
**Steps:**
|
|
- ?? Step MT.1: EF Core Setup (Entities, DbContext, Migrations)
|
|
- ?? Step MT.2: TenantResolutionMiddleware
|
|
- ?? Step MT.3: Redis Cache Integration
|
|
- ?? Step MT.4: Admin API (Tenant Management)
|
|
- ?? Step MT.5: Alle Endpoints mit X-API-Key absichern
|
|
|
|
---
|
|
|
|
### ?? Health Checks
|
|
|
|
**Was wird gebaut:**
|
|
- `/health` Endpoint (Liveness/Readiness Probes)
|
|
- DevExpressPdfHealthCheck (Smoke Test)
|
|
- Database Health Check (SQLite)
|
|
- Redis Health Check
|
|
|
|
**Steps:**
|
|
- ?? Step HC.1: DevExpressPdfHealthCheck
|
|
- ?? Step HC.2: Database Health Check
|
|
- ?? Step HC.3: Redis Health Check (optional)
|
|
|
|
---
|
|
|
|
### ??? Polly Resilience
|
|
|
|
**Was wird gebaut:**
|
|
- Retry Policy (3x mit Exponential Backoff)
|
|
- Circuit Breaker (nach 5 Fehlern 30s öffnen)
|
|
- Timeout Policy (30s max)
|
|
|
|
**Steps:**
|
|
- ?? Step PR.1: Polly Policies in DevExpressPdfProcessor
|
|
- ?? Step PR.2: Logging für Resilience Events
|
|
|
|
---
|
|
|
|
### ?? Logging & Monitoring
|
|
|
|
**Was wird gebaut:**
|
|
- Correlation IDs (X-Correlation-ID Header)
|
|
- Seq Sink (Log-Browsing UI)
|
|
- File Logging (Production)
|
|
- LoggingBehavior (MediatR Pipeline)
|
|
|
|
**Steps:**
|
|
- ?? Step LM.1: CorrelationIdMiddleware
|
|
- ?? Step LM.2: Seq Sink konfigurieren
|
|
- ?? Step LM.3: File Logging konfigurieren
|
|
- ?? Step LM.4: LoggingBehavior erweitern (Performance-Tracking)
|
|
|
|
---
|
|
|
|
## ?? 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 - später!)
|
|
- Wartbar an einer Stelle
|
|
|
|
---
|
|
|
|
## ??? 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)
|
|
|
|
---
|
|
|
|
### 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 (ApplyStamp, EmbedCertificate, 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;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 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
|
|
|
|
**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
|
|
|
|
**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)
|
|
```
|
|
|
|
---
|
|
|
|
## ?? 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.Sinks.Seq** | 8.0.0 | Log-Browsing UI (Development) |
|
|
| **Serilog.Enrichers.CorrelationId** | 3.0.1 | Correlation IDs für Request-Tracking |
|
|
| **Asp.Versioning.Http** | 8.1.1 | API Versioning (/api/v1/, /api/v2/) |
|
|
|
|
#### 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.) |
|
|
| **Polly** | 8.5.0 | Resilience (Retry, Circuit Breaker, Timeout) - SPÄTER! |
|
|
| **Microsoft.EntityFrameworkCore** | 8.0.0 | ORM für Tenant-Datenbank - SPÄTER! |
|
|
| **Microsoft.EntityFrameworkCore.Sqlite** | 8.0.0 | SQLite Provider - SPÄTER! |
|
|
| **BCrypt.Net-Next** | 4.0.3 | API-Key Hashing - SPÄTER! |
|
|
|
|
#### Domain Layer
|
|
|
|
| Package | Version | Purpose |
|
|
|---------|---------|---------|
|
|
| - | - | **Keine Dependencies!** (Clean Architecture) |
|
|
|
|
#### Tests
|
|
|
|
| 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 |
|
|
|
|
---
|
|
|
|
## ?? 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
|
|
??? ROADMAP.md ? This file
|
|
??? PHASENPLAN.md ? Project timeline
|
|
```
|
|
|
|
---
|
|
|
|
### ?? API Layer (DocumentOperator.API)
|
|
|
|
**Purpose:** HTTP Entry Point, Routing, Middleware
|
|
|
|
**Folder Structure:**
|
|
|
|
```
|
|
DocumentOperator.API/
|
|
??? Endpoints/
|
|
? ??? v1/
|
|
? ??? DocumentEndpoints.cs ? Minimal API Endpoints
|
|
??? Middleware/
|
|
? ??? ExceptionHandlingMiddleware.cs ? Zentrale Exception Handling ?
|
|
??? Configuration/
|
|
? ??? SwaggerConfiguration.cs ? Swagger Setup
|
|
??? appsettings.json ? Base Configuration
|
|
??? appsettings.Development.json ? Dev Overrides
|
|
??? Program.cs ? Application Entry Point
|
|
```
|
|
|
|
---
|
|
|
|
### ?? Application Layer (DocumentOperator.Application)
|
|
|
|
**Purpose:** Use Cases, Business Logic Orchestration
|
|
|
|
**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
|
|
? ? ??? LoggingBehavior.cs ? Structured Logging
|
|
? ??? DTOs/ ? Data Transfer Objects
|
|
? ??? ValidatePdfRequest.cs
|
|
? ??? ValidatePdfResponse.cs
|
|
??? DependencyInjection.cs ? Service Registration
|
|
```
|
|
|
|
---
|
|
|
|
### ?? Infrastructure Layer (DocumentOperator.Infrastructure)
|
|
|
|
**Purpose:** Technische Implementierungen
|
|
|
|
**Folder Structure:**
|
|
|
|
```
|
|
DocumentOperator.Infrastructure/
|
|
??? Services/
|
|
? ??? PdfProcessing/
|
|
? ??? DevExpressPdfProcessor.cs ? IPdfProcessor Implementation ?
|
|
??? Configuration/
|
|
? ??? DocumentOperatorSettings.cs ? Options Pattern Class
|
|
??? DependencyInjection.cs ? Service Registration
|
|
```
|
|
|
|
---
|
|
|
|
### ?? Domain Layer (DocumentOperator.Domain) - MINIMAL!
|
|
|
|
**Purpose:** Business Rules (nur was wirklich gebraucht wird!)
|
|
|
|
**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
|
|
```
|
|
|
|
---
|
|
|
|
### ?? Tests Layer (DocumentOperator.Tests)
|
|
|
|
**Purpose:** Unit & Integration Tests
|
|
|
|
**Folder Structure:**
|
|
|
|
```
|
|
DocumentOperator.Tests/
|
|
??? Unit/
|
|
? ??? Application/
|
|
? ? ??? Features/
|
|
? ? ??? ValidatePdf/
|
|
? ? ??? ValidatePdfHandlerTests.cs
|
|
? ??? Infrastructure/
|
|
? ? ??? Services/
|
|
? ? ??? DevExpressPdfProcessorTests.cs ?
|
|
? ??? Domain/
|
|
? ??? ValueObjects/
|
|
? ??? Base64StringTests.cs
|
|
??? Integration/
|
|
??? API/
|
|
??? DocumentEndpointsTests.cs
|
|
```
|
|
|
|
---
|
|
|
|
## ?? 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")
|
|
|
|
---
|
|
|
|
## ?? CURRENT STATUS
|
|
|
|
### ? Completed
|
|
|
|
- **Foundation & Domain Layer:**
|
|
- ? Solution Structure (4 Projekte)
|
|
- ? Dependencies (Clean Architecture Dependency Rule)
|
|
- ? Domain Exceptions (4 Exceptions)
|
|
- ? Enums (DocumentOperationType, ProcessingStatus)
|
|
- ? Value Objects (Base64String, TenantId, PdfMetadata)
|
|
|
|
- **Infrastructure Layer:**
|
|
- ? IPdfProcessor Interface
|
|
- ? DevExpressPdfProcessor.ValidateAsync (mit Tests!)
|
|
|
|
### ?? In Progress
|
|
|
|
- **Feature 1: ValidatePDF**
|
|
- ? Step 1.1: Application Layer (MediatR Setup + ValidatePDF Feature) - **NEXT**
|
|
|
|
### ? Pending
|
|
|
|
- **Feature 1: ValidatePDF**
|
|
- ? Step 1.2: API Layer (Endpoint + Exception Middleware)
|
|
- ? Step 1.3: Swagger Dokumentation
|
|
|
|
- **Feature 2-5:** ExtractAttachments, ApplyStamp, EmbedCertificate, ConcatenatePDFs
|
|
- **Cross-Cutting Concerns:** Multi-Tenancy, Health Checks, Polly, Logging
|
|
|
|
---
|
|
|
|
## ?? KEY LEARNINGS & DECISIONS
|
|
|
|
### 1. Feature-Driven Development statt Layer-by-Layer
|
|
|
|
**Entscheidung:** Jedes Feature komplett fertig (bis Swagger testbar)
|
|
|
|
**Warum:**
|
|
- ? Schnellerer Value (Feature 1 nach ~1 Tag fertig!)
|
|
- ? Klares Ziel (Swagger testbar = DONE)
|
|
- ? Weniger Komplexität (nicht alle Layer parallel)
|
|
- ? Besseres Lernen (Pattern wiederholt sich)
|
|
|
|
**Alternative wäre gewesen:**
|
|
- Domain komplett ? Infrastructure komplett ? Application komplett ? API komplett
|
|
- **Nachteile:** Viel Code ohne sichtbares Ergebnis, spekulativ
|
|
|
|
---
|
|
|
|
### 2. Domain Layer minimal halten
|
|
|
|
**Entscheidung:** Nur Enums + Value Objects + Exceptions
|
|
|
|
**Warum:**
|
|
- Domain = Business-Konzepte (technologie-unabhängig)
|
|
- Service-Anwendung (nicht Domain-lastig)
|
|
- YAGNI (You Ain't Gonna Need It)
|
|
|
|
**Was bedeutet das?**
|
|
- Domain/ValueObjects/TenantId.cs ? Value Object (immutable, validierend)
|
|
- Infrastructure/Data/Entities/Tenant.cs ? EF Core Entity (später!)
|
|
- Domain kennt KEINE EF Core Dependencies!
|
|
|
|
---
|
|
|
|
### 3. Multi-Tenancy NACH allen Features
|
|
|
|
**Entscheidung:** Erst alle synchronen Features, dann Multi-Tenancy
|
|
|
|
**Warum:**
|
|
- ? Multi-Tenancy betrifft ALLE Endpoints
|
|
- ? Einmal für alle Features (nicht 5x wiederholen!)
|
|
- ? Einfacher zu testen (erst ohne Tenancy, dann mit)
|
|
|
|
**Nachteile (akzeptiert):**
|
|
- ? Refactoring später nötig (alle Endpoints müssen X-API-Key Header bekommen)
|
|
- ? Aber: Aufwand überschaubar (Middleware erledigt das meiste!)
|
|
|
|
---
|
|
|
|
### 4. TDD beibehalten
|
|
|
|
**Entscheidung:** Test-First Development
|
|
|
|
**Warum:**
|
|
- ? Besseres Design (testbarer Code)
|
|
- ? Tests als Dokumentation
|
|
- ? Safety Net für Refactoring
|
|
|
|
---
|
|
|
|
### 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)
|
|
|
|
---
|
|
|
|
## ?? UPDATE LOG
|
|
|
|
| Date | Feature/Step | Changes |
|
|
|------|--------------|---------|
|
|
| 2024-XX-XX | Foundation | Project setup, dependencies, folder structure |
|
|
| 2024-XX-XX | Domain Layer | Exceptions, Enums, Value Objects |
|
|
| 17.01.2025 | Infrastructure | DevExpressPdfProcessor.ValidateAsync implementiert |
|
|
| 17.01.2025 | Tests | DevExpressPdfProcessorTests.cs erstellt (6 Tests) |
|
|
| 17.01.2025 | **ROADMAP** | ?? **Komplett umstrukturiert** (Feature-Driven Development!) |
|
|
| 17.01.2025 | **PHASENPLAN** | ?? **Komplett umstrukturiert** (Feature-basiert + Datum korrigiert) |
|
|
|
|
---
|
|
|
|
**END OF ROADMAP**
|
|
|
|
*This document is a living document and will be updated after each completed step.*
|