Action System Architecture

Architecture of the Action Registry and Execution System

⚡ Action System Architecture

The Action System provides a decoupled, resilient way to execute operations across module boundaries.

It implements the Command Pattern to encapsulate requests as objects, enabling logging, queuing, and undo/redo capabilities.


🎯 Design Goals

  1. Decoupling: Sender does not need to know the receiver's implementation.
  2. Resilience: Circuit Breaker pattern prevents cascading failures.
  3. Observability: Full execution tracing and event emission.
  4. Discovery: Auto-discovery of actions via @PublicAction.

🏗️ High-Level Architecture

graph LR
    Client[Client Code] --> Handler[ActionHandlerService]
    Handler --> Registry[ActionRegistryService]
    Handler --> Circuit[CircuitBreakerService]
    Registry --> Metadata[Action Metadata]
    Handler --> Execute[ExecutableAction]
    Execute --> Event[EventBus]

Core Components

1. ActionHandlerService

The invoker. Responsible for executing actions, managing state, and handling errors.

2. ActionRegistryService

The central repository. Stores metadata for all available actions.

3. CircuitBreakerService

The safety valve. Monitors failures and "opens" the circuit to prevent system overload.

4. ActionDiscoveryService

Scans modules for actions decorated with @PublicAction and registers them.


🧩 Implementation Patterns

Command Interface

All actions must implement ExecutableAction.

interface ExecutableAction<T> {
  execute(params: ActionParams, context: ActionContext): Observable<T>;
  validate?(params: ActionParams): Observable<boolean>;
  cleanup?(): void;
}

Decorator: @PublicAction

Marks a class as a discoverable action.

@PublicAction({
  id: 'users:create',
  name: 'Create User',
  description: 'Creates a new user',
  permissions: ['users:create'],
  tags: ['users', 'crud']
})
export class CreateUserAction implements ExecutableAction<User> { ... }

🔄 Execution Flow

  1. Request: Client calls ActionHandlerService.execute('action-id', params).
  2. Lookup: Handler checks ActionRegistryService for metadata.
  3. Guard: Checks CircuitBreakerService. If open, fail fast.
  4. Validation: Calls action's validate() method.
  5. Execution: Calls execute().
  6. Result:
    • Success: Update stats, emit ActionExecutedEvent, return result.
    • Failure: Update stats, emit error, trigger Circuit Breaker.

🛡️ Resilience & Circuit Breaker

To prevent one failing module from bringing down the system, we use a Circuit Breaker.


📊 State Management

The system uses Angular Signals for reactive state management:


Status: ✅ Production Architecture Last Updated: December 3, 2025