# 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