Phase 1 - Data Unification
Creating the single source of truth for all platform data
Phase 1: Data Unification
Priority: P0 - CRITICAL
Duration: 2-3 days
Status: ๐ข IN PROGRESS
Started: October 28, 2025
๐ฏ Objectives
Create UnifiedDataService as the single source of truth for all platform data, eliminating data islands and enabling seamless cross-tool communication.
Problems Being Solved
Launch Station Data Disconnect
- Currently shows "No Universes"
- Not using SpaceService or UniverseManagerService
- Isolated data management
Modularity Can't See Data
- Cannot browse pages from Editor
- Cannot browse flows from Singularity
- Must manually track what exists
Dashboard Lacks Insights
- No cross-universe statistics
- Cannot show space-level analytics
- Missing data aggregations
Data Duplication
- Each tool loads its own data
- Multiple sources of truth
- Inconsistency issues
๐ Implementation Checklist
Files to Create
-
libs/core-angular/src/lib/services/unified-data.service.ts -
libs/core-angular/src/lib/types/unified-data.types.ts -
libs/core-angular/src/lib/guards/data-loading.guard.ts
Files to Modify
-
libs/launch-station-lib/src/lib/components/launch-dashboard/launch-dashboard.component.ts -
libs/modularity-lib/src/lib/components/module-builder/module-builder.component.ts -
apps/gograb-space/src/app/g-space-dashboard/g-space-dashboard.component.ts -
libs/core-angular/src/index.ts -
apps/gograb-space/src/app/app.component.ts -
libs/core-models/src/lib/types/world.types.ts -
apps/gograb-space/src/app/app.routes.ts
๐๏ธ Architecture
UnifiedDataService Structure
@Injectable({ providedIn: 'root' })
export class UnifiedDataService {
// Core data signals
private _spaces = signal<Space[]>([]);
private _universes = signal<UniverseWithContent[]>([]);
private _pages = signal<WorldWithMetadata[]>([]);
private _flows = signal<FlowWithMetadata[]>([]);
private _modules = signal<ModuleWithAssets[]>([]);
private _widgets = signal<SpaceWidget[]>([]);
// Read-only accessors
readonly spaces = this._spaces.asReadonly();
readonly universes = this._universes.asReadonly();
readonly pages = this._pages.asReadonly();
readonly flows = this._flows.asReadonly();
readonly modules = this._modules.asReadonly();
readonly widgets = this._widgets.asReadonly();
// Computed cross-references
readonly universesWithContent = computed(() => { /* ... */ });
readonly modulesWithAssets = computed(() => { /* ... */ });
readonly pagesWithFlows = computed(() => { /* ... */ });
// Methods
async loadSpaceData(spaceSlug: string): Promise<void> { /* ... */ }
async updatePage(pageId: string, updates: Partial<World>): Promise<void> { /* ... */ }
async attachFlowToPage(pageId: string, flowId: string): Promise<void> { /* ... */ }
}
Extended Types
// UniverseWithContent - Universe + aggregated counts
interface UniverseWithContent extends Universe {
pageCount: number;
moduleCount: number;
flowCount: number;
galaxyCount: number;
}
// WorldWithMetadata - Page + additional metadata
interface WorldWithMetadata extends World {
universeId: string;
universeName: string;
attachedFlowIds?: string[];
attachedFlowCount: number;
widgetCount: number;
lastModified: Date;
}
// FlowWithMetadata - Flow + usage data
interface FlowWithMetadata extends Flow {
attachedToPages: string[];
usageCount: number;
category: string;
}
// ModuleWithAssets - Module + loaded assets
interface ModuleWithAssets extends ModuleConfig {
pages: World[];
flows: Flow[];
assetCount: number;
deploymentReady: boolean;
}
๐ง Implementation Details
Step 1: Create UnifiedDataService
File: libs/core-angular/src/lib/services/unified-data.service.ts
Key Features:
- Signals for all data types
- Computed signals for derived data
- Loading states
- Error handling
- Data refresh methods
Step 2: Update Launch Station
File: libs/launch-station-lib/src/lib/components/launch-dashboard/launch-dashboard.component.ts
Changes:
// BEFORE (broken)
private readonly universeManager = inject(UniverseManagerService);
readonly universes = []; // Empty!
// AFTER (working)
private readonly unifiedData = inject(UnifiedDataService);
readonly universes = this.unifiedData.universesWithContent;
// Now shows: universe.pageCount, universe.moduleCount, etc.
Step 3: Update Modularity
File: libs/modularity-lib/src/lib/components/module-builder/module-builder.component.ts
Changes:
// BEFORE (can't see pages)
// Had to manually track pages
// AFTER (sees everything)
private readonly unifiedData = inject(UnifiedDataService);
// Tab 1: Page Selection
readonly availablePages = this.unifiedData.pages;
// Tab 2: Flow Selection
readonly availableFlows = this.unifiedData.flows;
// Tab 4: Preview
readonly modulePreview = computed(() => {
return this.unifiedData.modulesWithAssets().find(m => m.id === this.currentModuleId());
});
Step 4: Update Dashboard
File: apps/gograb-space/src/app/g-space-dashboard/g-space-dashboard.component.ts
Changes:
// BEFORE (no stats)
// Only showed universe list
// AFTER (full analytics)
private readonly unifiedData = inject(UnifiedDataService);
readonly totalPages = computed(() => this.unifiedData.pages().length);
readonly totalFlows = computed(() => this.unifiedData.flows().length);
readonly totalModules = computed(() => this.unifiedData.modules().length);
readonly totalWidgets = computed(() => this.unifiedData.widgets().length);
readonly universesWithMostPages = computed(() => {
return this.unifiedData.universesWithContent()
.sort((a, b) => b.pageCount - a.pageCount)
.slice(0, 5);
});
Step 5: Initialize on App Startup
File: apps/gograb-space/src/app/app.component.ts
Changes:
export class AppComponent implements OnInit {
private readonly unifiedData = inject(UnifiedDataService);
private readonly auth = inject(AuthService);
async ngOnInit() {
// Wait for auth
const user = this.auth.user();
if (!user) return;
// Load all space data
await this.unifiedData.loadSpaceData('default-space');
console.log('โ
UnifiedData loaded:', {
universes: this.unifiedData.universes().length,
pages: this.unifiedData.pages().length,
flows: this.unifiedData.flows().length,
modules: this.unifiedData.modules().length
});
}
}
โ Acceptance Criteria
Data Loading
- UnifiedDataService loads all data on app init
- Loading states are handled properly
- Errors are caught and logged
Launch Station
- Shows all universes
- Displays accurate page counts
- Displays accurate module counts
- Displays accurate flow counts
- "No Universes" message gone
Modularity
- Tab 1 shows all pages with search/filter
- Tab 2 shows all flows with search/filter
- Can select pages for module
- Can select flows for module
- Preview shows correct data
Dashboard
- Shows total pages across all universes
- Shows total flows
- Shows total modules
- Shows total widgets
- Shows universes with most content
- Analytics update in real-time
Performance
- Data loads in < 500ms
- Computed signals are efficient
- No unnecessary re-renders
- Memory usage is reasonable
๐งช Testing Plan
Unit Tests
describe('UnifiedDataService', () => {
it('should load space data', async () => {
const service = TestBed.inject(UnifiedDataService);
await service.loadSpaceData('test-space');
expect(service.spaces().length).toBeGreaterThan(0);
});
it('should compute universesWithContent', () => {
const service = TestBed.inject(UnifiedDataService);
const universes = service.universesWithContent();
expect(universes[0].pageCount).toBeDefined();
});
});
Integration Tests
- Load app โ verify data loads
- Navigate to Launch Station โ verify universes display
- Navigate to Modularity โ verify pages/flows display
- Navigate to Dashboard โ verify stats display
Manual Testing
- Open app at http://localhost:5200/
- Check console for "โ UnifiedData loaded"
- Navigate to /launch-station
- Verify universes are displayed with counts
- Navigate to /modularity
- Verify Tab 1 shows pages
- Verify Tab 2 shows flows
๐ Progress Tracking
Created Files
- unified-data.service.ts (0% - Not started)
- unified-data.types.ts (0% - Not started)
- data-loading.guard.ts (0% - Not started)
Modified Files
- launch-dashboard.component.ts (0% - Not started)
- module-builder.component.ts (0% - Not started)
- g-space-dashboard.component.ts (0% - Not started)
- core-angular/index.ts (0% - Not started)
- app.component.ts (0% - Not started)
- world.types.ts (0% - Not started)
- app.routes.ts (0% - Not started)
Overall Progress: 0%
๐ Known Issues & Blockers
None yet - just starting implementation
๐ Notes & Learnings
To be filled as implementation progresses
Last Updated: October 28, 2025 - Starting implementation
Next Checkpoint: UnifiedDataService implementation complete