Date: 2026-04-02
Accepted
The API exposes endpoints for retrieving, updating, and deleting individual players. Each of these operations requires a route parameter that identifies the target player. Two candidates exist: the internal UUID primary key (Id) and the domain-meaningful squad number (SquadNumber).
Exposing the UUID would couple API consumers to an internal database concern. UUIDs carry no domain meaning — a caller who knows that player "Lionel Messi" wears number 10 cannot construct the URL from that knowledge alone; they would first need to discover the UUID from a prior GET /players call.
SquadNumber is the natural identifier for a football player within a team context. It is the value coaches, fans, and data consumers use to refer to players. It is also the field that other API operations (POST uniqueness check, PUT route matching) already reason about.
We will use squadNumber as the route parameter for mutation endpoints and primary lookup routes: GET /players/squadNumber/{n}, PUT /players/squadNumber/{n}, and DELETE /players/squadNumber/{n}. The internal Id (UUID) is never included in any request model or response model. A secondary GET /players/{id:Guid} route exists for internal or administrative lookups by UUID but is not part of the primary API contract.
- URLs are human-readable and predictable from domain knowledge alone (
/players/squadNumber/10unambiguously refers to the number-10 player). - API consumers are fully decoupled from the internal persistence model.
- Consistency: the same identifier used in the domain (
SquadNumber) is used in the API surface, reducing the mental translation between domain concepts and HTTP calls.
- Squad numbers are unique only within a single team and only for the duration of a season. They can be reassigned when a player leaves or retires. This project models a single team's squad, so cross-team ambiguity is not a concern, but the limitation is real.
- Uniqueness must be enforced at the application layer. The
BeUniqueSquadNumberasync validator rule in theCreaterule set guards against duplicate squad numbers on insert; theUpdaterule set intentionally omits this check because the player already exists. squadNumberis not a stable long-term identifier — if squad number reassignment were ever supported, consumers holding a bookmarked URL would silently reach a different player.
- The
GET /players/{id:Guid}endpoint exists as a secondary route for internal or administrative lookups by UUID. It is intentionally excluded from the primary API contract and is not the recommended path for general consumers.