Skip to content

refactor: Extract common validation and error handling patterns in service_container to eliminate duplication #297

Description

@kcenon

Summary

Extract duplicated validation and error handling patterns from service_container.h into reusable helper methods to reduce code duplication and improve maintainability.

Background (Why)

  • The same validation patterns (freeze check, lock acquisition, already-registered check) are repeated in multiple registration methods
  • Factory exception handling with error wrapping is duplicated in singleton, transient, and scoped resolution branches
  • Audit logging calls follow the same pattern in multiple locations
  • Violates DRY principle - Kent Beck's "No Duplication" rule
  • File is 745 lines; extracting common patterns will improve readability

Scope (What)

Current State (Duplicated Patterns)

Pattern 1: Registration Validation (repeated in register_factory_internal and register_instance_internal)

// Lines 368-392 in register_factory_internal
if (is_frozen()) {
    interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
        interfaces::registry_action::register_service, type_name,
        interfaces::source_location::current(), false,
        "Container is frozen"));
    return make_error<std::monostate>(
        error_codes::REGISTRY_FROZEN,
        "Cannot register factory: container is frozen",
        "di::service_container"
    );
}

std::unique_lock lock(mutex_);

if (services_.find(interface_type) != services_.end()) {
    interfaces::RegistryAuditLog::log_event(...);
    return make_error<std::monostate>(
        di_error_codes::already_registered,
        "Service already registered: " + type_name,
        "di::service_container"
    );
}

// Lines 418-443 - Nearly identical pattern in register_instance_internal

Pattern 2: Factory Exception Handling (repeated 3 times in resolve_with_detection)

// Lines 527-535 (singleton branch)
try {
    instance = factory_copy(*this);
} catch (const std::exception& e) {
    return make_error<std::shared_ptr<void>>(
        di_error_codes::factory_error,
        std::string("Factory threw exception: ") + e.what(),
        "di::service_container"
    );
}

// Lines 556-564 (transient branch) - Same pattern
// Lines 586-594 (scoped branch) - Same pattern

Proposed State

// Extract validation helper
private:
    VoidResult validate_registration(
        std::type_index interface_type,
        const std::string& type_name) 
    {
        if (is_frozen()) {
            log_registration_failure(type_name, "Container is frozen");
            return make_error<std::monostate>(
                error_codes::REGISTRY_FROZEN,
                "Cannot register: container is frozen",
                "di::service_container"
            );
        }

        // Lock is handled by caller
        if (services_.find(interface_type) != services_.end()) {
            log_registration_failure(type_name, "Service already registered");
            return make_error<std::monostate>(
                di_error_codes::already_registered,
                "Service already registered: " + type_name,
                "di::service_container"
            );
        }

        return VoidResult::ok({});
    }

    // Extract factory invocation helper
    Result<std::shared_ptr<void>> invoke_factory_safe(
        const std::function<std::shared_ptr<void>(IServiceContainer&)>& factory)
    {
        try {
            return Result<std::shared_ptr<void>>::ok(factory(*this));
        } catch (const std::exception& e) {
            return make_error<std::shared_ptr<void>>(
                di_error_codes::factory_error,
                std::string("Factory threw exception: ") + e.what(),
                "di::service_container"
            );
        }
    }

    // Extract audit logging helper
    void log_registration_failure(
        const std::string& type_name, 
        const std::string& reason)
    {
        interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
            interfaces::registry_action::register_service, type_name,
            interfaces::source_location::current(), false, reason));
    }

Simplified usage:

VoidResult service_container::register_factory_internal(...) {
    auto validation = validate_registration(interface_type, type_name);
    if (!validation.is_ok()) return validation;

    std::unique_lock lock(mutex_);
    // ... rest of registration
}

// In resolve_with_detection:
case service_lifetime::singleton: {
    if (entry.is_instantiated) {
        return Result<std::shared_ptr<void>>::ok(entry.singleton_instance);
    }
    read_lock.unlock();
    
    auto result = invoke_factory_safe(factory_copy);
    if (!result.is_ok()) return result;
    // ... store instance
}

Impact Analysis (Where)

Location Lines Duplicated Pattern
register_factory_internal 368-410 Validation + audit logging
register_instance_internal 413-461 Validation + audit logging
resolve_with_detection 527-535 Factory exception handling
resolve_with_detection 556-564 Factory exception handling
resolve_with_detection 586-594 Factory exception handling

Estimated duplication: ~80 lines of duplicated patterns

Implementation Plan (How)

  1. Add validate_registration() private helper method
  2. Add invoke_factory_safe() private helper method
  3. Add log_registration_failure() private helper method
  4. Refactor register_factory_internal() to use helpers
  5. Refactor register_instance_internal() to use helpers
  6. Refactor singleton resolution to use invoke_factory_safe()
  7. Refactor transient resolution to use invoke_factory_safe()
  8. Refactor scoped resolution to use invoke_factory_safe()
  9. Add unit tests for new helper methods
  10. Update documentation

Acceptance Criteria

  • Common validation logic extracted to single method
  • Factory exception handling extracted to single method
  • No duplicated audit logging patterns
  • File line count reduced by ~40-60 lines
  • All existing tests passing
  • Thread safety preserved (verify with TSan)

Labels

  • refactor
  • code-quality
  • duplication

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions