Backend API providing AI features, built as a Modular Monolith using Domain-Driven Design, Hexagonal Architecture (Ports & Adapters) and Clean Architecture.
Current focus is machine-to-machine (M2M) access: a client authenticates with an API token and requests the service (e.g. delivery of generated images). Human access (admin panel via Google SSO / email-password) is planned — its design is documented but not yet built.
Strict layer and module isolation for high cohesion (all code for a task lives in one Domain) and low coupling (modules talk only through Ports/Interfaces).
HTTP Request
│ (DTO via Request::getDto())
▼
[Controller] ──▶ [UseCase] ──┬──▶ [Domain Event] ──(async)──▶ [Listener]
(thin) (orchestration)│
│ (Port / Interface)
┌────────────────────────┤
▼ ▼
[Entity & Factory] [Adapter] ──▶ [Other Domain]
(rich Eloquent model) │
│ ▼
[Repository] ─────────────▶ [Database]
Entities are rich Eloquent models (behavior + invariants, no public setters) — not framework-free objects. See the guides below.
- System Prompt for code generation — self-contained architect prompt; feed it to an LLM when adding features so the code matches the conventions exactly.
- Modular Monolith & Hexagonal Architecture
- Domain-Driven Design
- Clean Architecture & Request Flow
- Enterprise Exception Handling
- Authentication & Authorization — current M2M token model and the target admin-SPA design (Sanctum cookies, Google SSO, etc.).
A complete worked example of every layer lives in examples/Domains/Reservation
(reference only — outside autoload/deploy).
app/
├── Core/ # Shared kernel: base Entity (UUID), marker exceptions, Http base, enums
└── Domains/
├── Common/ # Shared domain helpers (constants, exceptions)
├── Auth/ # Authentication: Sanctum tokens, login/logout, client:token command
│ ├── Console/Commands/ # IssueClientTokenCommand
│ ├── Contracts/ # Ports defined by Auth (e.g. UserProviderInterface)
│ └── ...
└── User/ # Users: entity/repository/factory, AuthUserProviderAdapter (Port impl)
examples/Domains/Reservation/ # Full reference domain (not autoloaded)
docker/ # Dockerfile, entrypoint, nginx, php-fpm/opcache config
docs/ # Architecture guides + LLM system prompt
Each domain follows: Http/{Controllers,Requests,Resources}, UseCases, Services, Repositories,
Entities, Factories, DTOs, Events/Listeners, Exceptions, Contracts/Adapters, Providers,
Enums, routes.php.
Fully dockerized — requires Docker / Docker Compose (OrbStack works too). No local PHP/Composer needed.
git clone <repo> laravel-modular-monolith && cd laravel-modular-monolith
docker compose up -d --buildOn first start the backend container installs dependencies, generates APP_KEY, and runs migrations
automatically. Follow with docker compose logs -f backend. The API is then at http://localhost
(health check: GET /up).
Port conflicts (running another project alongside): override host ports in .env —
APP_HTTP_PORT, FORWARD_DB_PORT, FORWARD_REDIS_PORT, FORWARD_MAILPIT_PORT,
FORWARD_MAILPIT_DASHBOARD_PORT (defaults are the standard ports).
Services: nginx (web), backend (php-fpm), worker (queue), scheduler, postgres, redis,
mailpit (mail UI at http://localhost:8025).
docker compose exec backend php artisan client:token client@example.com --create --abilities=image:sendUse it as Authorization: Bearer <token> against auth:sanctum-protected routes.
docker compose --profile test up -d postgres_test
docker compose exec backend php artisan testdocker compose exec backend ./vendor/bin/pint # format (Laravel preset + strict types)
docker compose exec backend ./vendor/bin/phpstan analyse # Larastan, level 6
docker compose exec backend ./vendor/bin/rector process # automated refactors- Laravel 13 / PHP 8.4
- PostgreSQL 18 (UUID primary keys), Redis 8 (cache / queue / session)
- Nginx 1.28 + PHP-FPM, Sanctum (API token auth)
- Docker (multi-stage: dev + prod). Deploy target: Google Compute Engine VM via
docker compose. - Tooling: Pint, Larastan, Rector
Read docs/laravel-modular-monolith-prompt.md first — it
defines the conventions. Core rules:
Dependencies point inward. Business logic lives in rich Entities. Controllers are thin (no try-catch). Cross-domain calls go through a Port + Adapter.
app/must pass Pint, Larastan (level 6) and Rector.