Architektur- und Technologie-Übersicht

Dieses Projekt ist ein ASP.NET Core Backend, das in C# entwickelt wurde.
Es handelt sich um eine REST API, die sowohl klassische Endpunkte für Clients bereitstellt als auch einzelne * dauerhaft laufende Komponenten* (sogenannte Hosted Services).
Getestet wird mit xUnit als eigenständigem Testprojekt.


Datenbank-Kommunikation

Für die Datenbank-Anbindung wird Entity Framework Core (EF Core) genutzt.
EF Core ist ein ORM (Object-Relational Mapper), das es erlaubt, Datenbanktabellen als Klassen (Entities) im Code darzustellen.
Statt rohe SQL-Abfragen zu schreiben, wird mit LINQ-Abfragen gearbeitet, die EF Core in SQL übersetzt.

👉 Im Code suchen nach:

  • ApplicationDbContext (zentraler Einstiegspunkt für EF Core)
  • DbSet<T> (eine Tabelle in Form einer Liste von Objekten)
  • UseSqlServer(...) oder UseInMemoryDatabase(...)

Architektur: Controller – Service – Repository

Das Projekt folgt einem klassischen 3-Schichten-Modell:

  1. Controller

    • Repräsentieren die API-Endpunkte.
    • Entgegennehmen von HTTP-Requests, Rückgabe von HTTP-Responses.
    • Enthalten wenig bis keine Geschäfts-Logik, sondern leiten Aufrufe an Services weiter und reagiert auf Ergebnisse.
    • Controller orchestrieren services und verarbeiten Web-Logik.

    👉 Im Code suchen nach:

    • Klassen mit Controller-Suffix
    • [HttpGet], [HttpPost], [HttpPut] usw.
  2. Services

    • Enthalten die Business-Logik (das „Wie“ der Prozesse).
    • Überprüfen, ob Aktionen erlaubt sind, verarbeiten Daten, orchestrieren Abläufe.
    • Rufen Repositories auf, um Daten zu speichern oder zu laden.

    👉 Im Code suchen nach:

    • Klassen mit Service-Suffix
    • Interfaces wie IExhibitorService
  3. Repositories

    • Direkter Zugriff auf die Datenbank.
    • Alle SQL-/EF-Core-Operationen sind hier gekapselt.
    • Controller und Services sprechen nie direkt mit EF Core, sondern immer über Repositories.

    👉 Im Code suchen nach:

    • Klassen mit Repository-Suffix
    • Interfaces wie IExhibitorRepository

Dependency Injection (DI)

Das Projekt nutzt Dependency Injection (DI), ein grundlegendes Muster in ASP.NET Core.
Statt Klassen direkt zu instanziieren, werden sie vom Framework automatisch mit ihren Abhängigkeiten versorgt.

Beispiel:

builder.Services.AddScoped<IExhibitorService, ExhibitorService>();

Das bedeutet: Immer wenn ein IExhibitorService gebraucht wird, bekommt man eine Instanz von ExhibitorService. So sind Klassen entkoppelt und leichter testbar.

👉 Im Code suchen nach:

  • builder.Services.AddScoped
  • AddSingleton, AddTransient

Basisklassen & Vererbung

Viele Services und Repositories erben von Basisklassen, die gemeinsame Funktionalität bereitstellen. Diese Basisklassen enthalten generische Methoden mit sogenannten Dynamics. Dynamics sind dynamisch typisierte Objekte in C#, die zur Laufzeit geprüft werden. Damit können universelle Low-Level-Funktionen implementiert werden, die von allen erbenden Klassen genutzt werden.

👉 Im Code suchen nach:

  • BaseService, BaseRepository
  • Schlüsselwort dynamic

Extension Methods

Ein weiteres genutztes Feature sind Extension Methods. Das sind Erweiterungsmethoden für bestehende Klassen oder Typen, ohne dass man diese ändern muss. So können auch „geschlossene“ Klassen um neue Funktionen ergänzt werden.

Beispiel:

public static class StringExtensions
{
    public static bool IsValidEmail(this string input)
    {
        return input.Contains("@");
    }
}

Hier wird dem Typ string die neue Funktion IsValidEmail() hinzugefügt. Im Code kann man dann schreiben:

