# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

Mangarr is a Symfony 7.0 manga management/reader application. It scrapes manga chapters from various sources, stores them as CBZ files, and provides a reader interface. It runs on FrankenPHP inside Docker.

## Common Commands

All commands run via Docker through the Makefile. Use `make help` to see all available targets.

```bash
make start          # Start Docker containers
make stop           # Stop containers
make install        # Build images, start containers, install deps (first-time setup)
make logs           # Follow container logs
make sh             # Shell into the PHP container
```

**PHP / Symfony:**
```bash
make sf c="about"                   # Run any Symfony console command
make cc                             # Clear cache
make vendor                         # Install Composer dependencies
make migration-migrate              # Run pending migrations
make fixtures-load                  # Load fixtures (drops and recreates data)
```

**Testing:**
```bash
make test                           # Run all tests
make test f="ScrapeChapterHandlerTest"  # Run a specific test by class name
make test c="--group e2e"           # Pass phpunit options
```

**Code Quality:**
```bash
make phpcs          # Fix code style (PSR-12 via php-cs-fixer)
make phpmd          # Run PHP Mess Detector
make quality        # Run both phpmd and phpcs
make phparkitect    # Check architectural rules
```

**Frontend:**
```bash
make npm-run        # Build assets once (dev)
make npm-watch      # Watch and rebuild assets
make npm-add p=pkg  # Add an npm dependency
```

**Messenger workers** (run in separate terminals):
```bash
make consume-commands   # Process command.bus messages
make consume-events     # Process domain events
make consume-schedule   # Process scheduled tasks
```

## Architecture

The project uses **Domain-Driven Design** with strict layer separation enforced by PHPArkitect (`phparkitect.php`).

### Domain Structure

```
src/Domain/
  {DomainName}/
    Domain/       # Pure domain: Models, Contracts (interfaces), Events, Exceptions
    Application/  # Use cases: Commands, Queries, CommandHandlers, QueryHandlers, Responses
    Infrastructure/ # Framework: Persistence, API Platform State, Clients, Services
  Shared/         # Cross-domain contracts and infrastructure (MangaPathManagerInterface, EventDispatcherInterface, etc.)
```

**Business domains:** `Manga`, `Reader`, `Scraping`, `Conversion`, `Setting` (+ `Shared`)

**Architectural rules enforced:**
- `Domain` layer has no outside dependencies (only std exceptions)
- `Application` layer may depend on its own Domain + `App\Domain\Shared\Domain\Contract` + `Symfony\Messenger` + `Ramsey\Uuid`; never on Infrastructure
- `Shared` depends on nothing outside itself

### Outside Domain (`src/`)

- `src/Entity/` — Doctrine ORM entities (legacy, used by repositories)
- `src/Controller/` — Symfony HTTP controllers
- `src/ApiResource/` — API Platform resource definitions + `OpenApiFactoryDecorator`
- `src/Service/` — Legacy services (being migrated into Domain)
- `src/Message/` + `src/MessageHandler/` — Legacy Messenger messages (outside DDD)

### Frontend

- `assets/controllers/` — Stimulus controllers (one per UI interaction)
- `assets/vue/app/` — Vue.js SPA mounted at `/vue/*`
- Tailwind CSS via PostCSS, bundled with Webpack Encore
- Mercure for real-time updates (queue status, download progress)

### Key Infrastructure

- **Database:** PostgreSQL 16 via Doctrine ORM; Adminer on port 8080
- **Scraping:** `scrapers.json` defines per-site CSS selectors; `HtmlScraper` and `JavascriptScraper` (Panther) strategies
- **File storage:** CBZ files stored at `MANGA_DATA_PATH` (default `~/Mangas`); images at `IMAGE_DATA_PATH`
- **External API:** MangaDex client for metadata (`MANGADEX_CLIENT_ID/SECRET/USERNAME/PASSWORD` env vars)
- **Messenger buses:** `command.bus` (sync commands), `events` transport, `commands` transport, `async` transport (scheduler)

### Adding a New Domain Feature

1. Define contracts (interfaces) in `Domain/{Name}/Domain/Contract/`
2. Write Command/Query + Handler in `Domain/{Name}/Application/`
3. Implement interfaces in `Domain/{Name}/Infrastructure/`
4. Register infrastructure aliases in `config/services.yaml`
5. Run `make phparkitect` to validate layer boundaries
