Skip to content

[Feature]: Refactor stack type handling from plugins via extensible stack-type implementations #5

@nacidai

Description

@nacidai

Description

This refactoring eliminates if/elif anti-patterns by using a direct handler dispatch model where handlers directly process operations without complex double dispatch callbacks.

The purpose is to eliminate the if/elif anti-pattern in stack type handling across the Muto Composer system. The refactoring introduces a double dispatch pattern where stack type handlers delegate to plugins, inverting control and simplifying both stack handlers and plugin implementations.

Initially support archive and json style stack in addition to legacy models.

Suggested Solution

Design Principles

The architecture has been streamlined from a complex double-dispatch pattern to a direct handler invocation model:1. Stack Type Categories:

  • Properly Defined Solutions: Stacks with metadata + launch structure where metadata.content_type defines the type (e.g., stack/json, stack/archive)

  • Before (Complex): Plugin → Handler → Plugin.accept_stack() → Handler.process_operation() - Legacy Format: Fallback validation for unrecognized formats to check if they match legacy launch/json format (raw nodes/composables)

  • After (Simple): Plugin → Handler.apply_to_plugin() → Direct operation processing

  1. Double Dispatch Pattern: Stack handlers delegate plugin-specific logic to plugins themselves using a visitor-like pattern, avoiding hardcoded logic in handlers

This eliminates unnecessary indirection while maintaining clean separation of concerns.

  1. Plugin Extensibility: Pipeline plugins (Compose, Launch, Provision, etc.) define their own operations that stack handlers can delegate to

Core Architecture

Architecture Diagrams

1. Stack Type Handlers

Stack Handler Class Design with Double Dispatch

Handlers encapsulate all logic for a specific stack type. Each handler:

  • Detects if it can handle a payload (can_handle())```mermaid

  • Directly processes all operations for that stack type (apply_to_plugin())classDiagram

  • Contains operation-specific logic (_start_*(), _kill_*(), _apply_*(), _provision_*()) class StackTypeHandler

**Base Handler Interface

class StackTypeHandler(ABC):    }

    """Abstract base class for stack type handlers."""    

        class StackContext {

    @abstractmethod        +stack_data: Dict~str,Any~

    def can_handle(self, payload: Dict[str, Any]) -> bool:        +metadata: Dict~str,Any~

        """Determine if this handler can process the given payload."""        +operation: str

        pass        +logger: Optional~Any~

        }

    @abstractmethod    

    def apply_to_plugin(self, plugin: BasePlugin, context: StackContext, 

                       request, response) -> any:    

        """    class Plugin {

        Process the stack operation directly.        <<interface>>

        Handler contains all operation logic for its stack type.        +accept_stack(handler: StackTypeHandler, context: StackContext) bool

        """    }

        pass    

```    class LaunchPlugin {

        +accept_stack(handler: StackTypeHandler, context: StackContext) bool

### 2. Stack Context        +handle_start(request, response) None

        +handle_kill(request, response) None

A simple dataclass that carries operation context:        +handle_apply(request, response) None

    }

### Alternatives

_No response_

### Additional Context

_No response_

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions