2026 03 12 User Management Module Modernization Design

Documentation for Plans

User Management Module Modernization — Design Spec

Session: 210 Date: 2026-03-12 Status: Draft Approach: Rebuild from gmodules1 base into gspace1


1. Objective

Modernize the modules/core-modules/user-management module to:

  1. Use real backend-api endpoints instead of mock data
  2. Replace NGXS state management with autoFlashState from @storage-system
  3. Incorporate the corrected L0-L4 user tier hierarchy (Session 209)
  4. Support both SystemRoles (immutable, platform-wide) and custom space-level roles
  5. Remain portable across gspace1 AND dynamically generated platforms

2. Architecture

2.1 Data Access: Dual-Layer Pattern

Components (UI)
    ↓ inject
Repository (domain queries, caching, events)
    ↓ delegates
API Service (HTTP, configurable base URL)
    ↓ HTTP
Backend API (port 3000 or platform backend)

Why dual-layer: The module must work in both gspace1 (where backend-api is at API_BASE_URL) and generated platforms (where the backend URL varies). The Repository abstracts this so components never know the URL.

2.2 State Management: autoFlashState

Replace NGXS @State + actions with autoFlashState signals:

// UI state — persisted locally, cross-tab synced
const userListState = autoFlashState<UserListState>('user-mgmt-list', {
  filters: {},
  pagination: { page: 1, pageSize: 20 },
  viewMode: 'table',
  bulkSelection: [],
}, {
  database: 'user-management',
  collection: 'ui-state',
  priority: 'normal',
  ttl: 7 * 24 * 60 * 60 * 1000, // 7 days
  debounceMs: 300,
});

Data (users, roles) is NOT stored in autoFlashState — it comes from the backend via API Service + Repository. Only UI preferences (filters, view mode, pagination, selection) are persisted locally.

Transient state (loading, error, selectedUser, total) lives as plain signal() values in the component or a lightweight service — NOT persisted. These reset on page load, which is correct behavior.

2.3 Tier Hierarchy Integration

There are TWO orthogonal classification systems. Both live on the User object:

System A: role: UserRole — Controls access level (what you CAN DO)

UserRole Level Used For
system 100 Backend automation only
executive 90 Governance (Vitto core values, directives, persona defaults)
developer 80 Full technical access (engine settings, debug, tools)
admin 60 Platform operations (user CRUD, space management)
manager 40 Space-level management (modules, workflows)
editor 20 Content management within a space
viewer 10 Read-only access
guest 0 Unauthenticated / temp

System B: userType: RegistrationUserType — Describes WHO you are (your tier)

Tier userType Default Role Description
L0 (assigned internally) executive Founders/C-suite
L1 (assigned internally) developer or admin GoGrab engineering & ops
L2 space_builder manager Creates & manages spaces
L2 space_admin manager CEO/executive of a space
L3 space_user editor Team member within a space
L4 platform_user viewer End customer of generated platform

Key: role determines access control enforcement (what the RolesGuard checks). userType determines the user's category in the platform hierarchy. A space_builder (L2) gets manager role by default but could be promoted to admin by an executive.

Rule: A user can only manage users with a LOWER role level than their own. Enforced at both API and UI level via the hierarchy-aware RolesGuard.


3. Module Structure (Target)

