134 lines
6 KiB
Markdown
134 lines
6 KiB
Markdown
# Backend context
|
|
|
|
Read `ai/shared.md` first. This file covers backend-specific rules.
|
|
|
|
## Project context
|
|
|
|
**Stack:** PHP 8.4, Laravel 13, PHPUnit, Larastan, Composer.
|
|
|
|
**Location:** `backend/`.
|
|
|
|
The backend follows a domain-oriented structure. Existing areas use entities,
|
|
DTOs, repository interfaces, Eloquent implementations, use cases, and test
|
|
fakes. Extend those patterns when adding behavior to an established area. Do
|
|
not add speculative layers or interfaces that the requested behavior does not
|
|
need.
|
|
|
|
The Vue application remains a separate project under `frontend/website/`.
|
|
Keep frontend source, dependencies, builds, and delivery out of the backend
|
|
unless the user explicitly asks to integrate them. The backend root route is
|
|
intentionally unclaimed; the built-in health endpoint is `/up`.
|
|
|
|
## Code patterns
|
|
|
|
- Inspect similar domain code before adding a new entity, DTO, repository,
|
|
use case, controller action, or fake.
|
|
- Entities own domain state and behavior and expose descriptive methods.
|
|
- DTOs are explicit data containers for creation or transfer between seams.
|
|
- Repository interfaces define domain-facing persistence operations. Keep
|
|
Eloquent details in their implementations and register bindings in the
|
|
application provider.
|
|
- Put reusable business behavior in a use case. Give a use case a request DTO
|
|
when it accepts a payload; call `execute()` directly when it has no input.
|
|
- Document use-case exceptions with `@throws` when callers are expected to
|
|
handle them.
|
|
- Keep controllers thin. Translate HTTP input to use-case input and domain
|
|
output to the response contract.
|
|
- Catch only documented, expected exceptions at the controller boundary. Let
|
|
unexpected exceptions reach Laravel's exception handler.
|
|
- Fake repositories are in-memory implementations used by unit tests. Return
|
|
a new entity instance from create and lookup methods rather than exposing a
|
|
stored reference.
|
|
- Use Eloquent relationships and query scopes consistently rather than
|
|
duplicating query fragments.
|
|
|
|
## Unit tests
|
|
|
|
- Follow the existing organization under `tests/Unit/<Area>/`.
|
|
- Use fake repositories and fake collaborators for the behavior under test.
|
|
Construct unrelated dependency entities directly instead of routing them
|
|
through additional repositories.
|
|
- Test use-case branches at the use-case seam. Do not repeat every branch in
|
|
controller or HTTP tests.
|
|
- Keep deletion behavior tests in the relevant deletion use-case suite. Do
|
|
not test deletion policy through entity, repository, or database-constraint
|
|
tests.
|
|
- Plain entities, value objects, use cases, middleware, and controller units
|
|
should extend `PHPUnit\Framework\TestCase` when they do not need Laravel.
|
|
- Extend `Tests\TestCase` only when a test needs Laravel's container, facades,
|
|
database, routing, or HTTP kernel.
|
|
- Reserve direct Eloquent repository feature tests for persistence mapping,
|
|
query behavior, or database constraints that cannot be proven through a
|
|
plain unit test.
|
|
|
|
## Feature tests
|
|
|
|
- Feature tests exercise the HTTP seam: routing, middleware, real Eloquent
|
|
bindings, cookies, persistence, validation, and response shape.
|
|
- Feature tests are additive to unit tests. Prefer a happy path and the
|
|
relevant authorization guard instead of duplicating all business branches.
|
|
- Place endpoint tests under `tests/Feature/<Area>/`, extend
|
|
`Tests\TestCase`, and use `RefreshDatabase` when database state is involved.
|
|
- Development and runtime use PostgreSQL through the local Unix socket.
|
|
PHPUnit uses SQLite `:memory:` as configured in `phpunit.xml`, so feature
|
|
tests are self-contained and do not need the worktree stack.
|
|
- Never point `RefreshDatabase` tests at the development PostgreSQL database.
|
|
- Keep mail on PHPUnit's `array` transport unless a test explicitly exercises
|
|
a real mail integration.
|
|
- Authentication uses a custom database-session cookie, not a Laravel guard.
|
|
`actingAs()` does not apply. Credentialed requests use
|
|
`withCredentials()` and `withUnencryptedCookie()` because API middleware
|
|
reads the raw cookie.
|
|
- Read an unencrypted response cookie with
|
|
`$response->getCookie($name, false)`.
|
|
|
|
## PHP rules
|
|
|
|
- Put imports at the top of the file. Do not use inline fully qualified class
|
|
names when a normal `use` statement is clearer.
|
|
- Do not use arrow functions. Use regular anonymous functions.
|
|
- Do not use first-class callable syntax. Use regular anonymous functions for
|
|
callbacks so the invocation is explicit.
|
|
- Do not add default values to function or constructor parameters. Pass every
|
|
argument explicitly, including nullable arguments.
|
|
- Use descriptive names for classes, methods, parameters, and local
|
|
variables.
|
|
- Document exceptions with `@throws` when a caller is expected to handle
|
|
them.
|
|
|
|
## Seeders
|
|
|
|
- Keep `DatabaseSeeder` as the orchestrator.
|
|
- Split substantial seed data into one seeder per domain concept and invoke
|
|
them in dependency order.
|
|
- Do not add production repository or model APIs solely to make seeding
|
|
convenient.
|
|
- Use existing lookup methods for cross-seeder relationships. When a group of
|
|
records only makes sense together, keep it in one seeder and retain local
|
|
references.
|
|
|
|
## Migrations
|
|
|
|
- Attainly is not in production yet.
|
|
- While no production database exists, edit the original `create_*` migration
|
|
when changing a table instead of accumulating follow-up alter migrations.
|
|
- Keep one migration file per table during this pre-production phase.
|
|
- Rebuild the development database with:
|
|
|
|
```sh
|
|
direnv exec "$(git rev-parse --show-toplevel)" \
|
|
php artisan migrate:fresh --seed
|
|
```
|
|
|
|
- Once a production database exists, replace this policy with additive,
|
|
forward-only migrations.
|
|
|
|
## Backend workflow
|
|
|
|
- Run the focused PHPUnit test while developing.
|
|
- Use `just backend-types-check` for Larastan and `just backend-test` for the
|
|
full PHPUnit suite during iteration.
|
|
- Fix failures caused by the change. Report unrelated baseline failures
|
|
precisely rather than expanding scope silently.
|
|
- The shared `just test-all` command is the required completion gate. Focused
|
|
backend recipes never replace it.
|