A context with a library group of its own in the front end but a single large file on the server is only half cut. This lesson carries the split through to the API: a group of endpoints per context, a contract per group, a client per contract. On top of that comes the question of a layer that assembles data from several contexts (backend for frontend, BFF). K2 teaches the .NET development of the API (2.3, 2.12); the idea of the contract from W9 0203 is assumed.

State of the checks. Run in a throwaway project outside the repository: an ASP.NET Core 10 project (SDK 10.0.400, Microsoft.AspNetCore.OpenApi 10.0.15) with two MapGroup groups and two documents, dotnet build -m:1 (exit 0, 0 warnings), starting the API and curl of the documents; ng-openapi-gen 1.1.0 for both documents in the Nx workspace from 0202 (exit 0), tsc --noEmit over both data-access libraries (first exit 2, after the fix exit 0), nx run-many -t lint over 13 projects (exit 0); a BFF route that calls the API through HttpClient against itself (output below). Not run: the rename company to firma from exercise 1, sign-in, tokens, scopes and any authorization, a BFF as a service of its own, Azure, a browser, an application against the API. Read in the text: the Learn pages "Backends for Frontends Pattern" and "Use Domain Analysis to Model Microservices" and the article on angulararchitects.io. The Learn page on OpenAPI in ASP.NET Core was not read: for AddOpenApi and WithGroupName, this states what ran for us (open: what the page promises).

One group per context

In 0201 and 0202, contacts, appointments and tenders are separate library groups. The server of the Kladde is one application (K4 4.3.6: a modular monolith), and there a context corresponds to one group of endpoints with a prefix and a name. The article on angulararchitects.io notes that front-end cuts often mirror those of the server; if the two cut differently, a BFF can translate, and the teams lose autonomy. A cut that is the same on both sides saves this translation. This is what the contacts group looks like (Contacts/ContactsApi.cs, shortened to the essentials; the sample data are fixed):

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;
}

The appointments group (Appointments/AppointmentsApi.cs) has the same structure with "/api/appointments" and the group name appointments. Each group lives in a folder of its own with types of its own: ContactSummary and ContactsPage in the one, AppointmentResponse in the other. No type is shared between the groups, just as the appointments in 0201 know no contacts. Program.cs:

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

var app = builder.Build();

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

AddOpenApi("contacts") creates a document with that name, MapOpenApi() serves each one under /openapi/<name>.json. That an endpoint ends up in its document is controlled in our case by WithGroupName: it ends up in the document of the same name. K2 2.3.10 introduces AddOpenApi and MapOpenApi.