- Use
Storagefacade for user-generated or external file operations
- Actions (
app/Actions/) - Business logic. Each action is a single use-case callable from any interface (Livewire, CLI, API). - Services (
app/Services/) - Domain operations. Stateless classes that handle specific technical concerns (git ops, diff parsing, markdown formatting, file ignoring). Injected into Actions. - DTOs (
app/DTOs/) - Immutable data containers. Carry data between layers. - Livewire SFCs (
resources/views/pages/,resources/views/livewire/) - Thin UI adapters as single-file components. Handle events, manage component state, delegate to Actions. No business logic. Pages usepages::namespace; non-page components use default namespace. - Models (
app/Models/) - Eloquent persistence. Minimal - no business logic.
- Create an Action in
app/Actions/ - If it needs new domain logic, add a Service or extend an existing one
- Wire the Action into whichever interface needs it (Livewire component, Artisan command, API controller)
- Add unit test in
tests/Unit/Actions/
- Actions that need DB state (e.g.
RestoreSessionAction,SaveSessionAction) read/write internally. Stateless actions receive data via parameters for reuse across interfaces. - Actions use constructor injection for service dependencies
- Actions may accept an optional
cacheKeyparam for opt-in caching (e.g.LoadFileDiffAction). UseDiffCacheKey::for()for diff cache keys.
composer test:lint # Pint composer test:types # PHPStan composer test # Pest
- Prefer Laravel collections over foreach/for loops
- No external resources (CDNs, Google Fonts) - all assets served locally (enforced by arch test)
LoadFileDiffActionuses self-healing cache: validates cached entries have expected keys (e.g.syntaxStyles) before returning. Stale entries are re-computed and overwritten automatically. Prefer adding key checks over bumpingDiffCacheKeyversion for format changes.
./rfa