Skip to content

ADR 0014: Introduce Processor Factory Functions in Domain Layer

Status

Accepted

Date

2025-12-08

Context

Application layer needs to create Processors from Profiles. The creation logic involves: - Retrieving Profile from repository - Handling ProfileNotFoundError - Creating Processor from Profile via ProcessorFactory - Wrapping Processor with fallback logic - Handling ProcessorCreationError

Without domain-level factory functions, this complex creation logic would be duplicated across multiple places (CLI, tests, future API layer).

Decision

Introduce two factory functions in domain layer:

# domain/processor.py
def create_processor_with_fallback(
    profile: Profile, processor_factory: ProcessorFactory
) -> Processor:
    """Create processor with automatic fallback to PassThroughProcessor."""

def create_processor_from_profile_name(
    profile_name: str,
    profile_repository: ProfileRepository,
    processor_factory: ProcessorFactory,
) -> Processor:
    """Create processor from profile name with automatic fallback."""

Rationale

  • Single Responsibility: Each function handles one creation scenario
  • Reusability: Application, CLI, and tests can use same creation logic
  • Domain logic in domain layer: Fallback decision is business logic, not application orchestration
  • Dependency injection: Functions accept repository and factory as parameters, enabling testability

Implications

Positive Implications

  • Application layer simplified to single function call
  • Consistent fallback behavior across all entry points
  • Easy to test creation logic in isolation
  • Clear extension point for future Processor creation variations

Concerns

  • Domain layer now depends on ProfileRepository abstraction (mitigation: repository is domain abstraction, this is acceptable)
  • Factory functions increase domain layer API surface (mitigation: only two focused functions, both essential)

Alternatives

Application Layer Handles Creation

Place creation logic in Application.create() method.

Pros: Simpler domain layer, orchestration in application layer

Cons: Cannot reuse creation logic in CLI or other contexts; tests must test through Application

Reason for rejection: Creation logic is business rule (when to fallback), belongs in domain

Builder Pattern

Use builder pattern for Processor creation.

Pros: Fluent API, can chain configuration

Cons: Overkill for two simple creation scenarios; more complex than needed

Reason for rejection: Factory functions provide sufficient flexibility with less ceremony

References