Module Creation Guide
Comprehensive guide to creating modules using visual builder or code-first approach
๐ ๏ธ Module Creation Guide
GoGrab Space offers TWO powerful ways to create production-ready modules: Visual Builder and Code-First.
๐ฏ Choose Your Approach
๐จ Visual Builder (NEW - Recommended)
Use the Module Workbench - A groundbreaking visual tool that guides you through module creation step-by-step and generates production-ready code.
Best For:
- Rapid prototyping
- Complex modules with CRUD operations
- Visual thinkers
- AI-assisted development
- Teams without deep Angular expertise
Access: Navigate to /modularity in GoGrab Space
๐ป Code-First (Traditional)
Write the code directly - Full control over implementation details.
Best For:
- Advanced developers
- Custom complex logic
- Performance-critical modules
- Integration with existing codebases
๐จ Method 1: Visual Builder (Modularity-Lib)
Overview
The Module Workbench is a revolutionary visual tool that enables creating complex, production-ready modules through a guided, step-by-step interface.
Location: libs/libs-structural/modularity-lib
Route: /space/:spaceSlug/modularity
Features
- โ 6-Step Guided Process: Models โ State โ Actions โ UI โ Test โ Deploy
- โ AI-Powered Generation: Describe your module, AI fills the details
- โ Entity-First Design: Define data models, auto-generate CRUD actions
- โ Visual Action Flow Builder: Build action logic with drag-and-drop blocks
- โ Embeddable Component Designer: Create widgets with input/output bindings
- โ Code Export: Generates production-ready TypeScript/Angular code
- โ Flash Persistence: Auto-saves your work with real-time sync
The 6-Step Workflow
Step 1: Models
Define your data entities (e.g., User, Product, Order).
- Field definitions with types and validation
- Relationships between entities
- Auto-generates TypeScript interfaces
Step 2: State
Configure state management for your entities.
- Choose persistence strategy (local, backend, hybrid)
- Define sync modes (realtime, on-demand, manual)
- Auto-generates state service with flash persistence
Step 3: Actions
Create module operations (CRUD, queries, mutations).
- Auto-Generated: CRUD actions from entities (Create, Read, Update, Delete)
- Custom Actions: Use the Action Flow Builder to define logic
- Action Flow Builder: Visual canvas for building action implementations
- Entity-aware block palette
- Smart suggestions based on your data model
- Drag-and-drop interface
- Real-time validation
Step 4: UI
Design embeddable components (widgets).
- Auto-Generated: List and card widgets from entities
- Custom Embeddables: Define inputs/outputs
- Data Bindings: Connect widgets to module state
- Output Actions: Link widget events to module actions
Step 5: Test
Test your module with mock data.
- Auto-generated test cases
- Mock data generator
- Interactive testing environment
Step 6: Deploy
Export production-ready code.
- TypeScript Export: Complete Angular module code
- Library Structure: Full Nx library with project.json, tsconfig, etc.
- BrowserModuleContract: Proper lifecycle implementation
- @PublicAction: Decorated action classes
- Embeddable Components: Standalone Angular components
Code Generation
The Module Export Service generates:
my-module/
โโโ index.ts # Barrel exports
โโโ my-module.module.ts # BrowserModuleContract implementation
โโโ my-module.manifest.ts # ModuleManifest
โโโ models/
โ โโโ my-entity.model.ts # TypeScript interfaces
โโโ actions/
โ โโโ my-action.action.ts # @PublicAction classes
โโโ components/
โโโ my-widget/
โโโ my-widget.component.ts
โโโ my-widget.component.html
โโโ my-widget.component.scss
Example: Creating a Product Catalog Module
- Navigate to
/modularity/workbench - Define Entity: Product (name, price, description, category)
- Auto-Generate: 5 CRUD actions (getProducts, getProductById, createProduct, updateProduct, deleteProduct)
- Customize Actions: Open Action Flow Builder for
createProduct, add validation logic - Add Widgets: Product List Widget, Product Card Widget
- Export Code: Click "Export as TypeScript" - Production-ready code generated!
๐ป Method 2: Code-First (Traditional)
๐๏ธ Module Architecture
Monorepo Structure
libs/
โโโ core/ # Core system libraries (@libs-core/*)
โโโ modules/ # Reusable business modules (@modules/*)
โโโ user-management/ # @modules/user-management
โโโ product-catalog/ # @modules/product-catalog
โโโ analytics/ # @modules/analytics
Key Benefits
- โ Total Reusability: Import into any app
- โ Independent Development: Isolated lifecycles
- โ Lazy Loading: Optimized by Nx
- โ Independent Versioning: Evolve separately
- โ Isolated Testing: Unit tests per module
- โ Tree-shaking: Efficient builds
๐ Code-First: Step-by-Step Creation
When you choose the code-first approach, follow these steps:
1. Generate Nx Library
Use the Nx generator to create a new standalone library.
# Generate new module library
npx nx g @nx/angular:lib <module-name> --directory=libs/modules --standalone --changeDetection=OnPush
# Example: User Management
npx nx g @nx/angular:lib user-management --directory=libs/modules --standalone --changeDetection=OnPush
2. Directory Structure
Organize your module following this standard structure:
libs/modules/user-management/
โโโ src/
โ โโโ lib/
โ โ โโโ components/ # UI Components
โ โ โโโ services/ # Module-specific Services
โ โ โโโ models/ # Interfaces & Types
โ โ โโโ actions/ # Public Actions
โ โ โโโ mock/ # Mock Data
โ โ โโโ user-management.module.ts # Main Module Class
โ โโโ index.ts # Barrel exports
โ โโโ test-setup.ts
โโโ project.json
โโโ tsconfig.json
โโโ README.md
3. Implement Module Contract
The heart of your module is the class implementing BrowserModuleContract.
File: libs/modules/user-management/src/lib/user-management.module.ts
import { Injectable, inject } from '@angular/core';
import { Observable, of, delay } from 'rxjs';
import { BrowserModuleContract, ModuleManifest, ModuleDecorator } from '@libs-core/module-base';
import { LoggerService } from '@libs-core/log-system';
import { EventBusService } from '@libs-core/event-system';
@ModuleDecorator({
id: 'user-management',
version: '1.0.0',
name: 'User Management',
description: 'User management and RBAC system',
permissions: ['users:read', 'users:write']
})
@Injectable({ providedIn: 'root' })
export class UserManagementModule implements BrowserModuleContract {
private readonly logger = inject(LoggerService);
private readonly eventBus = inject(EventBusService);
// Manifest must match decorator
readonly manifest: ModuleManifest = {
id: 'user-management',
version: '1.0.0',
name: 'User Management',
description: 'User management and RBAC system',
permissions: ['users:read', 'users:write'],
// Define your public actions here
publicActions: [],
// Define your embeddable widgets here
embeddableComponents: []
};
configure<T = Record<string, unknown>>(config: T): Observable<void> {
this.logger.info('UserManagementModule', 'Configuring...', config);
return of(void 0);
}
initialize(): Observable<void> {
this.logger.info('UserManagementModule', 'Initializing...');
// Setup services, connections, etc.
return of(void 0);
}
start(): Observable<void> {
this.logger.info('UserManagementModule', 'Starting...');
this.eventBus.emit({ type: 'MODULE_STARTED', payload: { moduleId: this.manifest.id } });
return of(void 0);
}
stop(): Observable<void> {
this.logger.info('UserManagementModule', 'Stopping...');
return of(void 0);
}
destroy(): Observable<void> {
this.logger.info('UserManagementModule', 'Destroying...');
return of(void 0);
}
}
4. Define Models (Shared Models First)
Always prioritize using shared models before creating local types.
File: libs/modules/user-management/src/lib/models/user.types.ts
import { UserProfile, BaseEntity, SystemRoles } from '@libs-core/shared-models';
// โ
Extend base types (Tenant ID is handled by context, omit if redundant)
export interface User extends Omit<UserProfile, 'tenantId'> {
status: UserStatus;
primaryRole: SystemRoles;
}
export type UserStatus = 'active' | 'inactive';
5. Create Context-Aware Services
Services should use LoggerService and EventBusService which are already tenant-aware.
File: libs/modules/user-management/src/lib/services/user.service.ts
@Injectable({ providedIn: 'root' })
export class UserService {
private readonly http = inject(HttpClient);
private readonly logger = inject(LoggerService);
getUsers(): Observable<User[]> {
// Logger automatically includes context
this.logger.debug('UserService', 'Fetching users...');
return this.http.get<User[]>('/api/users');
}
}
6. Main Component
Create a standalone component as the entry point for your module's UI (if applicable).
File: libs/modules/user-management/src/lib/components/user-management.component.ts
@Component({
selector: 'lib-user-management',
standalone: true,
imports: [CommonModule],
template: `
<div class="module-container">
<h2>User Management</h2>
<!-- Module content -->
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserManagementComponent {}
7. Export the Module
Expose your module class and public assets.
File: libs/modules/user-management/src/index.ts
export * from './lib/user-management.module';
export * from './lib/components/user-management.component';
export * from './lib/models';
// Do NOT export internal services
๐ Hybrid Approach: Visual Builder + Code Refinement
The Power Workflow: Use both methods together!
- Start Visual: Use Module Workbench to scaffold the module structure
- Export Code: Generate production-ready TypeScript code
- Refine Manually: Add complex business logic, custom validation
- Integrate: Import into your application as an Nx library
When to Use Hybrid
- โ Complex Modules: Start visual for structure, refine code for complexity
- โ Learning: Visual builder teaches correct patterns
- โ Speed: 80% done visually, 20% manual refinement
- โ Consistency: Generated code matches production standards
๐งช Testing
Every module must include tests.
describe('UserManagementModule', () => {
let module: UserManagementModule;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
UserManagementModule,
{ provide: LoggerService, useValue: mockLogger },
{ provide: EventBusService, useValue: mockEventBus }
]
});
module = TestBed.inject(UserManagementModule);
});
it('should implement lifecycle methods', () => {
expect(module.configure).toBeDefined();
expect(module.initialize).toBeDefined();
// ...
});
});
โ Best Practices
General
- Independence: Modules must be self-contained.
- Contracts: Always implement
BrowserModuleContract. - Logging: Use
LoggerServiceexclusively. - Events: Emit lifecycle events.
- OnPush: Use
ChangeDetectionStrategy.OnPush.
Multi-Tenancy
- Context: Rely on application context for tenant ID.
- Shared Models: Use
@libs-core/shared-models. - Clean Events: Avoid redundant tenant IDs in payloads.
Mocking
- Requirement: Create realistic mock data for development.
- Volume: Use 50+ records for realistic testing.
- Types: Use shared model types for mocks.
๐ฏ Quick Decision Matrix
| Requirement | Visual Builder | Code-First | Hybrid |
|---|---|---|---|
| Speed | โกโกโก Very Fast | โก Slower | โกโก Fast |
| Control | ๐ง๐ง Good | ๐ง๐ง๐ง Full | ๐ง๐ง๐ง Full |
| Learning Curve | ๐ Gentle | ๐๐๐ Steep | ๐๐ Moderate |
| AI Assistance | โ Built-in | โ Manual | โ Partial |
| CRUD Modules | โ Perfect | โ ๏ธ Tedious | โ Excellent |
| Complex Logic | โ ๏ธ Limited | โ Perfect | โ Perfect |
| Code Quality | โ Consistent | โ ๏ธ Varies | โ Best of Both |
Recommendation: Start with Visual Builder for 95% of modules. Use Code-First only for highly specialized cases. Use Hybrid for complex modules.
๐ Related Documentation
- Modularity System - The visual builder
- Module System Architecture - System design
- Action System - Actions and commands
- Widget System - Embeddables
- User Management Reference - Complete example
Last Updated: December 3, 2025 - 9:45 PM EST Session ID: 3 Status: โ Reflects Hybrid Visual + Code-First Approach