# Pattern: Clean Architecture in .NET (the way we use it)

How the .NET solution is organized and the rules each layer follows.

## Dependency direction

```
Api  →  Application  →  Domain  ←  Infrastructure
                              ▲
                              │
                            Shared
```

- `Domain` has **zero** external references (no EF, no MediatR, no NuGet beyond `Shared`).
- `Application` depends on `Domain` + `Shared` only.
- `Infrastructure` implements `Application` abstractions; depends on everything below it.
- `Api` composes the host; no business logic.

## Where things go

| Concern                                                  | Layer                             |
| -------------------------------------------------------- | --------------------------------- |
| Entities, value objects, domain events                   | `Domain`                          |
| Use cases (commands/queries + handlers)                  | `Application` (MediatR)           |
| Validators (FluentValidation)                            | `Application`                     |
| Cross-cutting pipeline behaviors                         | `Application/Common/Behaviors`    |
| Abstractions (`IApplicationDbContext`, `IJwtService`, …) | `Application/Common/Abstractions` |
| EF Core `DbContext` + configurations                     | `Infrastructure/Persistence`      |
| External integrations (JWT, S3, email)                   | `Infrastructure/<Concern>`        |
| Controllers, middleware, host wiring                     | `Api`                             |
| `ApiResponse<T>`, `Result`, `Error`, constants           | `Shared`                          |

## CQRS shape

```csharp
public sealed record CreateDocumentCommand(string Title, ...) : IRequest<CreateDocumentResponse>;

public sealed class CreateDocumentValidator : AbstractValidator<CreateDocumentCommand>
{
    public CreateDocumentValidator() { RuleFor(x => x.Title).NotEmpty().MaximumLength(256); }
}

public sealed class CreateDocumentHandler(IApplicationDbContext db, ...) : IRequestHandler<CreateDocumentCommand, CreateDocumentResponse>
{
    public async Task<CreateDocumentResponse> Handle(CreateDocumentCommand request, CancellationToken cancellationToken) { ... }
}
```

The controller is a thin shell that sends the command and wraps in `ApiResponse<T>`.

## EF reads

ALWAYS:

```csharp
return await db.Users
    .AsNoTracking()
    .Where(u => u.IsActive)
    .Select(u => new UserListItem(u.Id, u.Email, u.DisplayName))
    .ToArrayAsync(cancellationToken);
```

NEVER:

```csharp
var entity = await db.Users.FirstAsync(...);  // tracked
return _mapper.Map<UserListItem>(entity);     // hidden N+1 + extra work
```

## Audit

Mutations call `IAuditDispatcher.Emit(...)` AFTER the change succeeds (parallel of the Node `em-audit`). Until that .NET-side adapter exists, the audit happens at the worker via the BullMQ enqueue from the Node side; for now, log + TODO.

## Related

- `dotnet/README.md`
- `.claude/agents/backend-agent.md`
- `.claude/skills/feature-vertical-slice/SKILL.md`
