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
- No compile-time safety: Typos like
'opanai'pass silently at runtime. - Scattered validation: Each usage site re-validated allowed values.
- Missing behavior: Constants carried no associated logic (labels, icons, defaults).
- 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:
Detected-- Provider detection result with confidence score.Provider Discovered-- Model metadata from API discovery.Model Suggested-- AI-generated configuration preset.Configuration Completion-- ImmutableResponse final readonly classfor LLM responses.
Consequences
Positive:
- ●● Invalid values caught at instantiation (
BackedEnum::from()throws). - ●● PHPStan level 10 compliance without
@phpstan-ignoresuppressions. - ● Self-documenting:
AdapterType::OpenAI->defaultEndpoint()vs string lookup. - ● IDE auto-completion and refactoring support.
- ◐
matchexpressions 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/ Adapter Type. php Classes/Domain/ Enum/ Model Capability. php Classes/Domain/ Enum/ Model Selection Mode. php Classes/Domain/ Enum/ Task Category. php Classes/Domain/ Enum/ Task Input Type. php Classes/Domain/ Enum/ Task Output Format. php
Modified:
Classes/-- UsesDomain/ Model/ Provider. php Adapterenum.Type Classes/-- UsesDomain/ Model/ Model. php Modelenum.Capability Classes/-- UsesDomain/ Model/ Task. php Task,Category Task,Input Type Task.Output Format Classes/-- Adapter type matching via enum.Provider/ Abstract Provider. php