Projekt: TransferX.Api

Basis Dokumente: Read Me, Clean Architecture, Coding Standards and Best Practices und TransferX Architektur

Rolle in der Architektur: Presentation Layer – REST-API für externe Systeme, Automatisierung und Integrationen. Kommuniziert ausschliesslich über Application Features (CQS-Muster) mit der Business-Logik. Enthält keine Geschäftslogik.

Projektstruktur

TransferX.Api
│   appsettings.json
│   appsettings.Development.json
│   Program.cs
│   TransferX.Api.csproj
│
├───Controllers
│       ProvidersController.cs
│       TransferConfigsController.cs
│       TransfersController.cs
│
├───Extensions
│       ResultExtensions.cs
│
└───Hubs
        ProgressHub.cs

Abhängigkeiten

graph TD
	API["TransferX.Api (Composition Root, Controller)"] 
	App["TransferX.Application (Features, IProviderEngine, ITransferEngine, DTOs)"] 
	Core["TransferX.Core (ProviderEngine, TransferEngine, DI-Extensions)"] 
	Infra["TransferX.Infrastructure (Repositories, SignalR, Security, DI-Extensions)"]
	
	API -->|"referenziert (Use Cases aufrufen)"| App
	API -->|"referenziert (DI registrieren)"| Core
	API -->|"referenziert (DI registrieren)"| Infra

	style API fill:#ffff4,stroke:#f1f12d,stroke-width:2px
	style App fill:#fff9c4,stroke:#fbc02d,stroke-width:2px
	style Core fill:#e1f5fe,stroke:#01579b,stroke-width:2px
	style Infra fill:#ffe0b2,stroke:#e65100,stroke-width:2px
Paket / Projekt Version Zweck
TransferX.Application Features (Commands/Queries), Interfaces, Models
TransferX.Core Engine-Implementierungen (ProviderEngine, TransferEngine)
TransferX.Infrastructure Repositories, SignalR Reporter, Security
Microsoft.AspNetCore.OpenApi 8.0.23 OpenAPI-Unterstützung
Microsoft.AspNetCore.SignalR 1.2.9 Echtzeit-Fortschrittsmeldungen via WebSocket
Swashbuckle.AspNetCore 10.1.3 Swagger UI und OpenAPI-Dokumentation

Konfiguration (appsettings)

Die API liest ihre Konfiguration aus appsettings.json bzw. appsettings.Development.json:

Schlüssel Beschreibung Beispielwert (Development)
TransferX:DataDir Verzeichnis für persistierte Daten C:\Data\Projects\TransferX\Data
TransferX:ProviderPluginDir Verzeichnis für Provider-Plugins (.dll) C:\Data\Projects\TransferX\Plugins\Providers
TransferX:TransferPluginDir Verzeichnis für Transfer-Plugins (.dll) C:\Data\Projects\TransferX\Plugins\Transfers
TransferX:Security:AesKey AES-Schlüssel (Base64, 32 Byte) für Credentials (Base64-Wert)
TransferX:Security:AesIv AES-IV (Base64, 16 Byte) für Credentials (Base64-Wert)

Sicherheitshinweis: AesKey und AesIv müssen in Produktionsumgebungen durch sichere Secret-Management-Lösungen (z.B. Azure Key Vault, User Secrets) ersetzt werden. Die Platzhalterwerte in appsettings.json dürfen nicht in Produktion verwendet werden.

Composition Root (Program.cs)

Program.cs ist der einzige Ort, an dem Abhängigkeiten registriert werden (Composition Root). Alle ICommandHandler<,> und IQueryHandler<,> werden explizit per AddScoped registriert.