var mail = "test@example.com";
if (mail.IsValidEmail()) { ... }

👉 Im Code suchen nach:

  • Klassen mit Extensions im Namen
  • Schlüsselwort this im Methodenparameter

Hosted Services

Zusätzlich zu den API-Endpunkten laufen im Hintergrund Hosted Services. Das sind Prozesse, die dauerhaft aktiv sind, zum Beispiel:

  • Warteschlangen abarbeiten
  • Cronjobs ausführen
  • Synchronisationen durchführen

Sie werden beim Start der Anwendung registriert und vom Framework parallel betrieben.

👉 Im Code suchen nach:

  • Klassen, die IHostedService oder BackgroundService implementieren

Tests

Das Projekt nutzt xUnit als Framework für Unit- und Integrationstests. Dazu gibt es ein eigenes Testprojekt (messeapp.Backend.Test). Tests überprüfen, ob Controller, Services und Repositories wie gewünscht zusammenarbeiten.

👉 Im Code suchen nach:

  • [Fact] (einzelner Test)
  • [Theory] (Test mit Datenvarianten)
  • TestClientFactory (wird genutzt, um Test-APIs mit InMemory-Datenbanken aufzusetzen)

Konfigurationsvalidierung (IValidateOptions<T>)

Um Laufzeitfehler durch fehlerhafte Konfigurationen zu vermeiden, werden alle kritischen Konfigurationsabschnitte bereits beim Start der Anwendung über das IValidateOptions<T>-Muster überprüft.
Beispielsweise implementiert die Klasse ChefsInspirationEventImportOptions dieses Interface, um sicherzustellen, dass alle erforderlichen Felder (wie ApiKey, ChefsInspirationHost und CutoffInYears) korrekt gesetzt und logisch konsistent sind.
Schlägt die Validierung fehl, startet die Anwendung nicht – falsche oder unvollständige Umgebungsvariablen werden somit bereits beim Deployment oder lokalen Start erkannt.
Dieses Vorgehen sorgt für saubere Konfigurationen und ein vorhersehbares Laufzeitverhalten über alle Umgebungen hinweg.


Authentifizierungsschema

Die API verwendet ein hybrides Authentifizierungssystem, das JWT-Bearer-Tokens und API-Keys kombiniert.

  • JWT-Tokens werden über die Endpunkte /api/v1.0/Auth/User oder /api/v1.0/Auth/ApiKey ausgestellt und repräsentieren entweder Benutzer oder Systemidentitäten.
  • API-Keys werden nicht direkt zur Authentifizierung verwendet, sondern müssen zunächst über den entsprechenden Auth-Endpunkt gegen ein JWT eingetauscht werden.
  • Das Standard-Authentifizierungsschema ist Bearer und wird durch den integrierten ASP.NET Core JWT-Handler verwaltet.
    Zusätzliche prüfungs- oder scope-bezogene Logik (z. B. API-Key-Scopes) wird durch eine eigene Middleware realisiert.

Dieses einheitliche Authentifizierungskonzept sorgt dafür, dass alle Requests – unabhängig davon, ob sie von Benutzern oder Systemen stammen – denselben standardisierten, tokenbasierten Authentifizierungsfluss durchlaufen.
Dadurch können Audit-Logs, Claims und Zugriffsprüfungen zentral und konsistent gehandhabt werden.


Hot Reloading der API-Key-Konfiguration

Die Definitionen der API-Keys werden in einer separaten Konfigurationsdatei (appsettings.ApiKey.json) gespeichert und über IOptionsMonitor<ApiKeySchemeOptions> geladen.
Durch den Einsatz von IOptionsMonitor ist Hot Reloading möglich – sobald sich die Datei ändert, wird die neue Konfiguration automatisch übernommen, ohne dass die Anwendung neu gestartet werden muss.
Administratoren können so API-Keys zur Laufzeit hinzufügen, anpassen oder sperren, und die Authentifizierungsmiddleware arbeitet sofort mit dem aktualisierten Stand (optionsMonitor.CurrentValue).
Dieses Design ermöglicht es, laufende Systeme flexibel an neue betriebliche oder sicherheitsrelevante Anforderungen anzupassen, ohne Downtime oder Redeployment.