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 BookNotFoundException extends DomainException
{
    public static function forId(IdInterface $id): self
    {
        return new self(
            sprintf('Book with ID %s not found', $id->getValue())
        );
    }
}

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

Naming Conventions

Pattern Example Use Case
Invalid{VO}Exception InvalidTitleException Value Object validation
{Entity}NotFoundException BookNotFoundException Entity not found
{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): Book
{
    return $this->findById($id) ?? throw BookNotFoundException::forId($id);
}

HTTP Status Mapping

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