# Backend context

> Read `ai/shared.md` first. This file only covers backend-specific rules.

## Project Context

**Stack:** PHP 8.x, Slim 4, PHP-DI/Slim-Bridge, PHPUnit (dev), Composer.
Persistence: PostgreSQL.
**Architecture:** Domain-Driven Design. Code is organized by domain entity
under `backend/app/` (PSR-4 `App\\`) into Entities, DTOs, Repositories,
Use Cases, and Fakes (in-memory repos for tests). The entity list is
intentionally omitted here - update this section as entities land.

## Code patterns

- Look at similar entities for reference before writing anything new
- Entities: constructor with properties, getters
- DTOs: simple data containers for creation (e.g. `CreateSetDto`)
- Repositories: interfaces that define data access
  - Do not write unit tests for concrete repository implementations
    (e.g. `Postgres*Repository`). They are exercised by e2e tests.
    Use cases are tested with fake repositories.
- Use cases: business logic with Request objects
  - When throwing exceptions, add `@throws` docblock
- Fakes: in-memory implementations for testing
  - Live under `tests/Fakes/`
  - Find/lookup methods must return a new instance of the entity, not
    the stored reference
- Tests: follow `tests/Unit/[Entity]/UseCases/` layout
  - In `setUp`, only use fake repositories for entities under test -
    construct dependency objects directly with `new` (e.g.
    `new Set(...)`) instead of creating them through their fake
    repositories

## PHP rules

- Imports: always put `use` statements at the top of the file, never use
  inline imports (e.g. `\App\Foo\Bar::class`)
- Closures: never use arrow functions (`fn () =>`) - always use regular
  anonymous functions (`function () { return ...; }`)

## Pre-commit

Run `phpcs` on worked-on directories before committing (phpcs ruleset
to be added to the repo; the Nix flake already provides
`php-codesniffer`). Run `./vendor/bin/phpunit tests` and confirm green.

## LLM anti-patterns

Constructs LLMs default to that this project forbids. Name the trap
explicitly so you catch yourself before writing it.

| Anti-pattern | Forbidden | Required |
|---|---|---|
| Arrow function | `fn ($x) => $x->getId()` | `function ($x) { return $x->getId(); }` |
| Inline FQCN type | `function f(): \Psr\Http\Message\ResponseInterface` | `use Psr\Http\Message\ResponseInterface;` then `function f(): ResponseInterface` |
| Inline `::class` | `Container::get(\App\Foo\Bar::class)` | `use App\Foo\Bar;` then `Container::get(Bar::class)` |
| Default param | `function f(int $count = 10)` | `function f(int $count)` |
| Default in fake | `public function create(Dto $dto, bool $strict = true)` | no default; every caller passes a value |
| Lookup returns stored ref | `return $this->items[$id] ?? null;` | rebuild a new instance with the stored fields |
| Short variable name | `$t`, `$n`, `$res`, `$req`, `$e` | `$text`, `$node`, `$response`, `$request`, `$exception` |
| Em dash | `// fetches user — by email` | `// fetches user - by email` |

When generating code, scan the diff for these patterns before writing it
to disk. If you catch one mid-write, rewrite that line.
