Skip to content

HTTP Boundary

ASP.NET Core Minimal API

  • [Required] このreferenceはASP.NET Core Minimal API、HTTP DTO、認証filter、typed result、OpenAPIを扱う。domain modelと配置はarchitecture.md、Handlerはapplication.md、HTTP入力変換はvalidation.md、repositoryはpersistence.md、DIはruntime.mdを正とする。

Route Groups

  • [Required] Controller ではなく Minimal API を使う。
  • [Required] Area単位でMapGroupを作り、use case endpointをぶら下げる。
csharp
namespace Backend.Features.Customers;

public static class CustomerEndpoints
{
    public static RouteGroupBuilder MapCustomerApi(this IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/customers");

        group.MapSignupApi();

        return group;
    }
}
  • [Required] 複数 use case を持つ area は Features/{Area}/{Area}Endpoints.cs に area route group を置く。
  • [Required] route group は API/resource の主語に合わせる。/rooms/{roomId}:join のような action route でも、group は /rooms のままにする。
  • [Required] area endpoint は route group を作り、use case endpoint の Map{UseCase}Api() を呼び出すだけにする。
  • [Required] area endpoint に個別 route の MapPost / MapPut / MapDelete などや endpoint lambda を直接置かない。
  • [Contextual] Healthz のような単一目的 endpoint は Endpoints.cs / Endpoints を許可する。
  • [Required] Program.cs には mapping の 1 行だけを追加する。
  • [Required] Program.cs に個別 endpoint を直接書かない。
csharp
app.MapCustomerApi();

Use Case Endpoints

  • [Required] Use case endpoint は Features/{Area}/{UseCase}/{Area}{UseCase}Endpoint.cs に置く。

  • [Required] AreaとUse Caseの所有境界はarchitecture.mdを正とし、このreferenceではHTTP mappingだけを扱う。

  • [Required] Use case endpoint fileには、そのuse caseのroute mappingとendpoint lambdaを閉じ込める。

  • [Required] request / response / handlerと対応するendpoint fileは同じuse case directoryに置く。

csharp
namespace Backend.Features.Customers.Signup;

public static class CustomerSignupEndpoint
{
    public static RouteGroupBuilder MapSignupApi(this RouteGroupBuilder group)
    {
        group.MapPost("/", async Task<Results<
            Created<CustomerSignupResponse>,
            ProblemHttpResult>> (
            CustomerSignupRequest request,
            ApiProblemResults problems,
            CustomerSignupHandler handler,
            CancellationToken ct) =>
        {
            var email = Ensure.Valid(EmailAddress.Create(request.Email));
            var result = await handler.HandleAsync(email, ct);
            if (result.IsError)
            {
                return problems.FromErrors(result.Errors);
            }

            var value = result.Value;
            var body = new CustomerSignupResponse(
                value.CustomerDisplayId,
                value.ObjectId,
                value.Email.Value);
            return TypedResults.Created($"/customers/{body.CustomerDisplayId}", body);
        })
        .AddEndpointFilter<RequestValidationFilter<CustomerSignupRequest>>();

        return group;
    }
}
  • [Required] Endpoint lambdaは薄く保つ。

  • [Required] request を受ける。

  • [Required] validation filter を通す。

  • [Required] handler を呼び出す。

  • [Required] handler result を HTTP response に mapping する。

Endpoint lambda に書かないもの:

  • [Required] 業務ロジック。
  • [Required] DB 操作。
  • [Required] 複数集約の生成・更新処理。
  • [Required] validator と重複する入力検証。
  • [Required] handler / repository / service の組み立て。

Authentication Filter

  • [Required] Bearer token検証や認証済みuser解決のような横断的なHTTP認証はendpoint filterに寄せる。
  • [Required] 認証が必要なendpointは、endpoint lambda内でtoken検証やuser解決を直接行わず、filterが設定した認証contextから必要な値だけを読む。
csharp
group.MapPost("/", CreateCustomerAsync)
    .AddEndpointFilter<AuthenticatedUserFilter>()
    .AddEndpointFilter<RequestValidationFilter<CustomerCreateRequest>>();
  • [Required] endpoint lambda 内で VerifyIDTokenAsyncGetUserAsync など認証用 service method を直接呼ばない。
  • [Required] filter は認証失敗時にRFC 9457 Problem Detailsの401 responseを返してendpoint本体を実行しない。
  • [Required] endpoint lambda は AuthenticatedUserContext などの context から認証済み user id / user 情報を読む。
  • [Required] 認証 filter 通過後の AuthenticatedUserContext.RequireUID() / RequireUser().UID は認証 context の契約済み値として扱う。PlayerId などの Value Object へ変換するときは Ensure.Valid(...) を使い、失敗時は 400 にせず filter / context の契約違反として扱う。
  • [Required] endpoint filter で route parameter を暗黙変換したり EndpointFilterInvocationContext.Arguments を差し替えたりしない。filter は認証、validation、context setup、short-circuit response に留める。
  • [Contextual] handler に渡す引数が多い場合は endpoint で {Area}{UseCase}Command を named arguments で組み立て、handler.HandleAsync(command, ct) を呼ぶ。
  • [Required] display name 更新など use case 固有の外部 service 呼び出しは、認証検証ではないため endpoint または handler の責務として別に扱う。
  • [Contextual] request validation filter と併用する場合は、認証 filter を先に接続する。