graph TD
    PR["Program.cs (Composition Root)"]
    Infra["AddTransferXInfrastructure"]
    Core["AddTransferXCore"]
    AppCmds["Application Commands / Queries (Scoped)"]
    SignalR["SignalR"]
    Swagger["Swagger / OpenAPI"]
    MW["Middleware Pipeline"]

    PR --> Infra
    PR --> Core
    PR --> AppCmds
    PR --> SignalR
    PR --> Swagger
    PR --> MW
    
    style PR fill:#f9f,stroke:#333,stroke-width:2px
    style Infra fill:#ffe0b2,stroke:#e65100
    style Core fill:#e1f5fe,stroke:#01579b
    style AppCmds fill:#fff9c4,stroke:#fbc02d

Middleware-Pipeline

Middleware Bedingung Beschreibung
UseSwagger Development only Swagger JSON-Endpoint
UseSwaggerUI Development only Swagger-Oberfläche unter /swagger
UseHttpsRedirection immer Umleitung auf HTTPS
UseAuthorization immer Autorisierungs-Middleware
MapControllers immer Routet HTTP-Anfragen an Controller
MapHub<ProgressHub> immer SignalR-Hub unter /hubs/progress

Controllers

Alle Controller erben von ControllerBase und sind mit [ApiController] und [Route(...)] dekoriert. Sie delegieren vollständig an Application-Features – keine Geschäftslogik im Controller.

ProvidersController – api/providers

Verwaltet registrierte Provider-Konfigurationen und Provider-Operationen.

Methode Route HTTP Rückgabe Beschreibung
GetAll api/providers GET 200 IReadOnlyList<ProviderDto> Alle registrierten Provider
GetById api/providers/{id} GET 200 ProviderDto / 404 Details eines einzelnen Providers
GetPlugins api/providers/plugins GET 200 IReadOnlyList<ProviderPluginDto> Verfügbare Provider-Plugins im Plugin-Verzeichnis
Browse api/providers/{id}/browse GET 200 BrowseResultDto / 400 Ordner und Dateien unter einem Pfad auflisten
Register api/providers POST 200 Guid / 400 Neuen Provider registrieren
Update api/providers/{id} PUT 204 / 400 Konfiguration und Zugangsdaten aktualisieren
Delete api/providers/{id} DELETE 204 / 400 Provider löschen
TestConnection api/providers/{id}/test POST 204 / 400 Verbindung zum Provider testen

Request-Records:

Record Properties Beschreibung
UpdateProviderConfigRequest Name, Username, Password Body für Update

TransferConfigsController – api/transfer-configs

Verwaltet gespeicherte Transfer-Konfigurationen (wiederverwendbare Quell/Ziel-Definitionen).

Methode Route HTTP Rückgabe Beschreibung
GetAll api/transfer-configs GET 200 IReadOnlyList<TransferConfigDto> Alle Transfer-Konfigurationen
Save api/transfer-configs POST 200 Guid / 400 Neue Konfiguration erstellen
Rename api/transfer-configs/{id}/rename PATCH 204 / 400 Konfiguration umbenennen
Delete api/transfer-configs/{id} DELETE 204 / 400 Konfiguration löschen

Request-Records:

Record Properties Beschreibung
SaveTransferConfigRequest Name, SourceProviderId, SourcePath, TargetProviderId, TargetPath, Operation Body für Save
RenameTransferConfigRequest NewName Body für Rename

TransfersController – api/transfers

Steuert Transfer-Ausführung, Status und Fortschrittsabfragen.

Methode Route HTTP Rückgabe Beschreibung
GetAll api/transfers GET 200 IReadOnlyList<TransferListDto> Alle gespeicherten Transfers
GetStatus api/transfers/{id}/status GET 200 TransferStatusDto / 404 Aktueller Status eines Transfers
GetProgress api/transfers/{id}/progress GET 200 TransferProgressDto / 404 Aktueller Fortschritt eines laufenden Transfers
Start api/transfers/start POST 200 Guid / 400 Transfer direkt starten (Fire-and-Forget)
StartConfig api/transfers/start-config/{configId} POST 200 Guid / 404 / 400 Transfer anhand einer gespeicherten Konfiguration starten
Cancel api/transfers/{id}/cancel POST 204 / 400 Laufenden oder ausstehenden Transfer abbrechen

Request-Records:

