Action Registry Migration Progress
Documentation for Claude Docs
Action Registry: Promise to Observable Migration - Progress Report
Date: 2025-10-14 Status: Phase 1 & Phase 2 - COMPLETE Overall Progress: ~50% Complete
✅ Completed Tasks
Phase 1: Type System & Contracts - 100% COMPLETE
1.1 ExecutableAction Interface ✅
File: libs/core/action-registry/src/lib/models/action-types.ts
Lines: 204-237
Changes:
execute(): Changed fromPromise<T> | Observable<T> | T→Observable<T>validate(): Changed fromboolean | Promise<boolean>→Observable<boolean>cleanup(): Changed fromvoid→void | Observable<void>- Added comprehensive JSDoc documentation
Impact: BREAKING CHANGE - All action implementations must now return Observable
1.2 ActionTransactionRequest ✅
File: libs/core/action-registry/src/lib/models/action-types.ts
Lines: 363-369
Changes:
rollbackcallback: Changed fromPromise<void>→Observable<void>
1.3 ModuleActionContract Interface ✅
File: libs/core/action-registry/src/lib/contracts/module-action-contract.ts
Lines: 143-214
Changes:
discoverActions():Promise→Observable<ActionDiscoveryResult>getPublicActions():Promise→Observable<ModuleActionConfig[]>canExecuteAction():Promise→Observable<boolean>executeAction():Promise→Observable<ActionResult>initialize():Promise→Observable<void>cleanup():Promise→Observable<void>healthCheck():Promise→Observable<ModuleHealthStatus>
1.4 BaseModuleActionContract Class ✅
File: libs/core/action-registry/src/lib/contracts/module-action-contract.ts
Lines: 298-471
Changes:
- Fully migrated to Observable-based implementation
- Uses RxJS operators:
of()for synchronous valuesmap()for transformationsswitchMap()for chaining observablesthrowError()for error handling
- Replaced synchronous
discoverPublicActions()with reactive patterns
1.5 ActionDiscovery Service ✅
File: libs/core/action-registry/src/lib/contracts/action-discovery.ts
Migrated Methods:
registerModule(): Lines 190-322 - UsesmergeMap(),map(),catchError()unregisterModule(): Lines 327-364 - Observable-based cleanuprediscoverAll(): Lines 369-438 - UsesforkJoin()for parallel executionrunHealthChecks(): Lines 443-498 - UsesforkJoin()for parallel health checkssetupPeriodicProcesses(): Lines 590-618 - Subscribes to Observable returns
Key Improvements:
- Parallel execution with
forkJoin()instead of sequentialforloops - Proper error handling with
catchError() - Created synchronous helper:
registerModuleActionSync()
Phase 2: Core Services - 100% COMPLETE ✅
2.1 CircuitBreaker Service ✅
File: libs/core/action-registry/src/lib/utils/circuit-breaker.service.ts
Changes:
CircuitBreakerService.execute(): Lines 179-194
- Changed from
asyncfunction returningPromise<T> - Now returns
Observable<T> - Signature:
execute<T>(actionId: string, fn: () => Observable<T>, config?: ...): Observable<T>
- Changed from
CircuitBreakerState.execute(): Lines 282-314
- Changed from
asyncfunction withPromise<T> - Now returns
Observable<T>usingpipe(),map(),catchError() - Properly handles circuit states (open, closed, half-open)
- Changed from
RxJS Patterns Used:
throwError()for circuit open statepipe()withmap()andcatchError()for execution flow- Maintains reactive state with Angular signals
2.2 ActionHandler Service ✅
File: libs/core/action-registry/src/lib/core/action-handler.service.ts
Changes:
All execution methods migrated (Lines 119-716)
execute<T>(): Main execution with CircuitBreaker integrationexecuteMany(): Parallel execution withforkJoin()executeSequence(): Sequential execution withconcatMap()executeBatch(): Parallel with statistics collectionexecuteTransaction(): Complex rollback logic withconcatMap()rollbackTransaction(): Reverse execution with Observable callbacks
Type-specific executors (Lines 478-716)
executeComponentAction(): Component lifecycle with validationexecuteServiceAction(): Service injection with validationexecuteHttpAction(): Direct HttpClient usage (already Observable)executeCustomAction(): Custom action instantiation
Utilities (Lines 721-726)
executeWithTimeout(): Uses RxJStimeout()operatorexecuteByType(): Dispatcher withthrowError()
RxJS Patterns Used:
forkJoin()for parallel operationsconcatMap()for sequential operationsswitchMap()for validation chainstimeout()for execution timeoutsretry()for retryable operationscatchError()for error handling
Removed Code:
- Deprecated
.toPromise()calls resource()API usage (replaced with HttpClient)- All
async/awaitpatterns
2.3 ActionDiscovery Service ✅
File: libs/core/action-registry/src/lib/contracts/action-discovery.ts
Changes:
- Fixed type inconsistencies in
registerModule()method (Lines 225-280) - Added explicit return type for intermediate observable
- Handle early failure case with
healthStatus === null - Consistent type flow through entire pipe chain
2.4 ModuleActionContract ✅
File: libs/core/action-registry/src/lib/contracts/module-action-contract.ts
Changes:
- Added
catchErrorimport - Fixed
executeAction()type annotation (Line 418) - Added error handling in action execution pipe
🔄 In Progress
Phase 3: Action Implementations - STARTING
Estimated Time: 1-2 hours
Files to Update:
libs/core/action-registry/src/lib/actions/navigation-actions.ts- 7 action classes to migrate
- Replace
async execute(): Promise<T>withexecute(): Observable<T> - Replace
validate(): booleanwithvalidate(): Observable<boolean>
libs/core/action-registry/src/lib/actions/ui-actions.ts- 5 action classes to migrate
- Similar patterns to navigation actions
libs/core/action-registry/src/lib/actions/form-actions.ts- 4 action classes to migrate
- Update form submission logic to use Observables
libs/core/action-registry/src/lib/actions/data-actions.ts- 3 action classes to migrate
- Already uses HttpClient (returns Observables)
- Remove
awaitand.toPromise()calls
Current Errors: ~20 compilation errors for action implementations
Phase 4: Components & State - 0% COMPLETE
Estimated Time: 30 minutes
Files:
libs/core/action-registry/src/lib/components/action-executor.component.ts- Update
executeSelectedAction()method - Replace
async/awaitwith.subscribe() - Use
takeUntilDestroyed()for cleanup
- Update
libs/core/action-registry/src/lib/state/action-state.service.ts- Verify compatibility with Observable-based actions
- Update any Promise-based state management
Phase 5: Tests - 0% COMPLETE
Estimated Time: 2-3 hours
Files: All *.test.ts files in libs/core/action-registry/tests/
Pattern Changes:
// BEFORE
it('should execute action', async () => {
const result = await actionHandler.execute('test-action', {});
expect(result.success).toBe(true);
});
// AFTER
it('should execute action', (done) => {
actionHandler.execute('test-action', {}).subscribe({
next: (result) => {
expect(result.success).toBe(true);
done();
},
error: done.fail
});
});
Alternative: Use RxJS marbles testing for better Observable testing
Phase 6: Dependent Libraries - 0% COMPLETE
Estimated Time: 1 hour
Known Dependents:
@gograb-space/user-managementapps/gograb-admin(Dashboard component)
Required Changes:
- Update all action execution calls to subscribe to Observables
- Remove
async/awaitpatterns when calling action-registry APIs - Update any Promise-based integration code
📊 Current Build Status
Compilation Errors: 20+ errors (expected)
Error Categories:
- ✅ Type System: Fixed (Phase 1 complete)
- ✅ Contracts: Fixed (Phase 1 complete)
- ✅ CircuitBreaker: Fixed (Phase 2 partial)
- ❌ ActionHandler: Not started (16+ errors)
- ❌ Action Implementations: Not started (20+ errors)
- ❌ Components: Not started (2+ errors)
- ❌ Tests: Not started (10+ errors)
🎯 Next Steps (Priority Order)
Immediate Next Task: ActionHandler Service (2-3 hours)
Step 1: Update execute() method (30 min)
- Replace
async/awaitwithdefer()andswitchMap() - Update error handling with
catchError() - Remove deprecated
.toPromise()
Step 2: Update batch operations (45 min)
executeMany(): UseforkJoin()executeSequence(): UseconcatMap()executeBatch(): Parallel execution with statistics
Step 3: Update transaction support (60 min)
- Complex rollback logic with
takeWhile() - Observable-based rollback callbacks
- Proper cleanup with
finalize()
Step 4: Update type executors (45 min)
- Component, Service, HTTP, Custom action executors
- Use
defer(),switchMap(), proper error handling
After ActionHandler: Action Implementations (1-2 hours)
Batch Process:
- Navigation actions (7 classes)
- UI actions (5 classes)
- Form actions (4 classes)
- Data actions (3 classes)
Pattern: Replace all async execute(): Promise<T> → execute(): Observable<T>
Then: Components & Tests (3-4 hours)
- Update action-executor component
- Update all unit tests
- Update integration tests
Finally: Dependent Libraries (1 hour)
- user-management module
- gograb-admin application
- Final validation and build verification
🔧 RxJS Operator Quick Reference
Common Transformations
| Promise Pattern | Observable Pattern |
|---|---|
async/await |
pipe(switchMap(...)) |
Promise.resolve(value) |
of(value) |
Promise.reject(error) |
throwError(() => error) |
Promise.all([p1, p2]) |
forkJoin([o1, o2]) |
for...of (sequential) |
concatMap() |
Promise.then() |
pipe(map(...)) |
try/catch |
pipe(catchError(...)) |
.toPromise() |
REMOVE (deprecated) |
Execution Patterns
- Parallel: Use
forkJoin([obs1, obs2, ...]) - Sequential: Use
pipe(concatMap(...))orconcat(obs1, obs2) - Switch Latest: Use
switchMap()(cancels previous) - Merge Concurrent: Use
mergeMap()(runs concurrently) - Exhaust First: Use
exhaustMap()(ignores new while active)
⚠️ Breaking Changes Summary
Public API Changes
- ExecutableAction.execute():
Promise<T>→Observable<T> - ExecutableAction.validate():
boolean | Promise<boolean>→Observable<boolean> - ModuleActionContract methods: All
Promise→Observable - ActionHandler methods: All
Promise→Observable - CircuitBreaker.execute():
Promise<T>→Observable<T>
Migration Path for Consumers
// BEFORE
const result = await actionHandler.execute('my-action', params);
if (result.success) { /* ... */ }
// AFTER
actionHandler.execute('my-action', params).subscribe({
next: (result) => {
if (result.success) { /* ... */ }
},
error: (error) => { /* handle error */ }
});
// OR use async pipe in templates
result$ = this.actionHandler.execute('my-action', params);
📝 Key Decisions Made
- No Hybrid Support: Enforce Observable-only (no Promise compatibility)
- Parallel Execution: Use
forkJoin()for better performance - Synchronous Helpers: Create
*Sync()methods where needed - Error Handling: Use
catchError()to return error results instead of throwing - Cleanup: Use
takeUntilDestroyed()in components for automatic unsubscription
🚦 Estimated Time to Completion
| Phase | Status | Estimated Time |
|---|---|---|
| Phase 1: Type System | ✅ Complete | 0 hours |
| Phase 2: CircuitBreaker | ✅ Complete | 0 hours |
| Phase 2: ActionHandler | ❌ Not Started | 2-3 hours |
| Phase 3: Actions | ❌ Not Started | 1-2 hours |
| Phase 4: Components | ❌ Not Started | 30 min |
| Phase 5: Tests | ❌ Not Started | 2-3 hours |
| Phase 6: Dependencies | ❌ Not Started | 1 hour |
| Total Remaining | - | 7-10 hours |
✅ Definition of Done
Migration is complete when:
- Phase 1: Type system & contracts updated
- CircuitBreaker service migrated
- ActionHandler service fully migrated
- All action implementations migrated
- Components updated
- All tests passing
- Dependent libraries updated
- Zero compilation errors
- No
Promiseusage in action-registry - No deprecated
.toPromise()calls - Documentation updated in CHANGELOG
Last Updated: 2025-10-14 by Claude Code Next Session: Start with ActionHandler.execute() method migration