Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR: Worker Actor-Service Decomposition

Status: Accepted Date: 2025-12 Ticket: TAS-69

Context

The tasker-worker crate had a monolithic command processor architecture:

  • WorkerProcessor: 1,575 lines of code
  • All command handling inline
  • Difficult to test individual behaviors
  • Inconsistent with orchestration actor architecture

Decision

Transform the worker from monolithic command processor to actor-based design, mirroring the orchestration actor pattern.

Before: Monolithic Design

WorkerCore
    └── WorkerProcessor (1,575 LOC)
            └── All command handling inline

After: Actor-Based Design

WorkerCore
    └── ActorCommandProcessor (~350 LOC)
            └── WorkerActorRegistry
                    ├── StepExecutorActor → StepExecutorService
                    ├── FFICompletionActor → FFICompletionService
                    ├── TemplateCacheActor → TaskTemplateManager
                    ├── DomainEventActor → DomainEventSystem
                    └── WorkerStatusActor → WorkerStatusService

Five Actors:

ActorResponsibilityMessages
StepExecutorActorStep execution coordination4
FFICompletionActorFFI completion handling2
TemplateCacheActorTemplate cache management2
DomainEventActorEvent dispatching1
WorkerStatusActorStatus and health4

Three Services:

ServiceLinesPurpose
StepExecutorService~400Step claiming, verification, FFI invocation
FFICompletionService~200Result delivery to orchestration
WorkerStatusService~200Stats tracking, health reporting

Consequences

Positive

  • 92% reduction in command processor complexity (1,575 LOC → 123 LOC main file)
  • Single responsibility: Each file handles one concern
  • Testability: Services testable in isolation, actors via message handlers
  • Consistency: Mirrors orchestration architecture
  • Extensibility: New actors/services follow established pattern

Negative

  • Two-phase initialization: Registry requires careful startup ordering
  • Actor shutdown ordering: Must coordinate graceful shutdown
  • Learning curve: New pattern to understand for contributors

Neutral

  • Public API unchanged (WorkerCore::new(), send_command(), stop())
  • Internal restructuring transparent to users

Gaps Identified and Fixed

GapIssueFix
Domain Event DispatchEvents not dispatched after step completionExplicit dispatch call in actor
Silent Error HandlingOrchestration send errors swallowedExplicit error propagation
Namespace SharingRegistry created new manager, losing namespacesShared pre-initialized manager

Alternatives Considered

Alternative 1: Service-Only Pattern

Extract services without actor layer.

Rejected: Loses message-based interfaces that enable testing and future distributed execution.

Alternative 2: Keep Monolithic with Better Organization

Refactor WorkerProcessor into methods without extraction.

Rejected: Doesn’t address testability or architectural consistency goals.

Alternative 3: Full Actor Framework (Actix)

Use production actor framework.

Rejected: Too heavyweight; we need lifecycle hooks and message-based testing, not distributed supervision.

References