-
Notifications
You must be signed in to change notification settings - Fork 4
Description
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+launchstructure wheremetadata.content_typedefines 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
- 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.
- 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_