Module System Architecture

Core architecture of the GoGrab Space module system

🏗️ Module System Architecture

The Module System is the backbone of GoGrab Space applications, enabling a modular, decoupled, and scalable architecture.

This document details the design and architecture of the module system, consolidating the core module-base and the orchestrating module-integrator.


🎯 Design Goals

  1. Strict Contracts: Define clear boundaries with BrowserModuleContract and ModuleManifest.
  2. Auto-Discovery: Self-registration via @ModuleDecorator.
  3. Multi-Tenancy: Built-in support for tenant contexts and configuration.
  4. Orchestration: Centralized lifecycle management via IntegratorService.
  5. Observability: Comprehensive event emission and structured logging.

🏛️ 8-Layer Architecture

The module system is organized into 8 distinct layers to separate concerns and ensure maintainability.

graph TD
    App[Application] --> Core
    Core[Core Layer] --> Management
    Management[Management Layer] --> Navigation
    Navigation[Navigation Layer] --> Communication
    Communication[Communication Layer] --> Bootstrap
    Bootstrap[Bootstrap Layer] --> Validation
    Validation[Validation Layer] --> Orchestration
    Orchestration[Orchestration Layer]

1. Core Layer

Orchestration and Lifecycle

2. Management Layer

Configuration and Activation

3. Navigation Layer

UI and Routing

4. Communication Layer

Inter-Module Exchange

5. Bootstrap Layer

Startup Sequence

6. Validation Layer

Security and Contracts

7. Orchestration Layer

Events and Workflow

8. Infrastructure Layer (Base)


📜 Contracts and Models

BrowserModuleContract

The mandatory interface for all modules.

export interface BrowserModuleContract {
  readonly manifest: ModuleManifest;
  configure<T = Record<string, unknown>>(config: T): Observable<void>;
  initialize(): Observable<void>;
  start(): Observable<void>;
  stop(): Observable<void>;
  destroy(): Observable<void>;
}

ModuleManifest

Metadata definition for modules.

export interface ModuleManifest {
  id: string;
  version: string;
  name: string;
  description?: string;
  permissions?: string[];
  publicActions?: any[]; // Array of action classes
  embeddableComponents?: ModuleEmbeddableComponent[];
  routes?: Route[];
  navigation?: NavigationConfig;
}

🔄 Lifecycle Management

Modules transition through specific states managed by the IntegratorService.

  1. Discovering: Scanning for @ModuleDecorator.
  2. Registered: Added to ModuleRegistryService.
  3. Configuring: configure() called with merged config.
  4. Initializing: initialize() called for resource setup.
  5. Starting: start() called to activate functionality.
  6. Running: Module is active and serving requests.
  7. Stopping: stop() called (e.g., on user logout or navigation).
  8. Destroyed: destroy() called for cleanup.

📝 Architecture Decision Records (ADR)

ADR-001: Configuration via Method vs. DI

Decision: Use configure(config) method. Rationale: Simplicity and better support for dynamic multi-tenant configurations without complex DI provider scopes.

ADR-002: Native Angular Lazy Loading

Decision: Use Angular's router for lazy loading instead of Module Federation. Rationale: Reduced build complexity, better type safety, and sufficient for the monorepo structure.

ADR-003: Centralized Orchestrator

Decision: Use IntegratorService as the single point of control. Rationale: Provides a unified view of system state and simplifies lifecycle coordination.

ADR-004: Signal-Based State

Decision: Use Angular Signals for internal state management. Rationale: Performance, fine-grained reactivity, and alignment with modern Angular best practices.


🛡️ Risks and Mitigations


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