Files
Services.DDDocumentOperator/DocumentOperator.API/ROADMAP.md

41 KiB

?? DocumentOperator - Project Roadmap (Pragmatic Edition)

Last Updated: 17.01.2025 | Status: In Development | Phase: 2 (Domain Layer - Minimal)


?? TABLE OF CONTENTS

  1. Project Overview
  2. Architecture & Design Decisions
  3. Development Philosophy
  4. Technology Stack
  5. Project Structure
  6. Development Roadmap
  7. Testing Strategy
  8. 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:

// 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 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:

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:

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:

  • Solution erstellt (4 Projekte)
  • Dependencies korrekt (Clean Architecture Dependency Rule)
  • NuGet Packages installiert
  • Folder-Struktur erstellt
  • appsettings.json konfiguriert
  • Options Pattern Classes erstellt
  • 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. DomainException.cs (Basis-Exception)
  2. DomainValidationException.cs (Value Object Validierung)
  3. NotFoundException.cs (Resource nicht gefunden)
  4. 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:
      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:
      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:
    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)

    [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)

    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

    public record ValidatePdfQuery(Base64String PdfContent) : IRequest<PdfMetadata>;
    
  2. ValidatePdfHandler.cs

    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)

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

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

// 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
    • ?? Step 3.2 - DevExpressPdfProcessor implementieren (TDD - IN PROGRESS)
      • ? Step 3.2.1 - ProcessDocument Ordner gelöscht (Application Layer cleanup)
      • ? Step 3.2.2 - Test-Ordnerstruktur erstellt (Unit/Infrastructure/Services/PdfProcessing)
      • ? Step 3.2.3 - Test-PDF Datei hinzugefügt (valid.pdf als Embedded Resource)

?? In Progress

  • Phase 3, Step 3.2: DevExpressPdfProcessor (TDD)
    • NEXT: Step 3.2.4 - DevExpressPdfProcessorTests.cs erstellen (TDD Red Phase)
    • Progress: 3/7 Mini-Steps abgeschlossen

? 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. Infrastructure Services:

    • Ordner existieren (PdfProcessing, FileStorage, DocumentValidation)
    • Aber: Alle leer
    • ?? Action: DevExpressPdfProcessor.cs implementieren (Step 3.2 - IN PROGRESS)
  2. 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 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

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
17.01.2025 Phase 3 ?? Step 3.2 gestartet - DevExpressPdfProcessor (TDD)
17.01.2025 Application ? Step 3.2.1 - ProcessDocument Ordner gelöscht (Cleanup)
17.01.2025 Tests ? Step 3.2.2 - Test-Ordnerstruktur erstellt, UnitTest1.cs gelöscht
17.01.2025 Tests ? Step 3.2.3 - Test-PDF (valid.pdf) als Embedded Resource hinzugefügt

END OF ROADMAP

This document is a living document and will be updated as development progresses.