# AGENTS.md — Balinyaar Server Agent-oriented guide to the backend. For human setup/run instructions see [README.md](README.md). ## Stack - **ASP.NET Core / .NET 10** (`net10.0`), Web API - **Clean Architecture** (Domain → Application → Infrastructure → API) - **CQRS** with **MediatR** (source-generated `Mediator`) - **EF Core 10** + **SQL Server** (Repository + Unit of Work) - **ASP.NET Core Identity** with **JWE** (signed + AES-encrypted JWT), **OTP**, and **dynamic permission** authorization - **Mapster** (mapping), **FluentValidation** (validation), **Serilog** (logging), **OpenTelemetry** + **prometheus-net** (observability), **NSwag/Swagger** (OpenAPI), **Asp.Versioning** (API versioning) - **xUnit** + **NSubstitute** (tests) - Centralized NuGet versions in `Directory.Packages.props` ## Commands (run from `server/`) | Task | Command | | ---------- | --------------------------------------------------------------------------------- | | Restore | `dotnet restore CleanArcTemplate.sln` | | Build | `dotnet build CleanArcTemplate.sln` | | Run API | `dotnet run --project src/API/CleanArc.Web.Api/CleanArc.Web.Api.csproj` | | Test | `dotnet test CleanArcTemplate.sln` | | Add migration | `dotnet ef migrations add --project src/Infrastructure/CleanArc.Infrastructure.Persistence --startup-project src/API/CleanArc.Web.Api` | **Startup project:** `src/API/CleanArc.Web.Api`. Default URL `https://localhost:5002`, Swagger at `/swagger`. On boot the app applies EF migrations and seeds default users (`Program.cs` → `ApplyMigrationsAsync()` / `SeedDefaultUsersAsync()`), so a reachable DB is required. ## Projects by layer ``` src/ ├── Core/ │ ├── CleanArc.Domain Entities (User, Order, Role...), BaseEntity, IEntity, ITimeModification │ └── CleanArc.Application CQRS Features/, Contracts/ (interfaces), Models/, MediatR pipeline (Common/) ├── Infrastructure/ │ ├── CleanArc.Infrastructure.Persistence ApplicationDbContext, Configuration/, Repositories/, Migrations/ │ ├── CleanArc.Infrastructure.Identity Jwt/, Identity/ (Managers, Stores, PermissionManager, Seed), ServiceConfiguration/ │ ├── CleanArc.Infrastructure.CrossCutting Logging (Serilog) │ └── CleanArc.Infrastructure.Monitoring HealthCheck / OpenTelemetry / Prometheus configs ├── API/ │ ├── CleanArc.Web.Api Program.cs, Controllers/V1/, appsettings*.json │ ├── CleanArc.WebFramework BaseController, Filters/, Middlewares/, Swagger/, Attributes/ │ └── Plugins/CleanArc.Web.Plugins.Grpc GrpcPluginStartup, Services/, ProtoModels/ ├── Shared/CleanArc.SharedKernel Extensions + validation base used by all layers └── Tests/ CleanArc.Tests.Setup + CleanArc.Test.Infrastructure.Identity ``` Dependency direction points **inward**: Domain depends on nothing; Application depends on Domain; Infrastructure and API implement/consume Application's contracts. Never make Domain or Application reference Infrastructure or the API. ## Startup wiring — `src/API/CleanArc.Web.Api/Program.cs` Service registration is composed from per-layer extension methods (in each project's `ServiceConfiguration`): ``` ConfigureHealthChecks() · SetupOpenTelemetry() AddApplicationServices() // MediatR + validators + pipeline behaviors RegisterIdentityServices(...) // Identity, JWT/JWE, authorization policies AddPersistenceServices(...) // DbContext, UnitOfWork, repositories AddWebFrameworkServices() // API versioning AddSwagger("v1","v1.1") · RegisterValidatorsAsServices() · AddMapster() ConfigureGrpcPluginServices() // gRPC plugin ``` Pipeline order: exception handling → Swagger → routing → **authentication → authorization** → controllers → metrics → health checks → `ConfigureGrpcPipeline()`. When adding infrastructure, expose it as an extension method and call it here rather than inlining into `Program.cs`. ## CQRS — how a feature is shaped Features live under `CleanArc.Application/Features//{Commands|Queries}//`. A query example (`Features/Order/Queries/GetAllOrders/`): - `GetAllOrdersQuery.cs` — `record ... : IRequest>` - `GetAllOrdersQueryHandler.cs` — `internal` handler; depends on `IUnitOfWork`, `IMapper`; returns `OperationResult` - `GetAllOrdersQueryResult.cs` — the DTO returned Commands additionally implement `IValidatableModel` and declare FluentValidation rules; the `ValidateCommandBehavior` MediatR pipeline (`Application/Common/`) runs validators before the handler and surfaces errors in `OperationResult`. **To add a feature:** create the folder with the request + handler (+ result/validator), then call it from a controller via `_sender.Send(...)`. Contracts the handler needs go in `Application/Contracts/` and are implemented in Infrastructure. ## Controllers & results - Controllers live in `CleanArc.Web.Api/Controllers/V1/` and inherit `BaseController` (`CleanArc.WebFramework/BaseController/BaseController.cs`), which exposes `UserId`/`UserName`/etc. from claims and maps `OperationResult` → `IActionResult`. - All responses are wrapped in `OperationResult` (`Application/Models/Common/`): `Result`, `IsSuccess`, `ErrorMessages`, `IsNotFound`, `IsException`. Use the factory methods (`SuccessResult`, `FailureResult`, `NotFoundResult`). - Protected endpoints use `[Authorize(ConstantPolicies.DynamicPermission)]`. ## Persistence - `ApplicationDbContext` (`CleanArc.Infrastructure.Persistence/ApplicationDbContext.cs`) extends `IdentityDbContext<...>`; it auto-registers `IEntity` types and applies all `IEntityTypeConfiguration` from the assembly. - Per-entity config in `Configuration/Config/`. Repositories in `Repositories/` derive from `BaseAsyncRepository`; expose them through `IUnitOfWork` (interface in `Application/Contracts/Persistence/`). Commit via `unitOfWork.CommitAsync()`. - Migrations in `Migrations/`. Add new ones with the `dotnet ef` command above. ## Identity & auth - Token service: `CleanArc.Infrastructure.Identity/Jwt/JwtService.cs` (`IJwtService`) — issues JWE (HMAC-SHA256 signed, AES-128 encrypted), refresh tokens, and OTP/phone-based tokens. - Custom Identity managers/stores under `Identity/Manager/` and `Identity/Store/`. - Dynamic permissions: `Identity/PermissionManager/` (`DynamicPermissionService`, `DynamicPermissionHandler`, `ConstantPolicies`). - Settings from `appsettings.json` → `IdentitySettings` (`SecretKey`, `Encryptkey` = 16 chars, `Issuer`, `Audience`, lifetimes). ## gRPC plugin `Plugins/CleanArc.Web.Plugins.Grpc` is a self-contained module mounted via Application Parts. `GrpcPluginStartup.cs` provides `ConfigureGrpcPluginServices()` / `ConfigureGrpcPipeline()` (called from `Program.cs`). Proto contracts in `ProtoModels/*.proto`, services in `Services/`. The host uses HTTP/2 (`Kestrel` config) for gRPC. ## Conventions - Add cross-layer wiring as `ServiceConfiguration` extension methods, not inline in `Program.cs`. - Keep handlers `internal`; return `OperationResult`; don't throw for expected failures (use `FailureResult`/`NotFoundResult`). - Use Mapster for entity↔DTO mapping; FluentValidation for input validation. - Centralize package versions in `Directory.Packages.props` (no inline `Version=` in `.csproj`). - The `CleanArc*` namespace/`.sln` naming is internal project naming, **not** template branding — don't rename it without an explicit request (it touches every file and the EF migrations).