# ?? DocumentOperator - Project Roadmap (Feature-Driven Development) > **Last Updated:** 17.01.2025 | **Status:** In Development | **Current Feature:** Feature 1 - ValidatePDF ? ABGESCHLOSSEN! --- ## ?? 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 | ? Abgeschlossen | ? | | **2. ExtractSwissQrCode** | Synchron | ? Abgeschlossen | ? | | **3. ExtractAttachments** | Synchron | ? Pending | ? | | **4. ApplyStamp** | Synchron | ? Pending | ? | | **5. EmbedCertificate** | Synchron | ? Pending | ? | | **6. 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) - **In Progress** **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 (ABGESCHLOSSEN) **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) - **ABGESCHLOSSEN** **Ziel:** MediatR + FluentValidation + ValidatePDF Handler **Was wurde 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 `ILogger` (Clean Architecture konform!) #### 1.1.3: ValidatePDF Feature (Vertical Slice) - ? **Ordner:** `Application/Features/Documents/ValidatePdf/` - ? **Datei:** `ValidatePdfQuery.cs` ```csharp public record ValidatePdfQuery(Base64String PdfContent) : IRequest; ``` - ? **Datei:** `ValidatePdfHandler.cs` ```csharp public class ValidatePdfHandler : IRequestHandler { private readonly IPdfProcessor _processor; public async Task Handle(ValidatePdfQuery query, CancellationToken ct) { byte[] bytes = query.PdfContent.ToByteArray(); return await _processor.ValidateAsync(bytes); } } ``` - ? **Datei:** `ValidatePdfValidator.cs` ```csharp public class ValidatePdfValidator : AbstractValidator { 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_PdfProcessorThrowsException_PropagatesException` **Akzeptanzkriterien:** - ? Build erfolgreich - ? Tests grün (alle 2 Tests) - ? MediatR Pipeline funktioniert (Validation + Logging) --- ### ? Step 1.2: API Layer (Endpoint + Exception Middleware) - **ABGESCHLOSSEN** **Ziel:** HTTP Endpoint + zentrale Exception Handling **Was wurde erstellt:** #### 1.2.1: Exception Handling Middleware - ? **Datei:** `API/Middleware/ExceptionHandlingMiddleware.cs` - Fängt alle Exceptions - Mappt zu HTTP Status Codes (ValidationException ? 400, DomainValidationException ? 400, NotFoundException ? 404, PdfProcessingException ? 500) - Gibt RFC 7807 Problem Details zurück #### 1.2.2: Minimal API Endpoint - ? **Datei:** `API/Endpoints/v1/DocumentEndpoints.cs` - POST /api/v1/documents/validate - Nutzt MediatR (ValidatePdfQuery ? ValidatePdfHandler) - Returns ValidatePdfResponse (200) oder ProblemDetails (400, 500) #### 1.2.3: Infrastructure DependencyInjection - ? **Datei:** `Infrastructure/DependencyInjection.cs` - Registriert IPdfProcessor ? DevExpressPdfProcessor #### 1.2.4: Program.cs Updates - ? Application Layer registriert (AddApplication) - ? Infrastructure Layer registriert (AddInfrastructure) - ? Exception Middleware registriert (FIRST in pipeline!) - ? Endpoints registriert (MapDocumentEndpoints) #### 1.2.5: Integration Tests - ? **Datei:** `Tests/Integration/API/DocumentEndpointsTests.cs` - ? Test: `POST_ValidatePdf_ValidPdf_Returns200` - ? Test: `POST_ValidatePdf_InvalidBase64_Returns400` - ? Test: `POST_ValidatePdf_EmptyPdf_Returns400` **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 - **ABGESCHLOSSEN** **Ziel:** API-Dokumentation + Swagger UI testbar **Was wurde erstellt:** #### 1.3.1: Swagger Configuration - ? **Datei:** `API/Configuration/SwaggerConfiguration.cs` - `AddSwaggerDocumentation()` Extension Method - Swagger mit XML Comments konfiguriert - API-Titel, Version, Beschreibung gesetzt #### 1.3.2: XML Comments aktiviert - ? **Datei:** `API/DocumentOperator.API.csproj` ```xml true ``` #### 1.3.3: Endpoint dokumentiert - ? **Datei:** `API/Endpoints/v1/DocumentEndpoints.cs` - XML Comments für `ValidatePdf` Methode - Swagger-Annotationen (`.WithSummary()`, `.WithDescription()`, `.Produces<>()`) #### 1.3.4: DTOs dokumentiert - ? **Datei:** `Application/Common/DTOs/ValidatePdfRequest.cs` - XML Comments für Request-Schema - ? **Datei:** `Application/Common/DTOs/ValidatePdfResponse.cs` - XML Comments für Response-Schema - `FileSizeMB` Property hinzugefügt #### 1.3.5: Program.cs aktualisiert - ? **Datei:** `API/Program.cs` - `builder.Services.AddSwaggerDocumentation()` statt `AddSwaggerGen()` - `using DocumentOperator.API.Configuration;` hinzugefügt **Akzeptanzkriterien:** - ? Build erfolgreich - ? Alle Tests grün (11/11 Tests) - ? XML-Dokumentation wird generiert (`DocumentOperator.API.xml`) - ? Swagger UI zeigt Endpoint `/api/v1/documents/validate` mit Dokumentation - ? Request/Response-Schemas sind dokumentiert - ? Endpoint ist im Swagger UI testbar --- ### ? 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 (11/11) - ? Clean Architecture eingehalten - ? TDD angewendet - ? Swagger-Dokumentation vollständig **Nächstes Feature:** ? **Feature 3: ExtractAttachments** --- ## ?? FEATURE 2: ExtractSwissQrCode (Synchron) - **ABGESCHLOSSEN** **Was macht dieses Feature?** - Client sendet PDF als Base64 + Referenzen (Array von Strings) - API extrahiert Swiss QR Code von der **letzten Seite** des PDFs - API gibt Referenzen + alle QR Code Felder zurück (Swiss QR Bill Standard 2.0) **Endpoint:** ``` POST /api/v1/documents/extract-swiss-qr-code Request: { "references": ["REF-001", "REF-002"], "base64Pdf": "JVBERi0xLjQK..." } Response: { "references": ["REF-001", "REF-002"], "qrCodeData": { "qrType": "SPC", "version": "0200", "codingType": "1", "iban": "CH4431999123000889012", "creditor": { "name": "Robert Schneider AG", "addressType": "S", "street": "Rue du Lac", "buildingNumber": "1268", "postalCode": "2501", "city": "Biel", "country": "CH" }, "ultimateCreditor": null, "amount": 1949.75, "currency": "CHF", "ultimateDebtor": { "name": "Pia-Maria Rutschmann-Schnyder", "addressType": "S", "street": "Grosse Marktgasse", "buildingNumber": "28", "postalCode": "9400", "city": "Rorschach", "country": "CH" }, "referenceType": "QRR", "reference": "210000000003139471430009017", "unstructuredMessage": "Rechnung vom 15.01.2025", "billInformation": "//S1/01/...", "alternativeProcedureParameters": ["Name AV1: UV;UltraPay005;12345", "Name AV2: XY;XYService;54321"] } } ``` **Technologie:** - **DevExpress PDF Document API** (PDF-Zugriff, letzte Seite, QR Code Image) - **Codecrete.SwissQRBill.Generator** (Swiss QR Code Parsing - Standard 2.0) **Steps:** - ? Step 2.1: Domain Layer (SwissQrCodeData Value Object) - ? Step 2.2: Infrastructure Layer (IQrCodeProcessor + DevExpressSwissQrCodeProcessor) - ? Step 2.3: Application Layer (ExtractSwissQrCodeQuery + Handler + Validator) - ? Step 2.4: API Layer (Endpoint + Integration Tests) - ? Step 2.5: Swagger Dokumentation **Akzeptanzkriterien:** - ? QR Code wird von letzter Seite extrahiert - ? Alle Swiss QR Bill Felder werden geparst (Standard 2.0) - ? Referenzen werden durchgeschliffen (Echo) - ? Fehler wenn kein QR Code gefunden - ? Swagger-testbar - ? Tests grün (19/19) ### ? Feature 2 ABGESCHLOSSEN! **Was haben wir erreicht?** - ? ExtractSwissQrCode Feature komplett implementiert (Domain ? Infrastructure ? Application ? API ? Tests ? Swagger) - ? Endpoint ist im Swagger UI testbar: `POST /api/v1/documents/extract-swiss-qr-code` - ? Unit Tests + Integration Tests grün (19/19) - ? 3 Libraries integriert: DevExpress PDF, ZXing.Net, Codecrete.SwissQRBill.Generator - ? Vollständige Swiss QR Bill Standard 2.0 Unterstützung - ? Clean Architecture eingehalten - ? Swagger-Dokumentation vollständig **Nächstes Feature:** ? **Feature 3: ExtractAttachments** --- ## ?? FEATURE 3: 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 3.1: Infrastructure Layer (DevExpressPdfProcessor.ExtractAttachmentsAsync) - ?? Step 3.2: Application Layer (ExtractAttachmentsCommand + Handler + Validator) - ?? Step 3.3: API Layer (Endpoint) - ?? Step 3.4: Swagger Dokumentation --- ## ?? FEATURE 4: 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 4.1: Infrastructure Layer (DevExpressPdfProcessor.ApplyStampAsync) - ?? Step 4.2: Application Layer (ApplyStampCommand + Handler + Validator) - ?? Step 4.3: API Layer (Endpoint) - ?? Step 4.4: Swagger Dokumentation --- ## ?? FEATURE 5: 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 5.1: Infrastructure Layer (DevExpressPdfProcessor.EmbedCertificateAsync) - ?? Step 5.2: Application Layer (EmbedCertificateCommand + Handler + Validator) - ?? Step 5.3: API Layer (Endpoint) - ?? Step 5.4: Swagger Dokumentation --- ## ?? FEATURE 6: 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 6.1: Infrastructure Layer (In-Memory Queue + Background Worker) - ?? Step 6.2: Application Layer (SubmitConcatenateJobCommand + GetJobStatusQuery) - ?? Step 6.3: API Layer (Async Endpoints) - ?? Step 6.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; // Handler public class ValidatePdfHandler : IRequestHandler { private readonly IPdfProcessor _processor; public async Task 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 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!) - ? DependencyInjection.cs (Infrastructure Services) - **Application Layer:** - ? DependencyInjection.cs (MediatR + FluentValidation) - ? ValidationBehavior.cs (FluentValidation Pipeline) - ? LoggingBehavior.cs (ILogger Pipeline) - ? ValidatePDF Feature (Query, Handler, Validator) - ? DTOs (ValidatePdfRequest, ValidatePdfResponse) - ? Tests (ValidatePdfHandlerTests - 2 Tests grün) - **API Layer:** - ? ExceptionHandlingMiddleware.cs (RFC 7807 Problem Details) - ? DocumentEndpoints.cs (POST /api/v1/documents/validate) - ? Program.cs (Services + Middleware + Endpoints) - ? Tests (DocumentEndpointsTests - 3 Tests grün) ### ?? In Progress - **Feature 1: ValidatePDF** - ? Step 1.3: Swagger Dokumentation - **NEXT** ### ? Pending - **Feature 1: ValidatePDF** - ? 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) | | 17.01.2025 | **Feature 1 - Step 1.1** | ? **ABGESCHLOSSEN** - Application Layer (MediatR, Behaviors, ValidatePDF Feature, DTOs, Tests) | | 17.01.2025 | **Feature 1 - Step 1.2** | ? **ABGESCHLOSSEN** - API Layer (ExceptionMiddleware, Endpoint, Program.cs, Integration Tests - 3/3 grün) | | 17.01.2025 | **Feature 1 - Step 1.3** | ? **ABGESCHLOSSEN** - Swagger Dokumentation (SwaggerConfiguration, XML Comments, Endpoint/DTO-Dokumentation - 11/11 Tests grün) | | 17.01.2025 | **Feature 1** | ? **KOMPLETT ABGESCHLOSSEN** - ValidatePDF Feature testbar im Swagger UI! | | 17.01.2025 | **Fix: Attachment Detection (Multiple Attachments)** | ? **KORRIGIERT** - ValidatePDF erkennt jetzt auch PDFs mit mehreren Attachments korrekt (globale Suche statt 1000-Zeichen-Limit) - 13/13 Tests grün | | 17.01.2025 | **Fix: Attachment Count (6 Attachments)** | ? **KORRIGIERT** - AttachmentCount wird jetzt korrekt gezählt (objectCount statt objectCount/2). PDFs mit 6 Attachments werden korrekt erkannt - 13/13 Tests grün | | 17.01.2025 | **ROADMAP** | ?? **Feature-Reihenfolge geändert** - Neues Feature 2: ExtractSwissQrCode (Swiss QR Bill Standard 2.0) eingefügt. Alte Features 2-5 werden zu Features 3-6. | | 17.01.2025 | **Feature 2 - Step 2.1** | ? **ABGESCHLOSSEN** - Domain Layer (SwissQrCodeData, AddressData Value Objects, SwissQrCodeNotFoundException) | | 17.01.2025 | **Feature 2 - Step 2.2** | ? **ABGESCHLOSSEN** - Infrastructure Layer (ISwissQrCodeProcessor, DevExpressSwissQrCodeProcessor, Libraries: Codecrete.SwissQRBill.Generator, ZXing.Net, System.Drawing.Common) | | 17.01.2025 | **Feature 2 - Step 2.3** | ? **ABGESCHLOSSEN** - Application Layer (Query, Handler, Validator, DTOs, Unit Tests - 2/2 grün) | | 17.01.2025 | **Feature 2 - Step 2.4** | ? **ABGESCHLOSSEN** - API Layer (Endpoint /extract-swiss-qr-code, Exception Handling, Integration Tests - 4/4 grün) | | 17.01.2025 | **Feature 2 - Step 2.5** | ? **ABGESCHLOSSEN** - Swagger Dokumentation (XML Comments, Request/Response Beispiele, Endpoint-Beschreibung) | | 17.01.2025 | **Feature 2** | ? **KOMPLETT ABGESCHLOSSEN** - ExtractSwissQrCode Feature testbar im Swagger UI! (19/19 Tests grün) | --- **END OF ROADMAP** *This document is a living document and will be updated after each completed step.*