modules/core-modules/user-management/src/lib/
├── models/
│   ├── user.model.ts              # User, UserStatus, USER_PERMISSIONS
│   ├── user-api.model.ts          # Request/response types (CRUD, bulk, audit)
│   ├── user-filters.model.ts      # UserFilters, UserPagination
│   ├── role.model.ts              # Role, SystemRoles, ROLE_PERMISSIONS
│   ├── role-api.model.ts          # CreateRoleRequest, UpdateRoleRequest
│   └── index.ts
├── services/
│   ├── user-api.service.ts        # HTTP calls to /api/users (extends BaseCrudApiService)
│   ├── role-api.service.ts        # HTTP calls to /api/roles
│   └── index.ts
├── data/
│   └── repositories/
│       ├── user.repository.ts     # Domain queries, caching, event publishing
│       ├── role.repository.ts     # Role domain queries
│       └── index.ts
├── state/
│   ├── user-list.state.ts         # autoFlashState for user list UI
│   └── index.ts
├── components/
│   ├── user-list/                 # Main list (table/grid/cards, pagination, bulk)
│   ├── user-form/                 # Create/edit form (reactive forms, role selector)
│   ├── user-detail/               # Profile view + workflow state machine
│   ├── user-role-manager/         # Role assignment with hierarchy enforcement
│   ├── multi-role-lookup/         # Multi-select role dropdown
│   ├── user-card/                 # Card display
│   ├── user-avatar/               # Avatar with initials fallback
│   ├── user-filters/              # Filter panel (search, role, status, date)
│   ├── user-delete-confirm/       # Delete confirmation dialog
│   ├── role-list/                 # System + custom roles list
│   ├── role-form/                 # Create/edit role with permissions
│   ├── role-detail/               # Role details view
│   └── index.ts
├── actions/
│   ├── create-user.action.ts      # Public action (cross-module)
│   ├── update-user.action.ts
│   ├── delete-user.action.ts
│   ├── navigate-to-user.action.ts
│   ├── bulk-update-roles.action.ts
│   └── index.ts
├── events/
│   ├── user.events.ts             # UserCreated, UserUpdated, UserDeleted, etc.
│   ├── role.events.ts             # RoleAssigned, RoleRemoved
│   └── index.ts
├── workflow/
│   ├── user-workflow.definition.ts # Status state machine (pending→active→suspended→inactive)
│   └── index.ts
├── embeddables/
│   ├── user-list-widget/          # Compact embeddable widget
│   └── index.ts
├── mocks/
│   ├── user-mock-api.ts           # Mock API (dev/test only)
│   ├── user-mock-data.ts          # Generated test data
│   └── index.ts
├── user-management.module.ts      # Module manifest, lifecycle, navigation
├── user-management.routes.ts      # User routes with guards
├── role-management.routes.ts      # Role routes with guards
└── index.ts                       # Public API

13 components + 1 embeddable widget.


4. Backend API Endpoints

4.1 Existing Endpoints (backend-api:3000)

These already exist and the module will wire to them:

Method Path Purpose
POST /api/auth/register Self-register (L2+ only: space_builder, space_admin, space_user)
POST /api/auth/login Login
GET /api/auth/profile Current user profile
GET /api/admin/users List all users (admin)
POST /api/spaces/:spaceId/universes/:universeId/users Create platform user (L4)
GET /api/spaces/:spaceId/universes/:universeId/users List platform users
PUT /api/spaces/:spaceId/universes/:universeId/users/:id Update platform user
DELETE /api/spaces/:spaceId/universes/:universeId/users/:id Delete platform user

4.2 Endpoints to Add (backend-api enhancements)

