Skip to content

Domain Exceptions

Domain Exceptions represent business rule violations and domain-specific error conditions.

Characteristics

  • Specific: One exception class per error type
  • Contextual: Include relevant data for debugging
  • Named constructors: Factory methods for clarity
  • Domain language: Names reflect business concepts

Creating Exceptions

Extend AbstractDomainException with named constructors:

final class BookNotAvailableException extends DomainException
{
    public static function forBook(IdInterface $bookId): self
    {
        return new self(
            sprintf('Book with ID %s is not available for borrowing', $bookId->getValue())
        );
    }
}

final class InvalidIsbnException extends DomainException
{
    public static function whenChecksumIsInvalid(): self
    {
        return new self('Invalid ISBN-13 checksum');
    }
}

Naming Conventions

Pattern Example Use Case
Invalid{VO}Exception InvalidTitleException Value Object validation
{Entity}Not{State}Exception BookNotAvailableException Invalid state

Throwing Exceptions

// In Value Objects
protected function validateRules(string $value): void
{
    Assert::that($value)->notEmpty('Title cannot be empty');
}

// In Entities
public function borrow(): void
{
    if ($this->status !== BookStatus::Available) {
        throw BookNotAvailableException::forBook($this->id);
    }
}

// In Repositories
public function getById(IdInterface $id): Loan
{
    return $this->findById($id) ?? throw new InvalidArgumentException('Loan not found');
}

HTTP Status Mapping

Exception Type HTTP Status
NotFoundException 404
Invalid*Exception 400
NotAllowedException 403
AlreadyExistsException 409