← ClaudeAtlas

labrodev-exceptionlisted

Use when creating or reviewing any custom Exception class in a Labrodev Laravel project — deciding which folder an exception belongs in (Domain, Shared, Infrastructure, Feature, or a delivery Layer/App), naming it, or wiring the `::make()` static-constructor throw contract.
labrodev/laravel-playbook · ★ 0 · DevOps & Infrastructure · score 73
Install: claude install-skill labrodev/laravel-playbook
# Exceptions: named unhappy paths, placed by origin Part of the Labrodev playbook. **The law for this component lives in the always-on `labrodev-exception` guideline** (musts, must-nots); the per-file checklist is `rules/exceptions.md`. This skill holds the craft: anatomy, canonical templates, and edge cases. An Exception is a **named, explicit unhappy path** — not a generic `throw new \Exception('...')`. Every thrown exception in a Labrodev project is its own `final` class, placed by where the failure *originates*, and constructed through a single static `make()` factory so the throw site reads as a sentence. Placement law (origin, not consumer — the Domain/Shared/Infrastructure/Feature/Layer table) → `labrodev-exception` guideline. ## Template: Domain exception ```php <?php declare(strict_types=1); namespace Core\Domain\Booking\Exceptions; use Core\Domain\Booking\Models\Booking; use RuntimeException; final class BookingOverlapException extends RuntimeException { private function __construct(string $message) { parent::__construct($message); } public static function make(Booking $booking): self { return new self( sprintf('Booking %s overlaps with an existing reservation.', $booking->uuid), ); } } ``` Throw site, inside a Domain Action or Service: ```php if (! BookingRule::canConfirm(booking: $booking)) { throw BookingOverlapException::make(booking: $booking); } ``` ## Template: Infrastructure excepti