Controller: New AdminUsersController at /api/admin/users to avoid route conflicts with the existing UsersController (which handles /api/users/me/*, /api/users/resolve-channel, etc.). All endpoints use @UseGuards(JwtAuthGuard, RolesGuard) at class level.

Method Path Purpose Decorator
GET /api/admin/users List users with pagination/filters @Roles(SystemRoles.ADMIN)
GET /api/admin/users/:id Get user by ID @Roles(SystemRoles.ADMIN)
PUT /api/admin/users/:id Update user (name, role, status) @Roles(SystemRoles.ADMIN)
DELETE /api/admin/users/:id Soft-delete user @Roles(SystemRoles.ADMIN)
PUT /api/admin/users/:id/status Change user status @Roles(SystemRoles.ADMIN)
PUT /api/admin/users/:id/roles Assign/change role @Roles(SystemRoles.ADMIN) + service-level check: assigning L0-L1 roles requires executive
POST /api/admin/users/bulk Bulk operations @Roles(SystemRoles.ADMIN)
GET /api/admin/roles List roles (system + custom) @Roles(SystemRoles.ADMIN)
POST /api/admin/roles Create custom role @Roles(SystemRoles.ADMIN)
PUT /api/admin/roles/:id Update custom role @Roles(SystemRoles.ADMIN)
DELETE /api/admin/roles/:id Delete custom role (not system) @Roles(SystemRoles.ADMIN)

Note: The existing GET /api/admin/users in AdminStatsController already supports limit, skip, search. We extend it or replace it with the new AdminUsersController that adds pagination, filters, sorting, and full CRUD.

4.3 Backend Schema Enhancements

The existing User schema needs:

New schema needed:

4.4 Pagination Strategy

All list endpoints use offset pagination (matching existing PlatformUsersController pattern):


5. Component Design

5.1 All Components

All components follow:

5.2 Key Component Details

UserListComponent:

UserFormComponent:

UserDetailComponent:

UserRoleManagerComponent:

RoleListComponent / RoleFormComponent / RoleDetailComponent:

5.3 Workflow State Machine

[pending] ──approve──→ [active] ──suspend──→ [suspended]
                           │                      │
                       deactivate            reactivate
                           ↓                      ↓
                       [inactive] ←──reactivate──+

Transitions require users:status permission. Confirmation dialogs for destructive transitions (suspend, deactivate).


6. Route Configuration

User Routes (/users)

Path Component Guards Permissions
/list UserListComponent authGuard, permissionGuard users:read
/create UserFormComponent authGuard, permissionGuard users:create
/:id UserDetailComponent authGuard, permissionGuard users:read
/:id/edit UserFormComponent authGuard, permissionGuard users:update
/admin/manage UserListComponent authGuard, roleGuard(ADMIN) users:manage

Role Routes (/users/roles)

Path Component Guards Permissions
/list RoleListComponent authGuard, permissionGuard roles:read
/create RoleFormComponent authGuard, permissionGuard roles:create
/:id RoleDetailComponent authGuard, permissionGuard roles:read
/:id/edit RoleFormComponent authGuard, permissionGuard roles:update

7. Domain Events

Event Payload Priority When
UserCreatedEvent user, createdBy, tenantId normal After successful creation
UserUpdatedEvent user, previousData, updatedBy, tenantId normal After update
UserDeletedEvent userId, userEmail, deletedBy, tenantId high After deletion
UserStatusChangedEvent user, previousStatus, newStatus, changedBy normal Status transition
UserBulkOperationEvent operation, userIds[], counts, executedBy normal After bulk op
RoleAssignedEvent roleId, userId, tenantId, assignedBy normal Role assignment
RoleRemovedEvent roleId, userId, tenantId, removedBy normal Role removal

Events extend DomainEvent<T> from the module system's event bus.


8. Public Actions (Cross-Module API)

Action ID Permissions Input Output
users:create-user users:create email, firstName, lastName, roleId, tenantId success, userId
users:update-user users:update userId, partial fields success, updatedFields[]
users:delete-user users:delete userId, hardDelete?, reason? success, deletionType
users:navigate-to-user users:read userId, mode? success, targetRoute
users:bulk-update-roles users:bulk assignments[], continueOnError? total, successful, failed

9. Module Manifest

@ModuleDecorator({
  id: 'user-management',
  version: '2.0.0',
  basePath: 'users',
  repositories: [UserRepository, RoleRepository],
  permissions: [
    'users:read', 'users:create', 'users:update', 'users:delete',
    'users:status', 'users:role', 'users:bulk', 'users:manage',
    'roles:read', 'roles:create', 'roles:update', 'roles:delete', 'roles:manage',
  ],
  navigation: {
    menuItems: [
      { label: 'Users', icon: 'people', route: 'list', permission: 'users:read',
        children: [
          { label: 'All Users', icon: 'list', route: 'list' },
          { label: 'Create User', icon: 'person_add', route: 'create' },
          { label: 'Admin Management', icon: 'shield', route: 'admin/manage' },
        ]
      },
      { label: 'Roles', icon: 'admin_panel_settings', route: 'roles/list', permission: 'roles:read',
        children: [
          { label: 'All Roles', icon: 'list', route: 'roles/list' },
          { label: 'Create Role', icon: 'add', route: 'roles/create' },
        ]
      },
    ]
  }
})

10. Portability Considerations

When embedded in gspace1:

When embedded in a generated platform:

Key abstraction points:


11. What We Are NOT Doing


12. Risks and Mitigations

Risk Mitigation
gmodules1 imports may not resolve in gspace1 Verify all @module/* paths exist in tsconfig; use gspace1's path aliases
Backend-api missing CRUD endpoints for L1/L2 users Add them as part of this spec (Section 4.2)
autoFlashState hydration timing on first load Use .hydrated signal to gate operations that depend on persisted state
Role hierarchy enforcement gap at API level Backend validates that requesting user's level > target user's level
Mock API still referenced in dev Keep mock API in mocks/ for offline development; toggle via environment flag