System Analysis - Complete Architecture Reference

Unified view of modules, embeddables, widgets, flows, backend APIs, and delivery pipeline with real code samples

System Analysis - Complete Architecture Reference

This document is the single source of truth for understanding GoGrab Space's platform generation architecture. It includes real code samples from the codebase to illustrate each concept.


1. Architecture Overview

1.1 The Component Hierarchy

┌─────────────────────────────────────────────────────────────────┐
│                        UNIVERSE (Space)                          │
│  Composition layer - places multiple modules on a canvas         │
│  Route: /modular-universe                                        │
├─────────────────────────────────────────────────────────────────┤
│                          MODULES                                 │
│  NX libraries implementing BrowserModuleContract                 │
│  Own: business logic, actions, configuration, embeddables        │
│  Location: modules/ folder                                       │
├─────────────────────────────────────────────────────────────────┤
│                       EMBEDDABLES                                │
│  Standalone Angular components with contract (inputs/outputs)    │
│  Registered in module manifests and/or embeddable registry       │
│  Location: libs/libs-ui-core/, module embeddables/               │
├─────────────────────────────────────────────────────────────────┤
│                         WIDGETS                                  │
│  Embeddable + editor metadata (property groups, variants)        │
│  For page composition in editor; NOT source of truth             │
│  Location: libs/libs-widgets/widgets/                            │
├─────────────────────────────────────────────────────────────────┤
│                       COMPONENTS                                 │
│  Raw Angular components with no platform semantics               │
│  Building blocks for embeddables                                 │
└─────────────────────────────────────────────────────────────────┘

1.2 Key Directories

Directory Purpose
apps/gograb-space/ Main platform application (port 5200)
apis/backend-api/ NestJS API with MongoDB (port 3000)
modules/ Business modules (NX libraries)
libs/libs-ui-core/ Embeddable UI atoms (source of truth)
libs/libs-widgets/widgets/ Widget wrappers for editor
libs/libs-widgets/widget-builder/ Widget builder tool
libs/libs-structural/modularity-lib/ Module workbench (6-step builder)
libs/libs-structural/editor-web-lib/ Page composition editor
libs/libs-space/universe-canvas/ Universe canvas builder

2. Embeddables (Source of Truth)

2.1 What is an Embeddable?

An embeddable is a standalone Angular component that:

2.2 Real Example: UiButtonComponent

Location: libs/libs-ui-core/ui-core/src/lib/components/ui-button/ui-button.component.ts

import { Component, input, ChangeDetectionStrategy } from '@angular/core';
import { NgClass, NgStyle } from '@angular/common';
import { BaseWidgetComponent } from '@libs-widgets/widget-system';

