Design
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
Design - User Management Module
Objetivos de Diseño
Diseñar una arquitectura modular y escalable para el módulo de gestión de usuarios que implemente los requerimientos definidos en requirements.md:
- R-001-R-003: Sistema de visualización flexible (US-001)
- R-004-R-006: Gestión completa de creación de usuarios (US-002)
- R-007-R-009: Sistema de edición con validaciones (US-003)
- R-010-R-011: Operaciones de eliminación seguras (US-004)
- R-012-R-013: Control granular de estados (US-005)
- R-014-R-016: Sistema de búsqueda y filtrado eficiente (US-006)
- R-017-R-019: Operaciones masivas optimizadas (US-007)
- R-020-R-021: Gestión integral de roles (US-008)
Dependencias y Límites del Módulo
Dependencias Internas
@libs-core/security- CRÍTICO: Autenticación, autorización, permisos, guards, directivas@libs-core/module-base- Contratos base y interfaces@libs-core/ui-framework- Componentes de diseño y utilidades UI@gograb-space/utilities- Utilidades de validación y transformación@libs-core/log-system- Sistema de logging con decoradores@libs-core/event-system- Bus de eventos para comunicación@libs-core/mock-api- API simulada para desarrollo@libs-core/shared-models- Modelos genericos compartidos
Dependencias Externas
@angular/forms- Formularios reactivos y validaciones@angular/common- Pipes y directivas comunes@angular/router- Navegación entre vistas
Límites del Módulo
- Entrada: APIs REST de usuarios (contexto de tenant implícito), configuración de módulo
- Salida: Eventos de sistema, actualizaciones de UI
- No gestiona: Autenticación, sesiones, permisos granulares, gestión de tenant context
- No almacena: Contraseñas, datos sensibles, información de tenant (manejado por contexto)
- Contexto: Tenant se maneja como contexto de aplicación, no como campo en modelos
Arquitectura y Componentes
Contexto del Módulo
[Security Layer] ←→ [Route Guards] ←→ [Permission Validation]
↓ ↓ ↓
[Mock API] ←→ [UserApiService] ←→ [UserStateService] ←→ [UI Components]
↓ ↓ ↓
[UserFilterService] ←→ [UserFormService] ←→ [Event System] ←→ [Log System]
↑
[Security Directives]
División de Responsabilidades
Capa de Presentación
- UserListComponent: Orquesta tabla principal con todas las funcionalidades (US-001)
- Integra *hasPermission="users:view" para visibilidad
- Uso de *hasRole y *hasAnyRole para acciones específicas
- UserCardComponent: Renderiza cards individuales optimizadas (US-001)
- Directivas de seguridad para botones de acción
- UserDetailComponent: Presenta vista completa de perfil (US-001)
- Validación de permisos con PermissionService
- UserFormComponent: Gestiona formularios crear/editar (US-002, US-003)
- Guards: permissionGuard(['users:create', 'users:update'])
- Validación tiempo real con hasPermission()
- UserFiltersComponent: Maneja panel de filtros y búsqueda (US-006)
Capa de Lógica
- UserStateService: Estado global con Signals, gestiona loading states (US-001-US-008)
- Integración con PermissionService para cache de permisos (tenant context implícito)
- Context-aware user permissions usando Role[] y Permission[] sin tenantId redundante
- UserApiService: Comunicación HTTP, construcción de queries (US-001-US-008)
- Headers de seguridad automáticos via interceptors
- UserFormService: Construcción de formularios y validaciones (US-002, US-003)
- Validaciones de permisos antes de construcción de formularios
- UserFilterService: Gestión de filtros y persistencia de sesión (US-006, tenant context implícito)
- UserPermissionService: Wrapper local del PermissionService para permisos específicos de usuarios (tenant context automático)
Capa de Utilidades
- UserAvatarComponent: Avatar con iniciales o imagen (US-001)
- UserBulkActionsComponent: Barra de acciones masivas (US-007)
- UserStatusToggleComponent: Control de estados inline (US-005)
Contratos y Modelos
Entidades Principales
User Interface (Extendiendo @libs-core/shared-models)
import { UserProfile, Role, Permission, BaseEntity, SystemRoles } from '@libs-core/shared-models';
// Reutiliza UserProfile de shared-models como base, omitiendo tenantId (contexto implícito)
interface User extends Omit<UserProfile, 'tenantId'> {
// Campos adicionales específicos del módulo user-management
status: UserStatus; // Estado específico del módulo
lastActive: string; // Última actividad (ISO string)
// roles: Role[] - ya incluido en UserProfile (usando Role base sin tenantId)
// permissions: Permission[] - ya incluido en UserProfile (usando Permission base)
primaryRole: SystemRoles; // Role primario usando enum de shared-models
}
// Extender entidades base sin tenantId redundante (contexto de aplicación)
interface UserEntity extends BaseEntity {
email: string;
username: string;
firstName: string;
lastName: string;
roles: Role[]; // Roles base de shared-models (sin tenantId redundante)
permissions: Permission[]; // Permisos base de shared-models (sin tenantId redundante)
status: UserStatus;
lastActive: Date;
// tenantId se maneja como contexto de aplicación, no como campo
}
// Usar SystemRoles de shared-models para consistencia
type UserRole = SystemRoles; // Alias para compatibilidad
type UserStatus = 'active' | 'inactive' | 'suspended' | 'pending';
API Contracts (Usando @libs-core/shared-models)
import { ApiResponse, FilterCriteria } from '@libs-core/shared-models';
// Usa ApiResponse estándar de shared-models
type UserListResponse = ApiResponse<{
users: User[];
}>;
// Filtros simplificados - tenantId como contexto implícito
interface UserFilters {
// Filtros básicos sin tenantId redundante
search?: string; // Búsqueda por nombre/email
roleIds?: string[]; // Filtro por IDs de roles (contexto del tenant actual)
status?: UserStatus[]; // Filtro por estados
createdFrom?: string; // Fecha desde
createdTo?: string; // Fecha hasta
isActive?: boolean; // Filtro por estado activo
// Criterios complejos usando FilterCriteria cuando sea necesario
advancedFilters?: FilterCriteria[];
}
// Request/Response types simplificados - tenant del contexto de aplicación
interface CreateUserRequest {
email: string;
firstName: string;
lastName: string;
roleIds: string[]; // Array de Role.id (usando Role base de shared-models)
primaryRole?: SystemRoles; // Role principal usando enum de shared-models
isActive?: boolean;
metadata?: Record<string, unknown>; // Compatible con UserProfile
// tenantId omitido - se obtiene del contexto de aplicación
}
interface UpdateUserRequest extends Partial<CreateUserRequest> {
id: string;
version?: number; // Optimistic concurrency control
// tenantId omitido - contexto implícito
}
// Para casos donde el tenantId sea crítico (logs, auditoría)
interface UserAuditEvent {
userId: string;
action: string;
tenantId: string; // Explícito para auditoría y logs
timestamp: Date;
correlationId: string;
}
APIs y Endpoints
Operaciones CRUD
- GET /api/users - Lista con filtros:
UserFilters → UserListResponse - GET /api/users/:id - Detalle:
string → User - POST /api/users - Crear:
CreateUserRequest → User - PUT /api/users/:id - Actualizar:
UpdateUserRequest → User - DELETE /api/users/:id - Eliminar:
string → void
Operaciones Especiales
- PUT /api/users/:id/status - Cambiar estado:
{status: UserStatus} → User - POST /api/users/bulk - Operaciones masivas:
BulkOperation → BulkResult
Errores Relevantes
400- Validación fallida, email duplicado403- Sin permisos para la operación404- Usuario no encontrado409- Conflicto (usuario con dependencias)
Decisiones de Diseño
DEC-001: Signals vs Observables
Decisión: Usar Signals para estado local y Observables para HTTP Alternativa: Observables en todo el módulo Justificación: Signals optimizan change detection y son más legibles para estado Impacto: Mejor performance y código más maintible
DEC-002: Filtrado Server vs Client Side
Decisión: Filtrado server-side por defecto, client-side para refinamiento Alternativa: Solo filtrado client-side Justificación: Performance con datasets grandes, flexibilidad para refinamiento Impacto: Menos transferencia de datos, UX más fluida
DEC-003: Formularios Reactivos vs Template-driven
Decisión: Formularios reactivos con FormBuilder Alternativa: Template-driven forms Justificación: Mayor control, validaciones complejas, testing más sencillo Impacto: Más código inicial pero mayor flexibilidad y maintibilidad
DEC-004: Optimistic Updates
Decisión: Updates optimistas para operaciones rápidas (toggle status) Alternativa: Siempre esperar confirmación del servidor Justificación: Mejor UX percibida, rollback automático en caso de error Impacto: UX más fluida pero lógica de rollback necesaria
DEC-005: Operaciones Masivas
Decisión: Procesamiento secuencial con feedback de progreso Alternativa: Procesamiento paralelo o batch único Justificación: Control granular de errores, feedback visual detallado Impacto: Más tiempo de procesamiento pero mejor experiencia y control de errores
DEC-006: Estrategia de Permisos
Decisión: Uso de @libs-core/security con PermissionService y directivas estructurales Alternativa: Sistema de permisos custom o role-based simple Justificación: Consistencia con arquitectura GoGrab, multi-tenant, cache optimizado Impacto: Dependencia fuerte pero funcionalidad robusta y consistente
DEC-007: Guards vs Service Validation
Decisión: Guards para rutas + Service validation para acciones en componentes Alternativa: Solo guards o solo service validation Justificación: Doble capa de protección: navegación y UI Impacto: Mayor código inicial pero seguridad más robusta
DEC-008: Component State vs Global State
Decisión: Estado global en NGXS para datos de dominio, estado local para UI temporal Alternativa: Todo en componentes o todo en store Justificación: Datos de usuarios son compartidos, estado UI como modals es local Impacto: Mejor separación de responsabilidades, state management más claro
DEC-009: Effects vs Service Calls
Decisión: Effects para operaciones complejas con side effects, service calls directos para operaciones simples Alternativa: Todo en effects o todo en services Justificación: Effects permiten composición, logging centralizado, error handling Impacto: Código más testeable y mantenible para operaciones complejas
Riesgos y Mitigaciones
Riesgo R-001: Performance con grandes datasets
Mitigación: Paginación server-side, virtual scrolling para casos extremos Monitoreo: Métricas de tiempo de carga y memoria usada
Riesgo R-002: Consistencia de datos en updates optimistas
Mitigación: Sistema de rollback automático, invalidación de caché Monitoreo: Logs de rollbacks y errores de sincronización
Riesgo R-003: Validaciones client-side únicamente
Mitigación: Validaciones defensivas, confianza en validaciones server-side Monitoreo: Logs de errores de validación del servidor
Riesgo R-004: Estado desincronizado entre componentes
Mitigación: Estado centralizado con Signals, eventos para comunicación Monitoreo: Testing de integración extensivo
Estrategia de Pruebas y Observabilidad
Testing
- Unitarios: Servicios, utilidades, validaciones (>90% cobertura)
- Integración: Flujos completos CRUD, comunicación entre componentes
- E2E: Flujos críticos de usuario, operaciones masivas
Observabilidad
- Eventos Emitidos: USER_CREATED, USER_UPDATED, USER_DELETED, USER_STATUS_CHANGED, USER_ROLE_CHANGED, BULK_OPERATION_COMPLETED
- Métricas: Tiempo de carga de lista, tiempo de operaciones CRUD, errores por tipo
- Logs: Operaciones críticas con contexto de usuario y correlación ID
Validaciones Clave
- Formularios: validación en tiempo real y al envío
- APIs: manejo de errores con feedback específico
- Estado: consistencia entre UI y estado global
- Performance: tiempos de respuesta <200ms para operaciones comunes
Mapeo de Cobertura
| Requerimiento | Componente Principal | Responsabilidad |
|---|---|---|
| US-001 (Visualización) | UserListComponent, UserCardComponent, UserDetailComponent | Renderizado flexible de datos |
| US-002 (Crear) | UserFormComponent, UserApiService | Formulario + validaciones + API |
| US-003 (Editar) | UserFormComponent, UserApiService | Formulario precargado + actualización |
| US-004 (Eliminar) | UserDeleteConfirmComponent, UserApiService | Confirmación + eliminación segura |
| US-005 (Estados) | UserStatusToggleComponent, UserApiService | Control rápido de estados |
| US-006 (Filtrado) | UserFiltersComponent, UserFilterService | Filtros + query building |
| US-007 (Masivas) | UserBulkActionsComponent, UserApiService | Selección múltiple + operaciones batch |
| US-008 (Roles) | UserFormComponent, RoleSelectorComponent | Asignación + visualización de roles |
Trazabilidad Requirements → Design
- R-001-R-003 → UserListComponent con tres modos de visualización + directivas *hasPermission
- R-004-R-006 → UserFormComponent + UserFormService + validaciones + permissionGuard
- R-007-R-009 → Mismo formulario en modo edición + UserApiService + hasPermission('users:update')
- R-010-R-011 → UserDeleteConfirmComponent + validaciones de dependencias + hasPermission('users:delete')
- R-012-R-013 → UserStatusToggleComponent + estados tipados + hasPermission('users:status')
- R-014-R-016 → UserFilterService + query parameters + persistencia
- R-017-R-019 → UserBulkActionsComponent + procesamiento secuencial + hasPermission('users:bulk')
- R-020-R-021 → RoleSelectorComponent + permisos de visualización + hasPermission('users:role')
- R-022-R-025 → Sistema completo de permisos con @libs-core/security
Arquitectura de Seguridad Integrada
Route Protection
// user-management.routes.ts
{
path: 'users',
loadComponent: () => import('./user-list.component'),
canActivate: [authGuard, modulePermissionGuard('user-management')]
},
{
path: 'users/create',
loadComponent: () => import('./user-form.component'),
canActivate: [authGuard, permissionGuard(['users:create'])]
}
Template Security
<!-- user-list.component.html -->
<button *hasPermission="'users:create'"
(click)="createUser()">
Nuevo Usuario
</button>
<div *hasAnyRole="['admin', 'user-manager']">
<app-user-bulk-actions></app-user-bulk-actions>
</div>
<td *hasPermission="'users:delete'; else: disabledAction">
<button (click)="deleteUser(user)">Eliminar</button>
</td>
Service Integration
// user-state.service.ts
constructor(
private permissionService: PermissionService,
private contextService: ContextService
) {
// Auto-refresh permissions on context change
this.contextService.contextChange$.subscribe(() => {
this.refreshUserPermissions();
});
}
NGXS Integration Examples
// user.state.ts
@State<UserStateModel>({
name: 'users',
defaults: {
users: [],
selectedUser: null,
loading: false,
error: null,
filters: {}
}
})
export class UserState {
@Selector()
static getUsers(state: UserStateModel): User[] {
return state.users;
}
@Action(UserActions.LoadUsers)
loadUsers(ctx: StateContext<UserStateModel>, action: UserActions.LoadUsers) {
ctx.patchState({ loading: true, error: null });
// Logic handled by Effects
}
}
// user.effects.ts
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.LoadUsers),
switchMap(action =>
this.userApiService.getUsers(action.filters).pipe(
map(users => UserActions.LoadUsersSuccess({ users })),
catchError(error => of(UserActions.LoadUsersFailure({ error })))
)
)
)
);
}
// user-list.component.ts
export class UserListComponent {
users$ = this.store.select(UserSelectors.getUsers);
loading$ = this.store.select(UserSelectors.isLoading);
constructor(private store: Store) {}
loadUsers() {
this.store.dispatch(new UserActions.LoadUsers());
}
}
El diseño está optimizado para cumplir todos los requerimientos funcionales y no funcionales, con integración completa del sistema de seguridad GoGrab Space y NGXS para state management robusto, manteniendo separación de responsabilidades y facilidad de testing y mantenimiento.