Add an endpoint
Serve a Feature over HTTP with an endpoint beside it that sends its command or query through the Module's Gateway and answers its Result.
What you build
One internal sealed class implementing IEndpoint beside its Feature. Its Map maps the Feature's route on its Module's route group. It injects the request's OperationContext and the Module's Gateway, sends the command or query, and answers with ToHttpResult(). It holds no business logic and touches no repository.
When to use it
For each Feature a client calls over HTTP. A Feature only other Modules use needs no endpoint: they reach it through the Module's Contracts.
Steps
Write the endpoint beside its Feature
Name it after the Feature with the Endpoint suffix. Inventory's GetStockItem endpoint sends its query through Inventory's Gateway with the context the request began:
// No :guid route constraint: an id that does not parse is the caller's mistake (400), not a missing route (404).
internal sealed class GetStockItemEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder routes) =>
routes.MapGet(
"/stock-items/{productId}",
async (
Guid productId,
OperationContext context,
IGatewayProcessor<InventoryModule> inventory,
CancellationToken cancellationToken
) =>
(
await inventory.QueryAsync(
new GetStockItemQuery(productId),
context,
cancellationToken
)
).ToHttpResult()
);
}For a body, declare an internal sealed record beside the endpoint and map it to the command by hand.
Choose the success shape
ToHttpResult() answers a success with or without the value, as the command or query has one; its overload that takes a mapping answers another shape, such as a created resource with its location. Every failure answers one problem response, whichever overload you use. From Result to response gives each status.
Declare the Module's endpoints
The Module's Composition Root calls AddPouryaKitEndpoints with the Module's route prefix, as Inventory's does, and finds every endpoint in the Module's assembly. The Host maps every Module's endpoints with MapPouryaKitModuleEndpoints().
Call it as an authenticated caller
The Web Capability begins the request's Operation with a context built from the authenticated caller: the Actor and, where the Host configures one, the tenant, each from the claim the Host names for it; the Kit's JWT bearer scheme writes both from a verified token. A test calls it with Fixture.Host.GetTestClient(): see Testing over HTTP.
The monolith template has no Gateway: its endpoints send through IDispatcher instead. See What each composition composes.
What the Kit guarantees
| Guarantee | Stated in |
|---|---|
| How a Result becomes a response | From Result to response |
| One problem response shape for every failure | Problem responses |
| Authenticated access by default, and a public endpoint's Actor | The caller and the Actor |
| An endpoint that would never answer stops the Host | The Host |
Failure messages
Next steps
Validate a command or query
Check a command's or query's input in a FluentValidation validator beside it, and get every failure back as Errors before the handler runs.
Make a command idempotent
Let a client repeat a request it never got an answer to, and replay the first success instead of running the command again.