Widget vs Module Taxonomy - Decision Matrix
Comprehensive guidelines for classifying components as widgets or modules
π·οΈ Widget vs Module Taxonomy - Decision Matrix
Last Updated: November 14, 2025
Status: π― ACTIVE GUIDELINES
Purpose: Clear, actionable criteria for component classification
π― CORE PRINCIPLE
Widget (Pure UI + I/O Contract) + Functionality (Flows/Logic/State) = Module (Reusable System)
Philosophy: Start simple (widget), promote when complexity requires it (module).
π DECISION MATRIX
Quick Reference Table
| Component | Start As | Promote When | Example Progression |
|---|---|---|---|
| Input Field | Widget | + Validation flow | gg-input β validated-input-module |
| Select Dropdown | Widget | + Data source API | gg-select β data-select-module |
| Button | Widget | + Multi-step flow | gg-button β action-button-module |
| Card | Widget | + Contains modules | gg-card β module-card-container |
| Badge | Widget | Never (pure UI) | gg-badge (stays widget) |
| Icon | Widget | Never (pure UI) | gg-icon (stays widget) |
| Modal | Module | Always | modal-module (immediate) |
| Sidebar | Module | Always | sidebar-nav-module (immediate) |
| Navigation | Module | Always | top-nav-module (immediate) |
| Data Grid | Module | Always | data-grid-module (immediate) |
| Form (simple) | Widget | + Validation logic | gg-form β smart-form-module |
| Form (multi-step) | Module | Always | wizard-form-module (immediate) |
| Chart | Widget | + Live data source | gg-chart β live-chart-module |
| Calendar | Widget | + Event management | gg-calendar β event-calendar-module |
π CLASSIFICATION CRITERIA
Widget Definition
A widget is:
- β Pure presentational component
- β Has @Input/@Output for data binding
- β No complex internal state
- β No routing or navigation
- β No direct API calls
- β No lifecycle business logic
- β Reusable across multiple contexts
- β Can be embedded anywhere
Widget Characteristics:
@Component({
selector: 'gg-input',
standalone: true,
template: '<input [value]="value" (input)="valueChange.emit($event.target.value)" />',
inputs: ['value', 'placeholder', 'disabled', 'type'],
outputs: ['valueChange', 'blur', 'focus']
})
export class GgInputWidget {
// Pure UI logic only
// No business logic
// No API calls
// No complex state
}
Module Definition
A module is:
- β Self-contained functional unit
- β Has manifest describing capabilities
- β May have complex internal state
- β May have routing
- β May make API calls
- β May contain other modules
- β Has lifecycle management
- β Registered with ModuleRegistry
Module Characteristics:
@Component({
selector: 'lib-validated-input',
standalone: true,
template: `
<gg-input [value]="value()" (valueChange)="onChange($event)"></gg-input>
<div *ngIf="error()">{{ error() }}</div>
`
})
export class ValidatedInputModule implements BrowserModuleContract {
readonly manifest: ModuleManifest = {
id: 'validated-input',
name: 'Validated Input',
type: 'feature',
actions: ['validate', 'reset', 'getValue'],
flows: ['validation-flow']
};
// Business logic
async validate(): Promise<boolean> { /* ... */ }
// Flow integration
private attachedFlows: string[] = [];
// Complex state
private validationState = signal<ValidationState>({ /* ... */ });
}
π¦ PROMOTION TRIGGERS
Automatic Promotion (Any of these β Module)
Singularity Integration β‘
// Widget with attached flow = Module if (component.hasAttachedFlows) { classify = 'module'; }Routing πΊοΈ
// Component with routes = Module if (component.hasRoutes) { classify = 'module'; }Child Modules π§©
// Component containing modules = Module if (component.containsModules) { classify = 'module'; }Backend Integration π
// Component making API calls = Module if (component.hasApiCalls) { classify = 'module'; }Complex State πΎ
// Component with persistence/sync = Module if (component.usesStorage || component.hasFlashState) { classify = 'module'; }Lifecycle Logic π
// Component with business logic in lifecycle = Module if (component.hasBusinessLogicInLifecycle) { classify = 'module'; }
Manual Promotion (Architectural Decision)
- Strategic Importance - Core platform capability
- Reusability Across Universes - Used in multiple projects
- Marketplace Potential - Could be sold/shared
- Team Collaboration - Multiple developers need to extend
π STORAGE STRUCTURE
Widget Storage
Location: libs/libs-ui/ or libs/widgets/
libs/
βββ libs-ui/
β βββ gg-input/
β βββ gg-button/
β βββ gg-select/
β βββ gg-card/
β βββ gg-badge/
β
βββ widgets/
βββ chart-widget/
βββ calendar-widget/
βββ table-widget/
Characteristics:
- Simple folder structure
- No manifest file
- Export from index.ts
- Used via direct import
Module Storage
Location: libs/libs-structural/ or libs/libs-module-features/
libs/
βββ libs-structural/
β βββ sidebar-nav-lib/
β β βββ src/
β β βββ sidebar-nav.manifest.ts β Has manifest
β β
β βββ modal-system-lib/
β β βββ src/
β β βββ modal.manifest.ts
β β
β βββ data-grid-lib/
β βββ src/
β βββ data-grid.manifest.ts
β
βββ libs-module-features/
βββ validated-input-lib/
β βββ src/
β βββ validated-input.manifest.ts
β
βββ smart-form-lib/
βββ src/
βββ smart-form.manifest.ts
Characteristics:
- NX library with manifest
- Registered with ModuleRegistry
- Appears in module-browser
- Can be assigned to sections
π PROMOTION WORKFLOW
Step 1: Identify Promotion Need
// Widget starts simple
@Component({
selector: 'gg-input'
})
export class GgInputWidget { }
// Developer adds validation logic
this.validationService.validate(value);
// π¨ PROMOTION TRIGGER: Business logic added!
Step 2: Create Module Version
# Create new NX library
nx generate @nx/angular:library validated-input-lib \
--directory=libs/libs-module-features \
--standalone
# Move widget code
# Add manifest
# Add business logic
# Register with ModuleRegistry
Step 3: Implement Module Contract
@Component({
selector: 'lib-validated-input',
standalone: true
})
export class ValidatedInputModule implements BrowserModuleContract {
// Required: Module ID
readonly moduleId = 'validated-input';
// Required: Manifest
readonly manifest: ModuleManifest = {
id: 'validated-input',
name: 'Validated Input Field',
type: 'feature',
category: 'Form Components',
version: '1.0.0',
author: 'GoGrab Team',
// What this module can do
actions: [
'validate',
'getValue',
'setValue',
'reset'
],
// Attached flows
flows: ['validation-flow'],
// Access control
access: {
visibility: 'public',
audience: 'space-builder',
showInBrowser: true,
showInSelector: true
}
};
// Lifecycle methods
async initialize(): Promise<void> { }
async start(): Promise<void> { }
async stop(): Promise<void> { }
async destroy(): Promise<void> { }
// Public actions
async validate(): Promise<boolean> { }
async getValue(): Promise<any> { }
async setValue(value: any): Promise<void> { }
async reset(): Promise<void> { }
}
Step 4: Register Module
// In manifest file or module decorator
@ModuleDecorator({
id: 'validated-input',
name: 'Validated Input',
type: 'feature'
})
export class ValidatedInputModule { }
// Auto-registers with ModuleRegistry on app init
Step 5: Deprecate Widget (Optional)
// Mark widget as deprecated
@Component({
selector: 'gg-input',
/**
* @deprecated Use ValidatedInputModule instead
* @see libs/libs-module-features/validated-input-lib
*/
})
export class GgInputWidget { }
π CLASSIFICATION EXAMPLES
Example 1: Input Field Progression
Stage 1: Simple Widget
@Component({ selector: 'gg-input' })
export class GgInputWidget {
@Input() value: string = '';
@Input() placeholder: string = '';
@Output() valueChange = new EventEmitter<string>();
}
// Classification: WIDGET β
Stage 2: With Validation (Promotion Trigger)
export class GgInputWidget {
@Input() value: string = '';
@Input() validators: ValidatorFn[] = [];
validate() {
// Business logic added!
return this.validators.every(v => v(this.value));
}
}
// Classification: Should be MODULE β οΈ
Stage 3: As Module
@Component({ selector: 'lib-validated-input' })
export class ValidatedInputModule implements BrowserModuleContract {
manifest: ModuleManifest = { /* ... */ };
async validate(): Promise<boolean> {
// Use singularity flow
return await this.flowExecutor.execute('validation-flow', { value });
}
}
// Classification: MODULE β
Example 2: Data Grid
Always a Module (Complex state + API + Pagination)
@Component({ selector: 'lib-data-grid' })
export class DataGridModule implements BrowserModuleContract {
manifest: ModuleManifest = {
id: 'data-grid',
type: 'feature',
actions: ['load', 'sort', 'filter', 'export']
};
// API integration
async loadData(endpoint: string) { }
// State management
private dataState = signal<GridState>({ /* ... */ });
// Pagination logic
async changePage(page: number) { }
}
// Classification: MODULE β
(Always)
Example 3: Modal System
Always a Module (Lifecycle + Overlay + State)
@Component({ selector: 'lib-modal' })
export class ModalModule implements BrowserModuleContract {
manifest: ModuleManifest = {
id: 'modal-system',
type: 'layout',
actions: ['open', 'close', 'confirm']
};
// Lifecycle management
async open() { }
async close() { }
// Complex interactions
async confirm(): Promise<boolean> { }
}
// Classification: MODULE β
(Always)
π¨ UI LIBRARY AUDIT
Current libs/libs-ui/ Inventory
Pure Widgets (Keep as Widgets):
β
gg-button - Pure UI
β
gg-input - Pure UI
β
gg-select - Pure UI
β
gg-badge - Pure UI
β
gg-chip - Pure UI
β
gg-icon - Pure UI
β
gg-avatar - Pure UI
β
gg-spinner - Pure UI
β
gg-tooltip - Pure UI
β
gg-divider - Pure UI
Candidates for Promotion:
β οΈ gg-form β smart-form-module (if validation added)
β οΈ gg-table β data-grid-module (if pagination/sort added)
β οΈ gg-chart β live-chart-module (if live data added)
β οΈ gg-calendar β event-calendar-module (if events added)
β οΈ gg-dropdown-menu β menu-module (if navigation added)
β οΈ gg-tab-group β tab-module (if routing added)
Already Modules (Move to libs-structural):
π gg-modal β modal-system-lib
π gg-sidebar β sidebar-nav-lib (already done β
)
π gg-navigation β navigation-module-lib
π gg-panel β panel-module-lib
π IMPLEMENTATION STRATEGY
Phase 1: Classification (Week 1)
- Audit all components in libs/libs-ui/
- Classify each as widget/module/candidate
- Document promotion triggers hit
- Create promotion backlog
Phase 2: Promote Critical Components (Week 2)
- Modal β modal-system-lib
- Navigation β navigation-module-lib
- Data Grid β data-grid-lib
- Panel β panel-module-lib
Phase 3: Create Widget-Builder (Week 3)
- Tool to create simple widgets
- Tool to promote widgets to modules
- Automatic manifest generation
- ModuleRegistry integration
Phase 4: Documentation (Week 4)
- Widget creation guide
- Module promotion guide
- Decision matrix poster
- Training materials
π SUCCESS METRICS
Classification Clarity
- 100% of components classified
- Zero ambiguity in decision matrix
- Clear promotion triggers documented
- Team aligned on philosophy
Technical Implementation
- All modules registered with ModuleRegistry
- All modules appear in module-browser
- Widget-builder operational
- Promotion workflow automated
Developer Experience
- Widget creation: < 5 minutes
- Module promotion: < 30 minutes
- Clear guidelines: No confusion
- Automated tooling: Minimal manual work
π RELATED DOCUMENTS
- Decisions:
DECISIONS-REFRESHED-NOV-2025.md - Foundation Review:
FOUNDATION-REVIEW-NOV-2025.md - Module System:
BREAKTHROUGH-MODULES-ARE-STRUCTURE.md - Master Plan:
ULTIMATE-PLATFORM-BUILDER-MASTER-PLAN.md
Status: β
GUIDELINES COMPLETE
Next Action: Begin component classification audit
Owner: Development Team + UX Team
Last Updated: November 14, 2025