Record Properties Beschreibung
StartTransferRequest SourceProviderId, SourcePath, TargetProviderId, TargetPath, Operation Body für Start

Extensions

ResultExtensions

Erweiterungsmethoden zur Umwandlung von Application-Result-Objekten in HTTP-Antworten. Zentralisieren die Mapping-Logik und vermeiden Wiederholungen in Controllers.

Methode Eingabe Erfolg Fehler
ToActionResult<TValue> Result<TValue> 200 OK 400 BadRequest
ToActionResult OperationResult 204 NoContent 400 BadRequest
ToNotFoundActionResult<TValue> Result<TValue> 200 OK 404 NotFound
graph LR
    R1["Result&lt;T&gt;.IsSuccess"]
    R2["Result&lt;T&gt;.IsFailure"]
    R3["OperationResult.IsSuccess"]
    R4["OperationResult.IsFailure"]

    R1 -->|"ToActionResult"| OK["200 OK + Value"]
    R2 -->|"ToActionResult"| BR["400 BadRequest + error"]
    R1 -->|"ToNotFoundActionResult"| OK2["200 OK + Value"]
    R2 -->|"ToNotFoundActionResult"| NF["404 NotFound + error"]
    R3 -->|"ToActionResult"| NC["204 NoContent"]
    R4 -->|"ToActionResult"| BR2["400 BadRequest + error"]

SignalR – ProgressHub

Der ProgressHub (/hubs/progress) ermöglicht Echtzeit-Fortschrittsmeldungen für laufende Transfers.

Client-Methoden (aufrufbar vom Client)

Methode Parameter Beschreibung
SubscribeToTransfer Guid transferId Client abonniert Fortschritt für einen bestimmten Transfer
UnsubscribeFromTransfer Guid transferId Client kündigt Abonnement für einen Transfer

Server-Events (vom Server an Client gesendet)

Event Beschreibung
ProgressUpdate Fortschrittsmeldung für einen Transfer (via IHubContext)

Ablauf

sequenceDiagram
    participant C as SignalR Client
    participant H as ProgressHub
    participant E as TransferEngine
    participant R as IProgressReporter

    C->>H: Verbinden (/hubs/progress)
    C->>H: SubscribeToTransfer(transferId)
    H->>H: AddToGroup(connectionId, transferId)
    
    Note over E,R: Transfer läuft im Hintergrund
    E->>R: ReportAsync(progress)
    R->>C: ProgressUpdate (Push via Gruppe)
    
    C->>H: UnsubscribeFromTransfer(transferId)
    H->>H: RemoveFromGroup(connectionId, transferId)

Swagger / OpenAPI

