Skip to content

refactor: Reduce ExecutionOrchestrator constructor dependencies (15 → 8)#1448

Merged
thomhurst merged 6 commits into
mainfrom
refactor/reduce-execution-orchestrator-dependencies
Dec 29, 2025
Merged

refactor: Reduce ExecutionOrchestrator constructor dependencies (15 → 8)#1448
thomhurst merged 6 commits into
mainfrom
refactor/reduce-execution-orchestrator-dependencies

Conversation

@thomhurst

Copy link
Copy Markdown
Owner

Summary

  • Extract 3 single-responsibility components from ExecutionOrchestrator (15→8 dependencies):
    • IPipelineOutputCoordinator: Coordinates all output operations (printing, logging, exception flushing) - wraps 5 dependencies
    • IIgnoredModuleResultRegistrar: Handles registration of skipped results for ignored modules - wraps 4 dependencies
    • IPipelineSummaryFactory: Factory for creating PipelineSummary instances - wraps 3 dependencies

Benefits

  • Improved testability: Each component can be unit tested in isolation
  • Better maintainability: Smaller, focused classes are easier to understand
  • Enhanced extensibility: Components can be replaced or extended via DI
  • Clearer intent: Each dependency group has a meaningful name

Dependency Reduction

Before (15 deps) After (8 deps)
IPipelineInitializer IPipelineInitializer
IModuleDisposeExecutor IModuleDisposeExecutor
IPrintModuleOutputExecutor IPipelineExecutor
IPrintProgressExecutor IPipelineOutputCoordinator (new)
IPipelineExecutor IIgnoredModuleResultRegistrar (new)
IConsolePrinter IPipelineSummaryFactory (new)
IAfterPipelineLogger EngineCancellationToken
EngineCancellationToken ILogger
ILogger
IExceptionBuffer
IModuleResultRegistry
IModuleResultRepository
IPipelineContextProvider
IMetricsCollector
IParallelLimitProvider

Test plan

Closes #1425

🤖 Generated with Claude Code

thomhurst and others added 5 commits December 29, 2025 18:39
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract single-responsibility components from ExecutionOrchestrator to reduce
constructor dependencies from 15 to 8, addressing issue #1425.

New components:
- IPipelineOutputCoordinator: Coordinates all output operations (printing,
  logging, exception flushing) - wraps 5 dependencies
- IIgnoredModuleResultRegistrar: Handles registration of skipped results for
  ignored modules - wraps 4 dependencies
- IPipelineSummaryFactory: Factory for creating PipelineSummary instances -
  wraps 3 dependencies

Benefits:
- Improved testability: Each component can be unit tested in isolation
- Better maintainability: Smaller, focused classes are easier to understand
- Enhanced extensibility: Components can be replaced or extended via DI
- Clearer intent: Each dependency group has a meaningful name

Closes #1425

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Summary

Extracts 3 single-responsibility components from ExecutionOrchestrator to reduce constructor dependencies from 15 to 8.

Critical Issues

None found ✅

Suggestions

1. Potential resource leak in PipelineOutputScope (PipelineOutputCoordinator.cs:71-75)

The nested PipelineOutputScope class disposes _printModuleOutputExecutor and _printProgressExecutor, but these instances are also stored as fields in the parent PipelineOutputCoordinator class. If InitializeAsync() is called multiple times, you could have multiple scopes trying to dispose the same shared instances.

Current code:

private sealed class PipelineOutputScope : IPipelineOutputScope
{
    private readonly IPrintModuleOutputExecutor _printModuleOutputExecutor;
    private readonly IPrintProgressExecutor _printProgressExecutor;

    public PipelineOutputScope(
        IPrintModuleOutputExecutor printModuleOutputExecutor,
        IPrintProgressExecutor printProgressExecutor)
    {
        _printModuleOutputExecutor = printModuleOutputExecutor;
        _printProgressExecutor = printProgressExecutor;
    }

    public async ValueTask DisposeAsync()
    {
        _printModuleOutputExecutor.Dispose();
        await _printProgressExecutor.DisposeAsync().ConfigureAwait(false);
    }
}

Concern: Looking at ExecutionOrchestrator.cs:171, the scope is properly used with await using, but the coordinator fields remain. Consider whether:

  • InitializeAsync() should enforce single-call semantics (throw if called twice)
  • Or the scope should dispose of new instances rather than shared coordinator fields

2. Type check using GetType() != typeof() (IgnoredModuleResultRegistrar.cs:47)

if (_resultRepository.GetType() != typeof(NoOpModuleResultRepository))

This is fragile - it breaks if someone creates a derived class. Consider:

if (_resultRepository is not NoOpModuleResultRepository)

This pattern would also work better with DI if you wanted to register a "null object" as a specific marker type.

Verdict

APPROVE - Well-structured refactoring following established patterns from #1447. The dependency reduction is meaningful and the new abstractions have clear responsibilities.

@thomhurst thomhurst merged commit 8e7e39b into main Dec 29, 2025
10 of 13 checks passed
@thomhurst thomhurst deleted the refactor/reduce-execution-orchestrator-dependencies branch December 29, 2025 22:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reduce constructor dependencies in ExecutionOrchestrator (16 parameters)

1 participant