request body、route parameter、認証contextからValue Objectへ変換する共通規則と失敗の扱いはvalidation.mdを正とする。

Typed Results

  • [Required] Response は TypedResultsMicrosoft.AspNetCore.Http.HttpResults.Results<T1, T2, ...> で型安全に返す。

  • [Required] endpoint の戻り値型には成功 response と想定 error response を列挙する。

  • [Required] Results<T1, T2, ...> は typed union 戻り値型として使う。

  • [Required] 成功responseはTypedResults.Ok(...)TypedResults.Created(...)などを使い、error responseはTypedResults.Problem(...)を使う。

  • [Required] Results.Ok(...)Results.Created(...)Results.Problem(...) など非 generic Results static helper は使わない。

  • [Required] IResult だけの戻り値に逃げない。

  • [Required] response DTO を省略するために IResult を使わない。

優先:

csharp
async Task<Results<
    Created<CustomerSignupResponse>,
    ProblemHttpResult>>

避ける:

csharp
async Task<IResult>

OpenAPI

  • [Contextual] .Produces<T>()はOpenAPI補足が必要な場合にのみ使い、typed resultの代替にしない。

  • [Recommended] ProblemHttpResultのstatus codeが実行時に決まるendpointでは、公開し得るstatusごとに.ProducesProblem(statusCode)を補足する。

  • [Required] Problem DetailsのOpenAPI media typeをapplication/problem+jsonとして公開し、独自error DTO schemaへ戻さない。

  • [Required] Swagger に公開される DTO 型名は API 利用者から見て意味が分かる名前にする。

API DTO

  • [Required] Request / Response DTO は HTTP 境界専用の record にする。

  • [Required] DTO は Features/{Area}/{UseCase}/ に置く。

  • [Required] DTOはdomain entityや永続化modelと兼用しない。

  • [Required] Response DTOはHandlerのapplication resultと兼用しない。Endpointで{Area}{UseCase}Resultから{Area}{UseCase}Responseへmappingする。

  • [Required] DTO は同じ JSON shape でも use case ごとに定義する。shape 共有だけを理由に Features/{Area}/{SharedUseCaseName}Features/{Area}/Dtos のような directory を作らない。

  • [Required] 複数 use case で同じ response field を返す場合も、Swagger schema と生成 client の意味を優先し、{Area}{UseCase}Response をそれぞれ持つ。

  • [Required] request DTO に domain method を持たせない。

  • [Required] response DTO に domain entity をそのまま露出しない。

  • [Required] API に出す値は primitive、string、Guid、UTC DateTime など OpenAPI と相性のよい型に変換する。

  • [Required] Value Object は response 境界で .Value などに展開する。

  • [Contextual] nullable は API として null が意味を持つ場合だけ使う。

  • [Contextual] Request DTO は 5 項目以上、または同型 property が 3 つ以上ある場合は property record にする。

  • [Contextual] 小さい Request DTO と少数項目の Response DTO は positional record を使ってよい。

  • [Recommended] Response DTO は項目が多い、nullable が多い、同型 property が多い場合は property record を推奨する。

  • [Required] Swaggerに出るRequest / Response DTO名にはareaとuse case名を含める。

csharp
public record CustomerSignupRequest
{
    public required string Email { get; init; }
    public required string LastName { get; init; }
    public required string FirstName { get; init; }
}

public record CustomerSignupResponse(
    string CustomerDisplayId,
    Guid ObjectId,
    string Email);

禁止:

csharp
public record Request(string Email);
public record Response(string CustomerDisplayId);
public record SignupRequest(string Email);
public record SignupResponse(string CustomerDisplayId);
  • [Required] API DTO の日時は UTC DateTime に統一し、JSON では 2026-05-18T12:34:56Z のような Z 付き ISO 8601 として扱う。API request で日時を受ける場合は DateTime.Kind == DateTimeKind.Utc を validation する。タイムゾーンなし入力を拒否する必要がある endpoint は JSON converter / model binder で補強する。