ADR-015: Type-Safe Domain Models via PHP 8.1+ Enums & Value Objects 

Status

Accepted

Date

2025-12

Authors

Netresearch DTT GmbH

Context 

Domain constants were stringly-typed throughout the codebase. Adapter types were plain strings ('openai', 'anthropic'), capabilities were CSV strings in database columns, task categories and output formats were validated ad-hoc. This caused subtle bugs and PHPStan violations at higher analysis levels.

Problem statement 

  1. No compile-time safety: Typos like 'opanai' pass silently at runtime.
  2. Scattered validation: Each usage site re-validated allowed values.
  3. Missing behavior: Constants carried no associated logic (labels, icons, defaults).
  4. PHPStan violations: Stringly-typed comparisons defeated type narrowing.

Decision 

Use PHP 8.1+ backed enums for all domain constants. Each enum provides:

  • A string-backed value for database/API compatibility.
  • Static helpers: values(), isValid(), tryFromString().
  • Domain-specific methods: label(), getIconIdentifier(), getContentType().
Example: AdapterType enum with behavior
enum AdapterType: string
{
    case OpenAI = 'openai';
    case Anthropic = 'anthropic';
    case Gemini = 'gemini';
    case Ollama = 'ollama';
    // ...

    public function label(): string { /* ... */ }
    public function defaultEndpoint(): string { /* ... */ }
    public function requiresApiKey(): bool { /* ... */ }
    public static function toSelectArray(): array { /* ... */ }
}
Copied!

Enums implemented:

Enum Purpose Cases
AdapterType LLM provider protocol type 9 cases (OpenAI through Custom)
ModelCapability Model feature flags 8 cases (chat, vision, tools...)
TaskCategory Task organization 5 cases (content, log_analysis...)
TaskInputType Task input source 5 cases (manual, syslog, file...)
TaskOutputFormat Response rendering format 4 cases (markdown, json...)
ModelSelectionMode Model selection strategy 2 cases (fixed, criteria)

Immutable readonly DTOs for composite data transfer:

  • DetectedProvider -- Provider detection result with confidence score.
  • DiscoveredModel -- Model metadata from API discovery.
  • SuggestedConfiguration -- AI-generated configuration preset.
  • CompletionResponse -- Immutable final readonly class for LLM responses.

Consequences 

Positive:

  • ●● Invalid values caught at instantiation (BackedEnum::from() throws).
  • ●● PHPStan level 10 compliance without @phpstan-ignore suppressions.
  • ● Self-documenting: AdapterType::OpenAI->defaultEndpoint() vs string lookup.
  • ● IDE auto-completion and refactoring support.
  • match expressions enforce exhaustive handling of all cases.

Negative:

  • ◑ Requires PHP 8.1+ (already the minimum for TYPO3 v13).
  • ◑ Enum #[CoversNothing] needed for PHPUnit 12 coverage.

Net Score: +6.0 (Strong positive)

Files changed 

Added:

  • Classes/Domain/Model/AdapterType.php
  • Classes/Domain/Enum/ModelCapability.php
  • Classes/Domain/Enum/ModelSelectionMode.php
  • Classes/Domain/Enum/TaskCategory.php
  • Classes/Domain/Enum/TaskInputType.php
  • Classes/Domain/Enum/TaskOutputFormat.php

Modified:

  • Classes/Domain/Model/Provider.php -- Uses AdapterType enum.
  • Classes/Domain/Model/Model.php -- Uses ModelCapability enum.
  • Classes/Domain/Model/Task.php -- Uses TaskCategory, TaskInputType, TaskOutputFormat.
  • Classes/Provider/AbstractProvider.php -- Adapter type matching via enum.