Files
tessera-ctl/.planning/phases/03-module-system-domaincheck/03-01-SUMMARY.md
T
schalli 0007ef58d9 docs(03-01): complete Module SDK + Registry plan summary
- Module SDK package with versioned interface contract
- Database-driven module registry with per-tenant activation
- ModuleGuard for tenant-scoped module access control
2026-06-19 12:38:40 +02:00

118 lines
5.4 KiB
Markdown

---
phase: 03-module-system-domaincheck
plan: 01
subsystem: module-registry
tags: [module-sdk, prisma, nestjs, multi-tenant, guard]
dependency_graph:
requires: [prisma-service, tenant-middleware, jwt-auth-guard, roles-guard]
provides: [module-sdk-types, module-registry-service, module-guard, module-registry-controller]
affects: [app-module]
tech_stack:
added: ["@tessera/module-sdk"]
patterns: [upsert-activation, tenant-scoped-guard, decorator-composition]
key_files:
created:
- packages/module-sdk/package.json
- packages/module-sdk/tsconfig.json
- packages/module-sdk/src/types.ts
- packages/module-sdk/src/index.ts
- apps/api/prisma/migrations/20260619103242_add_module_registry/migration.sql
- apps/api/src/module-registry/dto/activate-module.dto.ts
- apps/api/src/module-registry/module-registry.service.ts
- apps/api/src/module-registry/module-registry.controller.ts
- apps/api/src/module-registry/module.guard.ts
- apps/api/src/module-registry/module-registry.module.ts
modified:
- apps/api/prisma/schema.prisma
- apps/api/src/app.module.ts
- pnpm-lock.yaml
decisions:
- "Framework-agnostic ComponentType in SDK instead of React dependency to keep package backend-compatible"
- "Soft-delete activation records (isActive=false) rather than row deletion for audit trail"
- "ModuleGuard uses slug-based lookup via @UseModule() decorator for downstream modules"
metrics:
duration: 7 min
completed: 2026-06-19T10:37:24Z
---
# Phase 03 Plan 01: Module SDK + Registry Service Summary
Module SDK package with versioned interface contract and database-driven module registry with per-tenant activation/deactivation via REST API and ModuleGuard for access control.
## What Was Built
### Task 1: Module SDK Package + Prisma Schema (8c24c1e)
**@tessera/module-sdk package** (`packages/module-sdk/`):
- `TesseraModule` interface: full module definition with id, name, version, slug, category, i18n descriptions, icon, routes, lazy-loaded components
- `ModuleRoute` interface: HTTP method, path, handler for module API routes
- `ModuleManifest` type: backend-only metadata (omits component/cardComponent)
- `ModuleCategory` type: string literal union for module grouping
- `ComponentType`: framework-agnostic component type for lazy-loaded UIs
**Prisma schema extensions:**
- `Module` model: id (UUID), slug (unique), name, version, category, description (JSON), icon, isSystem flag, timestamps
- `TenantModuleActivation` model: tenant-module join with isActive soft-delete, unique constraint on [tenantId, moduleId], index on tenantId
- Migration `20260619103242_add_module_registry` applied successfully
### Task 2: ModuleRegistry NestJS Module (fa15d35)
**ModuleRegistryService** — full CRUD + tenant activation:
- `findAll()`: list all registered modules
- `findBySlug(slug)`: lookup by slug
- `findActiveForTenant(tenantId)`: active modules for a tenant
- `activateForTenant(tenantId, moduleId)`: upsert activation record
- `deactivateForTenant(tenantId, moduleId)`: soft-delete (isActive=false)
- `isModuleActive(tenantId, moduleSlug)`: boolean check for guard
- `seedModule(manifest)`: upsert module by slug for startup seeding
**ModuleRegistryController** — REST endpoints:
- `GET /modules` — list all modules (any authenticated user)
- `GET /modules/active` — active modules for current tenant
- `POST /modules/:moduleId/activate` — ADMIN/SUPER_ADMIN only
- `POST /modules/:moduleId/deactivate` — ADMIN/SUPER_ADMIN only
**ModuleGuard + @UseModule() decorator:**
- CanActivate guard checking tenant module activation via slug
- Composed decorator: `@UseModule('domaincheck')` applies metadata + guard
- TenantId sourced from JWT/middleware (not user input) per T-03-04
**ActivateModuleDto** with `@IsUUID()` validation per T-03-02.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] React.ComponentType reference in SDK types**
- **Found during:** Task 1
- **Issue:** `types.ts` referenced `React.ComponentType` but the SDK package has no React dependency, causing TypeScript errors
- **Fix:** Defined a framework-agnostic `ComponentType` within the SDK instead of depending on React types
- **Files modified:** packages/module-sdk/src/types.ts
**2. [Rule 1 - Bug] DTO definite assignment in strict mode**
- **Found during:** Task 2
- **Issue:** `ActivateModuleDto.moduleId` property lacked definite assignment assertion, causing TS2564 with strict mode
- **Fix:** Added `!` definite assignment assertion (`moduleId!: string`) consistent with class-validator pattern
- **Files modified:** apps/api/src/module-registry/dto/activate-module.dto.ts
## Threat Mitigations Applied
| Threat ID | Mitigation | Implementation |
|-----------|------------|----------------|
| T-03-01 | @Roles(ADMIN, SUPER_ADMIN) on activate/deactivate | module-registry.controller.ts |
| T-03-02 | @IsUUID validator + Prisma parameterized queries | activate-module.dto.ts, module-registry.service.ts |
| T-03-03 | Module catalog accessible to all authenticated users | GET /modules has no role restriction |
| T-03-04 | tenantId from request (JWT/middleware), not user input | module.guard.ts uses req.tenantId |
## Verification Results
- API build: PASS
- Prisma migration status: up to date (3 migrations)
- SDK type-check: PASS
- Required exports present: TesseraModule, ModuleRoute, ModuleManifest, ModuleCategory
- No stubs or placeholders found
## Self-Check: PASSED
All 10 created files verified on disk. Both task commits (8c24c1e, fa15d35) verified in git log.