Appearance
Application
Handler
[Required]Handler は use case の実行単位にする。[Required]Handlerはvalidation済みのapplication inputを受け取り、業務判断と状態変更に集中する。HTTP入力からapplication inputへの変換と失敗の扱いはvalidation.mdに従う。[Recommended]public method は原則HandleAsyncにする。[Contextual]HandleAsyncの引数がarchitectureでValue Object化の条件を満たす場合は、Value Object / feature-local modelとして受け取る。例:PlayerId callerPlayerId,RoomId roomId。[Required]HandleAsyncに同じ primitive 型の domain 値を複数並べない。例:string callerPlayerID, string roomIDは避ける。[Contextual]HandleAsyncの domain 引数が 5 個以上、または設定値や投票値のように意味の近い値が複数並ぶ場合は{Area}{UseCase}Commandrecord にまとめる。例:RoomCreateCommand、RoomUpdateSettingsCommand。[Required]command record は use case directory に置き、request DTO ではなく validation 済みの application input として扱う。Swagger に出す API contract と兼用しない。[Required]command recordはHTTP request DTOと兼用せず、Value Object化の条件を満たすpropertyにはarchitectureで定義した型を使う。追加制約のないbool ReverseModeなどはprimitiveのまま扱う。[Required]command record を作る endpoint / test では named arguments を使い、positional argument の順序依存を避ける。[Required]logging は障害解析に必要な箇所に限定し、個人情報を出さない。[Required]Handler に HTTP / provider SDK の詳細を漏らさない。
Handler Results
[Required]Handler の public method はuse case固有のapplication resultまたは業務エラーをErrorOr<T>で返す。application resultはHTTP Response DTOと兼用しない。[Required]application resultは{Area}{UseCase}Resultとし、handlerと同じuse case directoryに置く。HTTP status、header、URL、provider SDK型を持たせない。
csharp
public sealed class RoomCreateHandler(
IRoomCreateRepository repository,
IRoomIDGenerator ids,
TimeProvider clock)
{
public async Task<ErrorOr<RoomCreateResult>> HandleAsync(
PlayerId hostPlayerId,
PlayerName hostName,
DiscussionMinutes discussionMinutes,
VoteSeconds voteSeconds,
CancellationToken ct)
{
var now = clock.GetUtcNow().UtcDateTime;
var activeRoom = await repository.FindActiveHostRoomAsync(hostPlayerId, now, ct);
if (activeRoom is not null)
{
return RoomErrors.ActiveHostRoomExists();
}
var room = Ensure.Valid(Room.Create(
hostPlayerId,
hostName,
discussionMinutes,
voteSeconds,
ids.NewRoomID(),
ids.NewRoomCode(),
now));
return new RoomCreateResult(room.RoomID, room.RoomCode);
}
}[Required]Task<ErrorOr<TResult>>をhandlerの標準戻り値にする。Endpointは成功resultをHTTP Response DTOへ明示的にmappingする。[Required]業務上予期できる失敗はreturn {Area}Errors.SomeError(...)にする。[Required]throwを業務分岐、権限不足、not found、競合、状態不正の通常表現に使わない。[Required]handler は FluentValidation を呼ばない。[Required]handler は Entity / feature-local model の method を呼び、状態遷移 rule を Entity 外へ漏らさない。[Contextual]handler 内で Entity / feature-local model の factory が不変条件を再確認する場合は、失敗時に実装バグとして分かる helper を使う。例:Ensure.Valid(...)。
Error Definitions
[Required]業務エラーは area が所有する{Area}Errorsに集約する。[Required]ErrorOr のErrorには API response へ変換できる code、description、metadata を持たせる。
csharp
public static class RoomErrors
{
public static Error ActiveHostRoomExists()
=> Error.Conflict(
code: "room.active_host_room_exists",
description: "The authenticated player already has an active host room.");
public static Error NotFound()
=> Error.NotFound(
code: "room.not_found",
description: "The requested room was not found.");
}[Required]error code は area を含む安定した文字列にする。[Required]API 利用者へ返す message は{Area}Errorsか response mapping に置き、handler に散らさない。[Required]HTTP status は endpoint / problem result mapping で決める。[Required]同じ業務エラーをExceptionとErrorの両方で定義しない。[Required]通常の業務分岐は1件のerrorを返す。HTTP mapperはErrorOrが複数errorを保持していても先頭の1件だけをProblem Detailsへ変換する。[Contextual]application内部の一括処理で複数errorを保持してよいが、そのcollectionをAPI responseへそのまま公開しない。