Ein Kontext mit eigener Bibliotheksgruppe im Front End, aber einer einzigen großen Datei im Server, ist nur halb geschnitten. Diese Lektion zieht die Teilung bis zur API durch: eine Gruppe von Endpunkten je Kontext, ein Vertrag je Gruppe, ein Client je Vertrag. Dazu kommt die Frage nach einer Schicht, die Daten mehrerer Kontexte zusammenstellt (Backend for Frontend, BFF). Die .NET-Entwicklung der API lehrt K2 (2.3, 2.12); die Vertragsidee aus W9 0203 wird vorausgesetzt.

Stand der Prüfung. Ausgeführt in einem Wegwerf-Projekt außerhalb des Repositorys: ein ASP.NET-Core-10-Projekt (SDK 10.0.400, Microsoft.AspNetCore.OpenApi 10.0.15) mit zwei MapGroup-Gruppen und zwei Dokumenten, dotnet build -m:1 (Exit 0, 0 Warnungen), Start der API und curl der Dokumente; ng-openapi-gen 1.1.0 für beide Dokumente im Nx-Workspace aus 0202 (Exit 0), tsc --noEmit über beide Datenzugriff-Bibliotheken (zuerst Exit 2, nach der Behebung Exit 0), nx run-many -t lint über 13 Projekte (Exit 0); eine BFF-Route, die die API über HttpClient gegen sich selbst aufruft (Ausgabe unten). Nicht ausgeführt: die Umbenennung company zu firma aus Übung 1, Anmeldung, Token, Bereiche und jede Autorisierung, ein BFF als eigener Dienst, Azure, ein Browser, eine Anwendung gegen die API. Gelesen im Text: die Learn-Seiten „Backends for Frontends Pattern“ und „Use Domain Analysis to Model Microservices“ und der Beitrag von angulararchitects.io. Die Learn-Seite zu OpenAPI in ASP.NET Core wurde nicht gelesen: Zu AddOpenApi und WithGroupName steht hier, was bei uns lief (offen: was die Seite verspricht).

Eine Gruppe je Kontext

In 0201 und 0202 sind Kontakte, Termine und Ausschreibungen getrennte Bibliotheksgruppen. Der Server der Kladde ist eine Anwendung (K4 4.3.6: ein modularer Monolith), und dort entspricht einem Kontext eine Gruppe von Endpunkten mit einem Präfix und einem Namen. Der Beitrag von angulararchitects.io merkt an, dass Front-End-Schnitte oft die des Servers spiegeln; schneiden beide verschieden, kann ein BFF übersetzen, und die Teams verlieren Eigenständigkeit. Ein Schnitt, der auf beiden Seiten gleich ist, erspart diese Übersetzung. So sieht die Gruppe der Kontakte aus (Contacts/ContactsApi.cs, gekürzt auf das Wesentliche; die Beispieldaten sind fest):

C#
public static IEndpointRouteBuilder MapContactsApi(this IEndpointRouteBuilder app)
{
    var group = app.MapGroup("/api/contacts")
        .WithGroupName("contacts")
        .WithTags("contacts");

    group.MapGet("/", (int page = 1, int size = 20) => /* ContactsPage */)
        .WithName("ListContacts");
    group.MapGet("/{id:int}", (int id) => /* ContactSummary */)
        .WithName("GetContact");
    return app;
}

Die Gruppe der Termine (Appointments/AppointmentsApi.cs) hat denselben Aufbau mit "/api/appointments" und dem Gruppennamen appointments. Jede Gruppe liegt in einem eigenen Ordner mit eigenen Typen: ContactSummary und ContactsPage im einen, AppointmentResponse im anderen. Kein Typ wird zwischen den Gruppen geteilt, so wie die Termine in 0201 keine Kontakte kennen. Program.cs:

C#
builder.Services.AddOpenApi("contacts");
builder.Services.AddOpenApi("appointments");

var app = builder.Build();

app.MapOpenApi();
app.MapContactsApi();
app.MapAppointmentsApi();

AddOpenApi("contacts") legt ein Dokument mit diesem Namen an, MapOpenApi() liefert jedes unter /openapi/<name>.json aus. Dass ein Endpunkt in seinem Dokument landet, steuert bei uns WithGroupName: Er landet im Dokument gleichen Namens. K2 2.3.10 führt AddOpenApi und MapOpenApi ein.