@Component({
  selector: 'ui-button',
  standalone: true,
  imports: [NgClass, NgStyle],
  templateUrl: './ui-button.component.html',
  styleUrls: ['./ui-button.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UiButtonComponent extends BaseWidgetComponent {
  // CONTRACT: Explicit inputs using Angular signals
  text = input<string>('Button');
  type = input<'button' | 'submit' | 'reset'>('button');
  variant = input<string>('primary');
  size = input<string>('md');
  shape = input<string>('rounded');
  effect = input<string>('pulse');
  color = input<string | undefined>(undefined);
  leftIcon = input<string>('');
  rightIcon = input<string>('');
  disabled = input<boolean>(false);
  override loading = input<boolean>(false);
  readonly customStyles = input<Record<string, string> | undefined>(undefined);

  // Lifecycle
  onWidgetInit(): void {
    this.markReady();
  }

  onWidgetDestroy(): void {
    // Cleanup handled by base class
  }

  // Output via base class event system
  onClick(event: MouseEvent): void {
    if (this.disabled() || this.loading()) {
      event.preventDefault();
      return;
    }
    this.emitWidgetEvent('click', { 
      mouseX: event.clientX, 
      mouseY: event.clientY, 
      timestamp: Date.now() 
    });
  }
}

2.3 Embeddable Contract

Every embeddable must define:

  1. Inputs - Signal-based inputs with types and defaults
  2. Outputs - Events emitted via emitWidgetEvent() or @Output()
  3. Lifecycle - onWidgetInit() and onWidgetDestroy() methods
  4. Selector - Unique component selector (e.g., ui-button)

3. Widgets (Editor Wrappers)

3.1 What is a Widget?

A widget wraps an embeddable and adds:

3.2 Real Example: ButtonWidgetComponent

Location: libs/libs-widgets/widgets/src/lib/components/button-widget/button-widget.component.ts

import { Component, computed, inject, input, Output, EventEmitter } from '@angular/core';
import { Widget, BreakpointKey } from '@libs-foundational/core-models';
import { UiButtonComponent } from '@libs-ui-core/ui-core';
import { BaseWidgetComponent } from '@libs-widgets/widget-system';
import { usePropertyStyles } from '../../services/property-applicators';
import { WIDGET_FLOW_EXECUTOR_SERVICE } from '@libs-foundational/core-angular';

@Component({
  selector: 'lib-button-widget',
  standalone: true,
  imports: [FormsModule, UiButtonComponent],  // <-- Imports the embeddable
  templateUrl: './button-widget.component.html',
  styleUrls: ['./button-widget.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ButtonWidgetComponent extends BaseWidgetComponent {
  // Widget inputs from editor
  readonly widget = input.required<Widget>();
  readonly isSelected = input<boolean>(false);
  readonly isEditing = input<boolean>(false);
  readonly activeBreakpoint = input<BreakpointKey>('base');

  @Output() widgetChange = new EventEmitter<Widget>();
  @Output() widgetAction = new EventEmitter<{ widget: Widget; action: string; data?: any }>();

  // Optional flow executor for action binding
  private readonly flowExecutor = inject(WIDGET_FLOW_EXECUTOR_SERVICE, { optional: true });

  // Merge widget props with embeddable props
  config = computed<ButtonWidgetConfig>(() => {
    const defaultConfig = { text: 'Button', buttonType: 'primary', /* ... */ };
    const widgetProps = this.widget()?.properties ?? {};
    return { ...defaultConfig, ...widgetProps };
  });

  // SMART PROPERTY SYSTEM: Apply font + appearance via applicators
  // Replaces 70+ lines of manual style checking with 1 line!
  customButtonStyles = usePropertyStyles(this.widget, this.activeBreakpoint, ['font', 'appearance']);

  // Handle button click - can trigger flows
  async handleButtonClick(event: MouseEvent): Promise<void> {
    const action = this.config().action;
    
    if (action === 'flow' && this.config().flowId && this.flowExecutor) {
      await this.flowExecutor.executeFlow(this.config().flowId, {
        widgetId: this.widget().id,
        event: 'click'
      });
    }
    
    this.widgetAction.emit({ widget: this.widget(), action: 'click', data: event });
  }
}

3.3 Widget Template (wrapping embeddable)

<!-- button-widget.component.html -->
<ui-button
  [text]="buttonText()"
  [type]="buttonType_attr()"
  [variant]="buttonType()"
  [size]="buttonSize()"
  [color]="buttonColor()"
  [shape]="buttonShape()"
  [effect]="buttonEffect()"
  [disabled]="buttonDisabled()"
  [loading]="buttonLoading()"
  [leftIcon]="leftIcon()"
  [rightIcon]="rightIcon()"
  [customStyles]="customButtonStyles()"
  (click)="handleButtonClick($event)"
/>

3.4 Widget vs Embeddable Summary

Aspect Embeddable Widget
Source of truth YES NO
Has property groups NO YES
Has variants NO YES
Used in modules YES Indirectly
Used in editor Via widget YES
Location ui-core, ui-forms libs-widgets

4. Modules (Business Logic)

4.1 What is a Module?

A module is an NX library that:

4.2 Real Example: UserManagementModule

Location: modules/new-user-management/src/lib/user-management.module.ts

import { Injectable, inject } from '@angular/core';
import { Observable, of } from 'rxjs';
import {
  BrowserModuleContract,
  ModuleManifest,
  ModuleDecorator,
} from '@libs-core/module-base';
import { ActionParameterBuilder, ParameterBuilders } from '@libs-core/action-registry';
import { UserListWidgetComponent } from './embeddables';
import { CreateUserAction, UpdateUserAction, DeleteUserAction } from './actions';

@ModuleDecorator({
  id: 'user-management',
  version: '1.0.0',
  name: 'User Management',
  description: 'Comprehensive user management module',
  permissions: [
    { id: 'users:read', name: 'users:read', resource: 'users', action: 'read' },
    { id: 'users:create', name: 'users:create', resource: 'users', action: 'create' },
    { id: 'users:update', name: 'users:update', resource: 'users', action: 'update' },
    { id: 'users:delete', name: 'users:delete', resource: 'users', action: 'delete' },
  ],
  navigation: {
    menuItems: [
      {
        id: 'users-main',
        label: 'Users',
        icon: 'users',
        route: 'list',
        permissions: ['users:read'],
        children: [
          { id: 'users-list', label: 'All Users', route: 'list' },
          { id: 'users-create', label: 'Create User', route: 'create' },
        ],
      },
    ],
  },
})
@Injectable({ providedIn: 'root' })
export class UserManagementModule implements BrowserModuleContract {
  readonly manifest: ModuleManifest = {
    id: 'user-management',
    version: '1.0.0',
    name: 'User Management',
    basePath: 'users',
    
    // EMBEDDABLES: Components this module exposes
    embeddableComponents: [
      {
        id: 'users:list-widget',
        componentType: UserListWidgetComponent,
        displayName: 'User List Widget',
        description: 'Compact widget displaying a list of users',
        category: 'widget',
        permissions: ['users:read'],
        inputs: [
          ActionParameterBuilder.number('limit')
            .description('Maximum number of users to display')
            .defaultValue(5)
            .min(1).max(50)
            .build(),
          ActionParameterBuilder.string('roleFilter')
            .description('Filter users by role')
            .build(),
          ParameterBuilders.toggle('showAvatar', 'Show Avatar')
            .defaultValue(true)
            .build(),
        ],
      },
    ],
    
    // PUBLIC ACTIONS: Can be invoked by other modules
    publicActions: [
      CreateUserAction,
      UpdateUserAction,
      DeleteUserAction,
    ],
  };

  // Lifecycle methods
  configure<T>(config: T): Observable<void> { return of(void 0); }
  initialize(): Observable<void> { return of(void 0); }
  start(): Observable<void> { return of(void 0); }
  stop(): Observable<void> { return of(void 0); }
  destroy(): Observable<void> { return of(void 0); }
}

4.3 Module Actions

Actions are the public API of a module. Other modules can invoke them via the Action Registry.

Location: modules/new-user-management/src/lib/actions/create-user.action.ts

import { Injectable, inject } from '@angular/core';
import { Observable, of, map, catchError } from 'rxjs';
import {
  PublicAction,
  ExecutableAction,
  ActionContext,
  ActionParameterBuilder,
  ParameterBuilders,
} from '@libs-core/action-registry';
import { EventBusService } from '@libs-core/event-system';
import { UserService } from '../services/user.service';

export interface CreateUserInput {
  email: string;
  firstName: string;
  lastName: string;
  roleId: string;
  tenantId: string;
}

export interface CreateUserOutput {
  success: boolean;
  userId?: string;
  error?: string;
}

@PublicAction({
  id: 'users:create-user',
  name: 'Create User',
  description: 'Create a new user with role assignment',
  permissions: ['users:create'],
  parameters: [
    ParameterBuilders.email('email')
      .label('Email Address')
      .build(),
    ActionParameterBuilder.string('firstName')
      .required('First name is required')
      .minLength(2).maxLength(50)
      .build(),
    ActionParameterBuilder.string('lastName')
      .required('Last name is required')
      .build(),
    ActionParameterBuilder.string('roleId')
      .required('Role is required')
      .build(),
  ],
})
@Injectable({ providedIn: 'root' })
export class CreateUserAction implements ExecutableAction<CreateUserInput, CreateUserOutput> {
  private readonly userService = inject(UserService);
  private readonly eventBus = inject(EventBusService);

  execute(params: CreateUserInput, context: ActionContext): Observable<CreateUserOutput> {
    return this.userService.createUser({
      email: params.email,
      firstName: params.firstName,
      lastName: params.lastName,
      roleId: params.roleId,
    }).pipe(
      map(user => {
        // Emit event for other modules to react
        this.eventBus.emit('user:created', { userId: user.id, tenantId: params.tenantId });
        return { success: true, userId: user.id };
      }),
      catchError(error => of({ success: false, error: error.message }))
    );
  }
}

4.4 Invoking Actions from Other Modules

// From another module or widget
const actionRegistry = inject(ActionRegistryService);

actionRegistry.execute('users:create-user', {
  email: '[email protected]',
  firstName: 'John',
  lastName: 'Doe',
  roleId: 'role_123',
  tenantId: context.tenantId
}).subscribe(result => {
  if (result.success) {
    console.log('User created:', result.userId);
  }
});

5. Property System

5.1 How Properties Work

The property system provides:

  1. Property Definitions - Atomic properties (backgroundColor, fontSize, etc.)
  2. Property Templates - Groups of properties for widget types
  3. Property Applicators - Functions that apply properties to components
  4. Variants - Presets of property values

5.2 Using usePropertyStyles

The usePropertyStyles hook automatically applies property groups:

// Instead of 70+ lines of manual style checking:
customButtonStyles = usePropertyStyles(
  this.widget,           // Widget signal
  this.activeBreakpoint, // Current breakpoint
  ['font', 'appearance'] // Property groups to apply
);

// Returns computed styles like:
// {
//   'font-size': '16px',
//   'font-weight': '600',
//   'background-color': '#3b82f6',
//   'border-radius': '8px'
// }

5.3 Backend Property Endpoints

Controller: apis/backend-api/src/app/properties/properties.controller.ts

Endpoint Method Description
/api/spaces/:spaceId/properties GET List all properties
/api/spaces/:spaceId/properties POST Create property
/api/spaces/:spaceId/properties/templates GET List templates
/api/spaces/:spaceId/properties/templates/ai-context GET AI generation context
/api/spaces/:spaceId/properties/core-sync GET Flash persistence sync

6. Backend API Structure

6.1 Widget Endpoints

Controller: apis/backend-api/src/app/widgets/widgets.controller.ts

// Widget DTO
interface CreateWidgetDto {
  name: string;
  type: string;
  category: string;
  description?: string;
  variants?: Array<{
    id: string;
    name: string;
    properties: Record<string, unknown>;
  }>;
  propertyConfig?: {
    groups: Array<{
      id: string;
      title: string;
      properties: Array<{ key: string; label: string; type: string; }>;
    }>;
    values: Record<string, unknown>;
  };
}
Endpoint Method Description
/api/spaces/:spaceId/widgets GET List all widgets
/api/spaces/:spaceId/widgets POST Create widget
/api/spaces/:spaceId/widgets/:id GET Get widget
/api/spaces/:spaceId/widgets/:id PUT Update widget
/api/spaces/:spaceId/widgets/:id DELETE Delete widget
/api/spaces/:spaceId/widgets/:id/variants GET Get variants
/api/spaces/:spaceId/widgets/:id/variants POST Create variant
/api/spaces/:spaceId/widgets/:id/variants/:variantId PUT Update variant
/api/spaces/:spaceId/widgets/:id/embeddable GET Get embeddable metadata

6.2 Space Modules Endpoints

Controller: apis/backend-api/src/app/spaces/space-modules.controller.ts

Endpoint Method Description
/api/spaces/:spaceId/modules GET List modules
/api/spaces/:spaceId/modules POST Create module
/api/spaces/:spaceId/modules/batch POST Batch upsert
/api/spaces/:spaceId/modules/:id GET Get module
/api/spaces/:spaceId/modules/:id/activate PATCH Activate
/api/spaces/:spaceId/modules/:id/deactivate PATCH Deactivate
/api/spaces/:spaceId/modules/for-universe/:universeId GET Get for universe

6.3 Universe Deployment Endpoints

Controller: apis/backend-api/src/app/universes/universe-deployment.controller.ts

Endpoint Method Description
/api/universes/initialize POST Initialize universe (create collections)
/api/universes/:id/publish POST Publish universe
/api/universes/:id/unpublish POST Unpublish universe
/api/universes/:id/deployment-status GET Get deployment status
/api/universes/:id/verify-domain POST Verify custom domain

6.4 Singularity (Flows) Endpoints

Controller: apis/backend-api/src/app/singularity/singularity.controller.ts

Endpoint Method Description
/singularity/flows POST Create flow
/singularity/flows/:id GET Get flow
/singularity/flows/:id PUT Update flow
/singularity/flows/:id DELETE Delete flow
/singularity/flows/:id/execute POST Execute flow
/singularity/flows/:id/validate POST Validate flow

7. Routing Structure

7.1 Module Workbench Routes

Location: libs/libs-structural/modularity-lib/src/modularity.routes.ts

/modularity
├── /                     -> Dashboard (browse modules)
├── /workbench            -> Module workbench
│   ├── /models           -> Step 1: Define entities
│   ├── /state            -> Step 2: Configure state
│   ├── /actions          -> Step 3: Create actions
│   ├── /actions/:actionId -> Action flow builder
│   ├── /ui               -> Step 4: Design embeddables
│   ├── /test             -> Step 5: Test module
│   └── /deploy           -> Step 6: Export/deploy
├── /workbench/:moduleId  -> Edit existing module
├── /ai-builder           -> AI module generation
└── /ai-scaffold          -> Quick AI scaffold

7.2 Widget Builder Routes

Location: libs/libs-widgets/widget-builder/src/widget-builder.routes.ts

/widget-builder
├── /                     -> Builder workspace
├── /workspace            -> AI/Templates/Components modes
├── /workspace/:mode      -> Specific mode
├── /variant-editor/:widgetId -> Edit variants
├── /property-manager     -> Manage properties
└── /property-manager/:id -> Edit property

7.3 Editor Routes

Location: libs/libs-structural/editor-web-lib/src/lib/editor.routes.ts

/editor
├── /                     -> Blank canvas
├── /new                  -> Create new page
├── /page/:pageSlug       -> Edit page
├── /universe/:universeId/page/:pageSlug -> Edit in universe context
├── /module/:moduleId     -> Build module content
├── /ai-assist            -> AI page generation
└── /widget-builder       -> Nested widget builder

7.4 Universe Canvas Routes

Location: libs/libs-structural/modular-universe/src/lib/modular-universe.routes.ts

/modular-universe
├── /                     -> Universe canvas builder
├── /universe/:universeId -> Edit specific universe
└── /galaxy/:galaxyId     -> Edit galaxy

8. Current Gaps (Honest Assessment)

8.1 What Works

8.2 What Needs Work

Gap Status Description
End-to-end 0→100 build Not demonstrated No reference space built through entire pipeline
Deploy step implementation Partial UI exists, actual NX lib generation incomplete
Artifact bundling Stub deployment.service.ts is placeholder
Embeddable deduplication Pending ui-core vs widgets duplicates exist
Property migration In progress Some widgets still use manual styles
Variant persistence verification Untested CRUD exists but E2E not verified
Inter-module communication Implicit Event/action patterns not formalized

8.3 Duplication Issues to Resolve

Components that exist in both ui-core AND widgets (need consolidation):

Rule: ui-core is source of truth; widgets WRAP, not re-implement.


9. How to Use This Architecture

9.1 Creating a New Module

  1. Use /modularity/workbench to define:

    • Models (entities with fields)
    • State (collections, storage)
    • Actions (CRUD operations)
    • UI (embeddables)
  2. Export to modules/ folder

  3. Register embeddables in manifest:

embeddableComponents: [
  {
    id: 'my-module:my-widget',
    componentType: MyWidgetComponent,
    inputs: [/* ... */]
  }
]
  1. Wire to backend via /api/spaces/:spaceId/modules

9.2 Creating a Widget from Embeddable

  1. Create wrapper component in libs/libs-widgets/widgets/
  2. Import the embeddable from ui-core
  3. Add property groups using usePropertyStyles
  4. Register via backend widget endpoint
  5. Define variants

9.3 Composing Pages

  1. Open /editor/page/:pageSlug
  2. Drag widgets from library
  3. Configure properties in panel
  4. Bind events to module actions (via flow executor or direct)
  5. Save page

9.4 Deploying a Universe

  1. Open /modular-universe/universe/:universeId
  2. Place modules on canvas
  3. Configure behavior triggers
  4. Click Deploy → calls /api/universes/:id/publish
  5. Monitor status via /api/universes/:id/deployment-status

10. Success Criteria

To declare the platform "ready for end-to-end generation":


Last Updated: December 9, 2025
Session ID: 10.2