Skip to content

Refactor CompareCommand service configuration to match Program.cs pattern #36

@jbrinkman

Description

@jbrinkman

Problem Description

The CompareCommand.Execute() method currently contains inline dependency injection configuration, which is inconsistent with the clean ConfigureServices() pattern used in Program.cs. This violates separation of concerns, makes the code harder to test and maintain, and creates inconsistency across the application architecture.

Current Issues

  • CompareCommand.Execute() method likely contains mixed concerns (DI setup + command logic)
  • Inconsistent with the clean separation pattern established in Program.cs
  • Makes unit testing more difficult (harder to mock dependencies)
  • Violates single responsibility principle
  • Reduces code reusability and maintainability

Desired Solution

  • Extract dependency injection configuration from CompareCommand.Execute() into a separate ConfigureServices() method
  • Create a shared interface (IServiceConfigurator) implemented by both Program.cs and CompareCommand.cs
  • Ensure consistent service registration patterns across the application
  • Maintain clean separation between service configuration and business logic

Implementation Plan

Step 1: Create Shared Interface

Create src/DotNetApiDiff/Interfaces/IServiceConfigurator.cs:

using Microsoft.Extensions.DependencyInjection;

namespace DotNetApiDiff.Interfaces;

/// <summary>
/// Interface for classes that configure dependency injection services
/// </summary>
public interface IServiceConfigurator
{
    /// <summary>
    /// Configures services for dependency injection
    /// </summary>
    /// <param name="services">Service collection to configure</param>
    void ConfigureServices(IServiceCollection services);
}

Step 2: Update Program.cs

public class Program : IServiceConfigurator
{
    // Keep existing static method for backward compatibility
    public static void ConfigureServices(IServiceCollection services)
    {
        // Existing implementation remains the same
    }
    
    // Implement the interface
    void IServiceConfigurator.ConfigureServices(IServiceCollection services)
        => ConfigureServices(services);
}

Step 3: Refactor CompareCommand.cs

public class CompareCommand : Command<CompareSettings>, IServiceConfigurator
{
    /// <summary>
    /// Configures business logic services for the command
    /// </summary>
    /// <param name="services">Service collection to configure</param>
    public void ConfigureServices(IServiceCollection services)
    {
        // Move all DI configuration from Execute() method here
        // Register business logic services:
        // - AssemblyLoader
        // - ApiExtractor  
        // - ApiComparer
        // - Report formatters
        // - Configuration services
        // etc.
    }
    
    public override int Execute(CommandContext context, CompareSettings settings)
    {
        // Clean method focused purely on command execution logic
        // Create service container using ConfigureServices()
        // Execute business logic
        // Handle results and exit codes
    }
}

Benefits

Code Quality

  • Clean separation of concerns
  • Consistent architecture patterns
  • Improved readability and maintainability
  • Easier to understand and modify

Testing

  • Simplified unit testing (can mock service configuration)
  • Better isolation of business logic
  • Easier to test different service configurations

Consistency

  • Uniform service registration patterns
  • Consistent with established Program.cs architecture
  • Easier for new developers to understand

Acceptance Criteria

  • IServiceConfigurator interface created in src/DotNetApiDiff/Interfaces/
  • Program.cs implements IServiceConfigurator interface
  • CompareCommand.cs implements IServiceConfigurator interface
  • All DI configuration extracted from CompareCommand.Execute() to ConfigureServices()
  • Execute() method focuses solely on command execution logic
  • Consistent service registration patterns between Program and CompareCommand
  • All existing functionality preserved (no behavioral changes)
  • Unit tests updated to reflect new architecture
  • No breaking changes to public APIs
  • Code documentation updated for new patterns

Files to Modify

New Files

  • src/DotNetApiDiff/Interfaces/IServiceConfigurator.cs

Modified Files

  • src/DotNetApiDiff/Program.cs
  • src/DotNetApiDiff/Commands/CompareCommand.cs

Test Files

  • Update relevant unit tests to work with new architecture
  • Add tests for service configuration if not already present

Migration Strategy

  1. Create the IServiceConfigurator interface
  2. Update Program.cs to implement the interface (non-breaking change)
  3. Refactor CompareCommand.cs to extract service configuration
  4. Update tests to work with new architecture
  5. Verify all functionality works as expected

Priority

Medium - Improves code quality and maintainability without affecting end users

Additional Context

This refactoring aligns with established architectural patterns in the codebase and follows dependency injection best practices. It makes the code more testable, maintainable, and consistent without changing any external behavior.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions