# AGENTS.md — Założenia i standardy backendu (C#) Dotyczy projektu `backend` oraz testów `tests/TarninaHub.Tests` (ASP.NET Core Web API, .NET 10). ## Komunikaty commitów - Komunikaty commitów zapisujemy **wyłącznie w języku angielskim**. - Zasada obowiązuje **w obu projektach**: repozytorium `DevHeyah` oraz `20_projects/TarninaHub`. - Stosujemy konwencję Conventional Commits (typ: `feat`, `fix`, `docs`, `chore` itd.). ## Założenia języka C# - **Platforma:** .NET 10 (`net10.0`), `Nullable enable`, `ImplicitUsings enable`. Nie wyłączaj nullowalności; unikaj operatora `!` (jeśli musisz — uzasadnij). - **Jeden typ = jeden plik:** każdy typ najwyższego poziomu (klasa, `record`, interfejs, `enum`, `struct`) w osobnym pliku, nazwa pliku zgodna z nazwą typu. Nie zagnieżdżaj typów pomocniczych poza przypadkami prywatnych szczegółów implementacji. - **Przestrzeń nazw:** wyłącznie file-scoped (`namespace X;`). Kolejność: `using` → `namespace` → typy. Grupuj `using` bez zbędnych duplikatów. - **Typy:** klasy usług i kontrolerów oznaczaj `sealed`; modele wierszy i typy wewnętrzne jako `internal`. DTO to `public sealed record` (niemutowalne, pozycyjne). - **Zakaz map z wartościami `object`:** w DTO, interfejsach, `Domain` i modelach `*Row` nie stosuj `Dictionary` ani `IReadOnlyDictionary`. Elastyczne dane (`content`, `payload`, `contact`, `media`, `attachments`) to mapy wartości tekstowych (`IReadOnlyDictionary`), materializowane do konkretnych `Dictionary` przed przekazaniem do `RawQuery`. Powód: błąd deserializacji CBOR (Dahomey) i nieobsługiwana serializacja typów interfejsowych. Egzekwuje test `NoObjectValuedDictionariesTests`. - **Wzorce:** promuj `record`, pattern matching, wyrażenia (`=>`), kolekcje `[]`; unikaj `null`-owych kolekcji (inicjalizuj puste). - **Warstwy:** `Domain` (reguły, enumy), `Application` (DTO, abstrakcje, mapowania), `Infrastructure` (persistence, integracje), `Controllers` (tylko HTTP). Zależności kieruj do wewnątrz (Infrastructure → Application → Domain). - **Nazewnictwo:** typy/metody/właściwości `PascalCase`, parametry i zmienne lokalne `camelCase`, pola prywatne `_camelCase`, interfejsy `I...`, typy generyczne `T...`. Metody asynchroniczne z sufiksem `Async`. - **Formatowanie:** zgodnie z `.editorconfig` (C#: 4 spacje, LF, final newline, bez trailing whitespace). Weryfikacja: `dotnet format whitespace --verify-no-changes TarninaHub.slnx` i `dotnet format style --verify-no-changes --severity warn TarninaHub.slnx`. - **Zmienne:** preferuj `var`, gdy typ jest oczywisty; jawne typy przy niejasności. ## Język nazewnictwa - Wszystkie nazwy zmiennych, parametrów, pól, metod, klas i plików są **wyłącznie w języku angielskim**. - Zakaz używania polskich nazw w kodzie (np. `uzytkownik`, `zamowienie`), w tym w parametrach tras i modelach. - Komentarze i dokumentacja opisowa mogą być w języku polskim, ale identyfikatory w kodzie — tylko po angielsku. ## Organizacja kontrolerów - Każdy kontroler w osobnym pliku w katalogu `Controllers/`, nazwa pliku zgodna z nazwą klasy. - Nazwa klasy: `Controller` (np. `CustomersController`), dziedziczy po `ControllerBase`. - Stosuj atrybut `[ApiController]` oraz jawne trasy `[Route("api/")]` (np. `[Route("api/health")]`) — nie używaj tokenu `[controller]`. - Katalog `Controllers/` grupuje wyłącznie kontrolery — logika biznesowa trafia do serwisów. ## Konwencje akcji - Metody akcji w czasie teraźniejszym, opisujące operację: `GetById`, `GetAll`, `Create`, `Update`, `Delete`. - Używaj atrybutów HTTP: `[HttpGet]`, `[HttpGet("{id}")]`, `[HttpPost]`, `[HttpPut("{id}")]`, `[HttpDelete("{id}")]`. - Parametry tras w `{camelCase}` (np. `{customerId}`), zgodnie z nazwami w kodzie. - Zwracaj typy wyników `ActionResult` lub `IActionResult` z jawnymi kodami statusu: - `200 OK` — pobranie/zaktualizowanie, - `201 Created` — utworzenie (`CreatedAtAction`), - `204 NoContent` — usunięcie, - `400 BadRequest`, `404 NotFound` — błędy. - Kontroler nie zawiera logiki biznesowej ani dostępu do bazy — deleguje do serwisów/repozytoriów. - Używaj wstrzykiwania zależności przez konstruktor (tylko `readonly` pola). ## Modele i DTO - Wejście i wyjście kontrolera stanowią dedykowane DTO, nie modele encji. - Nazwy DTO: `Request`, `Response`. - Walidacja wejścia przez adnotacje (`[Required]`, `[StringLength]`, `[Range]`). - Nie zwracaj encji bazy bezpośrednio z akcji. ## Asynchroniczność - Wszystkie operacje I/O (baza, HTTP) jako `async` z sufiksem `Async` i zwrotem `Task<...>`. - Akceptuj `CancellationToken cancellationToken` i przekazuj go dalej. ## Przykład ```csharp [ApiController] [Route("api/customers")] public class CustomersController : ControllerBase { private readonly ICustomerService _customerService; public CustomersController(ICustomerService customerService) { _customerService = customerService; } [HttpGet("{customerId:int}")] public async Task> GetById( int customerId, CancellationToken cancellationToken) { var customer = await _customerService.GetByIdAsync(customerId, cancellationToken); return customer is null ? NotFound() : Ok(customer); } [HttpPost] public async Task> Create( CreateCustomerRequest request, CancellationToken cancellationToken) { var createdCustomer = await _customerService.CreateAsync(request, cancellationToken); return CreatedAtAction( nameof(GetById), new { customerId = createdCustomer.Id }, createdCustomer); } } ```