README
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
@libs-core/action-registry
Comprehensive action registry and execution system for Angular 20 applications with Command Pattern implementation
📋 Overview
@libs-core/action-registry is a robust, scalable action management system that implements the Command Pattern for Angular 20 applications. It provides centralized registration, discovery, and execution of actions with built-in support for permissions, validation, circuit breaking, and event-driven architecture.
Key Features
- Command Pattern Implementation - Standardized action execution with Observable-based async support
- Dynamic Action Discovery - Automatic module action discovery using decorators
- Parameter Metadata System - Rich parameter metadata with automatic validation and dynamic UI generation ✨ NEW
- Permission-based Execution - Fine-grained access control with RBAC integration
- Angular 20 Signals Integration - Reactive state management with computed signals
- Standalone Components Support - Full compatibility with Angular 20 standalone API
- Module Contract System - Standardized module integration protocol
- Built-in Common Actions - Pre-built actions for navigation, UI, forms, and data operations
- Validation and Error Handling - Comprehensive validation with circuit breaker pattern
- Caching and Performance - Optimized execution with intelligent caching
- Event-driven Architecture - Full integration with Event System for audit and monitoring
🚀 Quick Start
Installation
The library is part of the GoGrab Space monorepo. No separate installation required.
// Import in your Angular application
import { ActionRegistryService, ActionHandlerService } from '@libs-core/action-registry';
Basic Usage
1. Register an Action
import { Injectable, inject } from '@angular/core';
import { Observable, of } from 'rxjs';
import {
ExecutableAction,
ActionParams,
ActionContext,
PublicAction
} from '@libs-core/action-registry';
@PublicAction({
id: 'hello-world',
name: 'Hello World',
description: 'Simple hello world action',
permissions: [],
tags: ['demo', 'basic']
})
@Injectable({ providedIn: 'root' })
export class HelloWorldAction implements ExecutableAction<string> {
execute(params: ActionParams, context: ActionContext): Observable<string> {
return of(`Hello ${params['name'] || 'World'}!`);
}
}
2. Register the Action with ActionRegistryService
import { Component, inject, OnInit } from '@angular/core';
import { ActionRegistryService } from '@libs-core/action-registry';
import { HelloWorldAction } from './actions/hello-world.action';
@Component({
selector: 'app-root',
standalone: true,
template: `<h1>Action Registry Demo</h1>`
})
export class AppComponent implements OnInit {
private readonly actionRegistry = inject(ActionRegistryService);
ngOnInit(): void {
// Register action
this.actionRegistry.register({
id: 'demo.hello-world',
actionClass: HelloWorldAction,
metadata: {
name: 'Hello World',
description: 'Simple hello world action',
type: 'custom',
permissions: [],
tags: ['demo'],
isAsync: true,
timeout: 5000,
retryable: false,
maxRetries: 0
}
});
}
}
3. Execute the Action
import { Component, inject } from '@angular/core';
import { ActionHandlerService } from '@libs-core/action-registry';
@Component({
selector: 'app-demo',
standalone: true,
template: `
<button (click)="executeAction()">Execute Action</button>
<p>{{ result }}</p>
`
})
export class DemoComponent {
private readonly actionHandler = inject(ActionHandlerService);
result = '';
executeAction(): void {
this.actionHandler
.execute('demo.hello-world', { name: 'Claude' })
.subscribe({
next: (result) => {
this.result = result.data || 'No data';
console.log('Action executed:', result);
},
error: (error) => {
console.error('Action failed:', error);
}
});
}
}
✨ Parameter Metadata System (NEW)
The Parameter Metadata System provides rich, typed parameter definitions with automatic validation and dynamic UI generation capabilities.
Key Benefits
- Type-Safe Parameters - Full TypeScript support with defined parameter types
- Automatic Validation - Runtime parameter validation before action execution
- Dynamic UI Generation - Automatically generate forms from parameter metadata
- Introspection API - Discover and analyze action parameters programmatically
- Fluent Builder API - Easy parameter definition with ActionParameterBuilder
Quick Example
import { ActionParameterBuilder, ParameterBuilders } from '@libs-core/action-registry';
// Define action with rich parameter metadata
export const MY_ACTION_CONFIG = {
id: 'user.create',
actionClass: CreateUserAction,
metadata: {
name: 'Create User',
description: 'Creates a new user account',
type: 'service',
permissions: ['user.create'],
tags: ['user', 'management'],
parameters: [
ParameterBuilders.email('email')
.description('User email address')
.helpText('Will be used for login')
.build(),
ActionParameterBuilder.string('username')
.required()
.minLength(3)
.maxLength(20)
.pattern(/^[a-zA-Z0-9_]+$/, 'Only alphanumeric and underscore allowed')
.placeholder('john_doe')
.example('john_doe')
.build(),
ActionParameterBuilder.number('age')
.min(18)
.max(120)
.label('Age')
.helpText('Must be 18 or older')
.build(),
ActionParameterBuilder.enum('role', ['admin', 'user', 'guest'])
.required()
.defaultValue('user')
.label('User Role')
.build()
],
isAsync: true,
timeout: 10000
}
};
Dynamic Form Generation
import { DynamicActionFormComponent } from '@libs-core/action-registry';
@Component({
selector: 'app-create-user',
standalone: true,
imports: [DynamicActionFormComponent],
template: `
<lib-action-dynamic-form
[config]="{
actionId: 'user.create',
layout: 'vertical',
showSubmitButton: true,
submitButtonText: 'Create User'
}"
(formSubmit)="onCreateUser($event)"
(formValidChange)="isFormValid = $event"
></lib-action-dynamic-form>
`
})
export class CreateUserComponent {
isFormValid = false;
onCreateUser(params: ActionParams): void {
// Form is already validated, execute action
this.actionHandler.execute('user.create', params).subscribe({
next: (result) => console.log('User created:', result.data),
error: (error) => console.error('Failed:', error)
});
}
}
Parameter Introspection
import { ParameterIntrospectionService } from '@libs-core/action-registry';
@Component({...})
export class ActionExplorerComponent {
private readonly introspection = inject(ParameterIntrospectionService);
exploreAction(actionId: string): void {
// Get parameter analysis
this.introspection.analyzeParameters(actionId).subscribe(analysis => {
console.log('Total parameters:', analysis.totalParameters);
console.log('Required:', analysis.requiredParameters);
console.log('Optional:', analysis.optionalParameters);
console.log('Validation complexity:', analysis.validationComplexity);
});
// Get required parameters
this.introspection.getRequiredParameters(actionId).subscribe(params => {
console.log('Required params:', params);
});
// Get default values
this.introspection.getDefaultValues(actionId).subscribe(defaults => {
console.log('Defaults:', defaults);
});
}
}
Supported Parameter Types
string- Text values with pattern validationnumber- Numeric values with min/max constraintsboolean- True/false valuesdate- Date/datetime valuesobject- Complex object structuresarray- Array of valuesenum- Predefined list of valuesany- Any type (use sparingly)
Validator Types
required- Field must have a valuemin/max- Numeric range validationminLength/maxLength- String/array length validationpattern- Regex pattern matchingemail- Email format validationurl- URL format validationenum- Value must be in allowed listcustom- Custom validation function
Pre-configured Builders
import { ParameterBuilders } from '@libs-core/action-registry';
// Common parameter patterns
ParameterBuilders.id() // UUID field
ParameterBuilders.email() // Email field
ParameterBuilders.url() // URL field
ParameterBuilders.page() // Pagination page number
ParameterBuilders.pageSize() // Pagination page size
ParameterBuilders.search() // Search query field
ParameterBuilders.dateParam() // Date field
ParameterBuilders.toggle() // Boolean switch
ParameterBuilders.select() // Dropdown selection
ParameterBuilders.textarea() // Multi-line text
Learn More
📦 Core Components
Services
- ActionRegistryService - Central registry for action management
- ActionHandlerService - Executes registered actions with circuit breaker protection
- ActionStateService - Manages execution state and history
- ActionDiscoveryService - Discovers actions from modules
- CircuitBreakerService - Prevents cascading failures
- ParameterValidationService ✨ NEW - Validates action parameters against metadata
- ParameterIntrospectionService ✨ NEW - Discovers and analyzes parameter metadata
Contracts and Interfaces
- ModuleActionContract - Standard interface for module action integration
- ExecutableAction - Interface all actions must implement
- ActionMetadata - Complete action metadata definition
Built-in Actions
- Navigation Actions - Route navigation, back/forward, external links
- UI Actions - Notifications, modals, theme changes
- Form Actions - Form validation, submission, reset
- Data Actions - CRUD operations, data synchronization
📚 Documentation
- Requirements - Functional and non-functional requirements
- Design & Architecture - Detailed system design and patterns
- API Reference - Complete API documentation
- Examples & Recipes - Common use cases and patterns
🏗️ Architecture
Command Pattern Implementation
┌─────────────────┐
│ Client Code │
└────────┬────────┘
│ execute()
▼
┌─────────────────────────┐
│ ActionHandlerService │
│ (Invoker) │
└────────┬────────────────┘
│ getMetadata()
▼
┌─────────────────────────┐
│ ActionRegistryService │
│ (Registry) │
└────────┬────────────────┘
│ getInstance()
▼
┌─────────────────────────┐
│ ExecutableAction │
│ (Command Interface) │
└────────┬────────────────┘
│
▼
┌─────────────────────────┐
│ Concrete Action │
│ (Command) │
└─────────────────────────┘
Key Design Patterns
- Command Pattern - Encapsulates actions as objects
- Registry Pattern - Centralized action registration
- Circuit Breaker Pattern - Resilient execution with failure protection
- Observer Pattern - Event-driven notifications
- Decorator Pattern - Action metadata with
@PublicAction
🔧 Configuration
Action Registration Options
interface ActionRegistrationConfig {
id: string; // Unique action identifier
actionClass: Type<any>; // Action class constructor
metadata: {
name: string; // Display name
description: string; // Action description
type: ActionType; // 'component' | 'service' | 'http' | 'custom'
permissions: string[]; // Required permissions
tags: string[]; // Categorization tags
isAsync: boolean; // Async execution flag
timeout: number; // Execution timeout (ms)
retryable: boolean; // Retry on failure
maxRetries: number; // Max retry attempts
};
overwrite?: boolean; // Allow overwriting existing
}
Circuit Breaker Configuration
interface CircuitBreakerConfig {
failureThreshold: number; // Failures before opening (default: 5)
resetTimeout: number; // Recovery timeout (default: 60000ms)
successThreshold: number; // Successes to close (default: 2)
name?: string; // Circuit identifier
}
🎯 Common Use Cases
1. Module-to-Module Communication
// Module A exposes action
@PublicAction({
id: 'process-payment',
name: 'Process Payment',
description: 'Processes payment transaction',
permissions: ['payment.process']
})
export class ProcessPaymentAction implements ExecutableAction<PaymentResult> {
execute(params, context): Observable<PaymentResult> {
// Implementation
}
}
// Module B executes action
this.actionHandler.execute('payment.process-payment', {
amount: 100,
currency: 'USD'
}).subscribe(result => {
console.log('Payment processed:', result.data);
});
2. Permission-based Action Execution
// Get actions available to current user
const userPermissions = ['navigation.basic', 'ui.notification'];
const executableActions = this.actionRegistry
.getExecutableActions(userPermissions);
console.log('Available actions:', executableActions.map(a => a.name));
3. Batch Action Execution
// Execute multiple actions in parallel
this.actionHandler.executeBatch([
{ actionId: 'core.save-form', parameters: { formData } },
{ actionId: 'core.send-notification', parameters: { message } },
{ actionId: 'core.update-ui', parameters: { theme } }
]).subscribe(result => {
console.log(`Batch complete: ${result.successful}/${result.total} succeeded`);
});
4. Transactional Execution with Rollback
// Execute actions as transaction with rollback support
this.actionHandler.executeTransaction([
{
actionId: 'create-order',
parameters: { order },
rollback: () => this.deleteOrder(order.id)
},
{
actionId: 'charge-payment',
parameters: { payment },
rollback: () => this.refundPayment(payment.id)
},
{
actionId: 'send-confirmation',
parameters: { email }
}
]).subscribe(result => {
if (result.success) {
console.log('Transaction completed');
} else {
console.log('Transaction failed, rolled back');
}
});
🧪 Testing
Test Coverage Status ✅
The Action Registry library has comprehensive test coverage:
Integration Tests: ✅ 10/10 passing (2 skipped due to DI complexity)
- Complete action lifecycle (register → discover → execute → state)
- Event-driven workflows with full event system integration
- Circuit breaker integration and failure protection
- Multi-action workflows (batch and sequential execution)
- Transaction execution with rollback support
- State synchronization across services
- Permission-based filtering and access control
- Error recovery workflows
- Performance and metrics tracking
Unit Tests: Service-level tests for core components
Observable-based: All tests use RxJS Observables (no Promises)
Angular 20 Compatible: TestBed with standalone components
Testing Actions
import { TestBed } from '@angular/core/testing';
import { lastValueFrom } from 'rxjs';
import { HelloWorldAction } from './hello-world.action';
import { ExecutionContext } from '@libs-core/action-registry';
describe('HelloWorldAction', () => {
let action: HelloWorldAction;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [HelloWorldAction]
});
action = TestBed.inject(HelloWorldAction);
});
it('should execute successfully', async () => {
const params = { name: 'Test' };
const context = new ExecutionContext();
const result = await lastValueFrom(action.execute(params, context));
expect(result).toBe('Hello Test!');
});
});
Testing Transaction Workflows
import { lastValueFrom } from 'rxjs';
it('should handle transaction with rollback', async () => {
const rollbackSpy = vi.fn(() => of(undefined));
// Use lastValueFrom for transaction observables that emit once after completion
const result = await lastValueFrom(
actionHandler.executeTransaction([
{ actionId: 'step-1', parameters: {}, rollback: rollbackSpy },
{ actionId: 'step-2', parameters: {} }, // This fails
])
);
expect(result.success).toBe(false);
expect(result.rolledBack).toBe(true);
expect(rollbackSpy).toHaveBeenCalled();
});
Important Testing Notes
Observable Streams:
- Use
lastValueFromfor transaction/batch operations that emit once after completion - Use
firstValueFromfor single action executions concatMapemits one value per processed item - choose the appropriate conversion based on when you need the result
Event Testing:
- ActionHandlerService events:
'ActionExecuted','ActionExecutionFailed'(PascalCase) - ActionStateService events:
'action:registered','action:execution:completed'(colon-separated)
Permission Testing:
- ActionMetadata uses
permissionsproperty (notrequiredPermissions) - Handle undefined permissions gracefully:
permissions: string[] | undefined
🔗 Dependencies
Required Libraries
@angular/core^20.0.0@angular/common^20.0.0@angular/router^20.0.0rxjs^7.8.0
Internal Dependencies
@libs-core/log-system- Logging infrastructure@libs-core/event-system- Event-driven architecture@gograb-space/utilities- Common utilities@libs-core/security(optional) - Permission checking
📈 Performance
- Cached Execution: Circuit breaker with intelligent caching
- Batch Optimization: Parallel execution with
forkJoin - Lazy Loading: Actions loaded on-demand
- Signal-based Reactivity: Minimal re-renders with computed signals
🛡️ Security
- Permission-based Access Control: Integration with RBAC system
- Validation: Parameter validation before execution
- Circuit Breaking: Prevents abuse and cascading failures
- Audit Logging: Complete execution history via Event System
🤝 Contributing
This library is part of the GoGrab Space monorepo. Follow the project's contribution guidelines.
📄 License
MIT License - GoGrab Space Team
🔍 Learn More
📊 Status
- ✅ Integration Tests: 10/10 passing
- ✅ Observable-based Architecture: Full RxJS implementation
- ✅ Circuit Breaker: Production-ready with failure protection
- ✅ Transaction Support: Rollback mechanism implemented and tested
- ✅ Event System Integration: Complete audit trail
- ✅ Permission System: RBAC integration working
Last Updated: 2025-10-15 Version: 1.0.0 Angular: 20.x TypeScript: 5.8 Test Status: Integration tests complete ✅