api-layer-boundarieslisted
Install: claude install-skill Sarmkadan/dotnet-senior-skills
# API Layer Boundaries
## Controller / endpoint: translation only
A controller method does exactly: bind + validate input shape, call one application-layer method, map result to an HTTP response. Budget: ~10 lines. Anything else has leaked.
Reject in controllers:
- `DbContext` or repository injection. The controller must not know persistence exists.
- Business conditionals (`if (order.Total > limit)`), loops over domain data, price/permission calculations.
- `SaveChanges`, transactions, `try/catch` that maps exceptions to status codes per-action - use exception-handling middleware / `IExceptionHandler` once, globally.
- Composing multiple service calls into a workflow. A workflow is an application-service method; the controller calls it by name.
```csharp
// WRONG: orchestration in controller
[HttpPost]
public async Task<IActionResult> Create(CreateOrderRequest req)
{
var customer = await _customers.GetAsync(req.CustomerId);
if (customer.IsBlocked) return BadRequest("blocked");
var order = new Order { /* ... */ };
_db.Orders.Add(order);
await _db.SaveChangesAsync();
await _email.SendAsync(customer.Email, "...");
return Ok(order); // entity leaked, lazy-load serialization bomb included
}
// RIGHT
[HttpPost]
public async Task<ActionResult<OrderDto>> Create(CreateOrderRequest req, CancellationToken ct)
{
var result = await _orderService.CreateAsync(req.ToCommand(), ct);
return result is null ? Conflict() : CreatedAtAction(nameof(Get), new