Swagger ist im Development-Modus aktiv (/swagger). Die XML-Dokumentation (/// summary, /// param etc.) wird über IncludeXmlComments eingebunden.

Einstellung Wert
Swagger-Titel TransferX API
Version v1
XML-Kommentare aus Assembly-XML-Datei
Erreichbar (Development) /swagger

XML-Dokumentation wird automatisch beim Build erzeugt (<GenerateDocumentationFile>true</GenerateDocumentationFile>).

Tests (TransferX.Api.Tests)

Das Testprojekt TransferX.Api.Tests enthält Unit Tests für Controllers und Extensions.

Teststruktur

TransferX.Api.Tests
├───Controllers
│       ProvidersControllerTests.cs
│       TransferConfigsControllerTests.cs
│       TransfersControllerTests.cs
│
├───Extensions
│       ResultExtensionsTests.cs
│
└───Fakes
        FakeCommandHandler.cs
        FakeQueryHandler.cs

Test-Strategie

Controller werden direkt instanziiert (kein HTTP-Stack, kein DI-Container). ICommandHandler und IQueryHandler werden durch generische Fakes ersetzt:

Fake-Klasse Implementiert Beschreibung
FakeCommandHandler<TCommand, TResult> ICommandHandler<TCommand, TResult> Gibt konfigurierbares Result zurück, zeichnet empfangenen Command auf
FakeQueryHandler<TQuery, TResult> IQueryHandler<TQuery, TResult> Gibt konfigurierbares Result zurück, zeichnet empfangene Query auf

Test-Abdeckung

Testklasse Getestete Szenarien
ProvidersControllerTests GetAll, GetById (gefunden/nicht gefunden), GetPlugins, Browse, Register, Update, Delete, TestConnection (je Erfolg/Fehler)
TransferConfigsControllerTests GetAll, Save, Rename, Delete (je Erfolg/Fehler)
TransfersControllerTests GetAll, GetStatus, GetProgress, Start, StartConfig, Cancel (je Erfolg/Fehler)
ResultExtensionsTests ToActionResult<T>, ToActionResult (OperationResult), ToNotFoundActionResult<T> (je Erfolg/Fehler)

Testframework

Paket Version Zweck
MSTest.Sdk 3.6.4 Testframework (MSTest)
Microsoft.AspNetCore.Mvc.Testing 8.0.0 WebApplicationFactory für Integrationstests

Sequenz: Transfer starten via API

sequenceDiagram
    participant Client as HTTP Client
    participant TC as TransfersController
    participant H as StartTransferHandler
    participant R as ITransferRepository
    participant E as ITransferEngine
    participant SH as ProgressHub (SignalR)

    Client->>TC: POST /api/transfers/start
    TC->>H: HandleAsync(StartTransferCommand)
    H->>R: AddAsync(transfer)
    H->>R: UpdateAsync(transfer – Running)
    H-->>TC: Result (Transfer-ID)
    TC-->>Client: 200 OK (Transfer-ID)
    
    Note over H,E: Fire-and-Forget im Hintergrund
    H-)E: ExecuteAsync(config, progress)
    E->>SH: IProgressReporter.ReportAsync(progress)
    SH-->>Client: ProgressUpdate (SignalR Push)
    E-->>H: TransferResultDto
    H->>R: UpdateAsync(transfer – Completed/Failed)

Architecture Decision Records (ADR)

Entscheidung Kontext Begründung
Keine MediatR-Abhängigkeit Commands/Queries direkt via ICommandHandler / IQueryHandler Kein Framework-Overhead, direkte Testbarkeit, Kontrolle über DI-Registrierung
Explizite DI-Registrierung Alle Handler in Program.cs per AddScoped registriert Sichtbar, nachvollziehbar, kein Magic-Scanning
ResultExtensions statt Controller-Logik HTTP-Status-Mapping zentralisiert in ResultExtensions DRY-Prinzip, einheitliche HTTP-Semantik in allen Controllers
Fire-and-Forget bei Start Transfer-Ausführung ist langläufig Sofortige Rückgabe der Transfer-ID, Fortschritt via SignalR oder /progress
SignalR für Echtzeit-Fortschritt Polling ist ineffizient für grosse Transfers Push-basiertes Update, geringer Client-Overhead
Swagger nur in Development Produktions-APIs sollen keine Swagger-Oberfläche exponieren Sicherheit und Reduktion der Angriffsfläche
InternalsVisibleTo für Tests FakeCommandHandler / FakeQueryHandler sind internal Testzugriff ohne public, keine unnötige API-Exposition

Qualitätsmerkmale

Aspekt Umsetzung
Keine Geschäftslogik Controller delegieren vollständig an Application Features
CQS-Muster ICommandHandler / IQueryHandler trennen Schreib- und Lesezugriffe
Result-Muster Result<T> und OperationResult – keine Exceptions für erwartbare Fehler
Async First Alle Controller-Methoden async mit CancellationToken
XML-Dokumentation Alle public Members dokumentiert, in Swagger eingebunden
Nullable Reference Types Aktiviert – optionale Properties explizit als ? deklariert
Sealed Controller Alle Controller sealed – kein unbeabsichtigtes Erben
Sealed Request-Records Alle Request-Bodies als sealed record – immutabel, strukturell vergleichbar
Unit Tests Controller direkt instanziiert, kein HTTP-Stack, schnelle Ausführung