docs(03): create phase 3 plans - Module System & Domaincheck

4 plans in 3 waves:
- 03-01: Module SDK + Registry + Activation API
- 03-02: Domaincheck backend + frontend (vertical slice)
- 03-03: Lazy Loading + Category Pages
- 03-04: Visual Verification

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 12:14:54 +02:00
parent 89f559ce62
commit 6e2f6e7c3e
2 changed files with 216 additions and 4 deletions
@@ -19,12 +19,15 @@ Modulares Plugin-System mit versioniertem SDK (@tessera/sdk), datenbankgestuetzt
- **D-03:** TLD-Liste soll konfigurierbar sein (Standard-TLDs vorausgewaehlt, erweiterbar).
### Modul-Darstellung
- **D-04:** Claude entscheidet das UI-Pattern fuer die Modul-Darstellung basierend auf der bestehenden Architektur (Next.js App Router, Route Groups, Lazy Loading).
- **D-04:** Module werden in Kategorien gruppiert (z.B. "Domain-Tools"). Eine Kategorie-Seite zeigt alle Module dieser Kategorie im Hauptbereich — nicht ein einzelnes kleines Modul auf der ganzen Seite.
- **D-05a:** Einheitliches Look & Feel: Module sollen als Karten/Panels innerhalb der Kategorie-Seite erscheinen, sodass auch kleine Module nicht verloren wirken.
- **D-05b:** Bei Bedarf kann ein Modul auch eine erweiterte/Vollbild-Ansicht haben (z.B. wenn der Benutzer es oeffnet), aber die Standard-Ansicht ist kompakt in der Kategorie-Uebersicht.
### Module SDK
- **D-05:** Modul-Interface als `@tessera/sdk` Package im Monorepo (packages/sdk oder packages/module-sdk).
- **D-06:** Module werden per Datenbank-Registry verwaltet — kein Filesystem-Scanning.
- **D-07:** Aktivierung/Deaktivierung pro Mandant durch Admin ohne Neustart (Requirement MOD-03, MRKT-02).
- **D-06:** Modul-Interface als `@tessera/sdk` Package im Monorepo (packages/sdk oder packages/module-sdk).
- **D-07:** Module werden per Datenbank-Registry verwaltet — kein Filesystem-Scanning.
- **D-08:** Aktivierung/Deaktivierung pro Mandant durch Admin ohne Neustart (Requirement MOD-03, MRKT-02).
- **D-09:** Jedes Modul gehoert zu einer Kategorie (z.B. "Domain-Tools", "Utilities"). Kategorie ist Teil der Modul-Metadaten im SDK.
### Claude's Discretion
- Modul-UI-Pattern: eigene Seite vs. Panel vs. anderes — basierend auf bestehender App Router Architektur
@@ -0,0 +1,209 @@
# Phase 3: Module System & Domaincheck - Research
**Researched:** 2026-06-19
**Status:** Complete
## Module System Architecture
### NestJS Dynamic Modules Pattern
NestJS provides `DynamicModule` for runtime module configuration. For Tessera's use case:
1. **Module Registry Service** — A core NestJS service that manages module metadata in PostgreSQL
2. **Module Loader** — Discovers and registers module routes at startup, checks activation per request
3. **Module Guard** — NestJS guard that checks if a module is activated for the requesting tenant
**Pattern:**
```
ModuleRegistryModule (core)
├── ModuleRegistryService — CRUD for module metadata
├── ModuleActivationService — per-tenant activation/deactivation
├── ModuleGuard — checks activation before allowing access
└── ModuleController — admin endpoints for module management
```
### Frontend Lazy Loading with Next.js App Router
Next.js App Router supports:
- `next/dynamic` with `{ ssr: false }` for client-only lazy components
- Route-based code splitting via file-system routing (automatic)
- Dynamic route segments: `app/(portal)/modules/[moduleId]/page.tsx`
**Recommended approach for Tessera:**
- Each module registers a frontend component path in the registry
- Category pages load module cards/panels via dynamic imports
- Expanded view uses a dynamic route: `/modules/[moduleId]`
- Inactive modules are never imported (no bundle impact)
### Module SDK Design (@tessera/module-sdk)
The SDK package defines the contract between the platform and modules:
```typescript
interface TesseraModule {
id: string;
name: string;
version: string;
category: string;
description: Record<string, string>; // i18n: { de: '...', en: '...' }
icon?: string;
// Backend
routes?: ModuleRoute[];
// Frontend
component: () => Promise<React.ComponentType>;
cardComponent?: () => Promise<React.ComponentType>; // compact card view
}
interface ModuleRoute {
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
path: string;
handler: string; // reference to controller method
}
```
### Database Schema for Module Registry
```prisma
model Module {
id String @id @default(uuid())
slug String @unique // e.g. "domaincheck"
name String // display name
version String
category String // e.g. "domain-tools"
description Json // { de: "...", en: "..." }
icon String?
isSystem Boolean @default(false) // built-in modules
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
activations TenantModuleActivation[]
}
model TenantModuleActivation {
id String @id @default(uuid())
tenantId String
moduleId String
isActive Boolean @default(true)
activatedAt DateTime @default(now())
module Module @relation(fields: [moduleId], references: [id])
@@unique([tenantId, moduleId])
@@index([tenantId])
}
```
## Domaincheck Module Implementation
### Domain Availability Check Methods
| Method | Pros | Cons | Recommendation |
|--------|------|------|----------------|
| RDAP (Registration Data Access Protocol) | Standard, JSON response, no rate limits | Not all TLDs support it | Primary method |
| DNS lookup (node:dns) | Fast, no external deps, built-in | Only tells if DNS exists, not registration | Fallback/complement |
| WHOIS via library | Most complete data | Rate-limited, text parsing fragile | Not needed (user chose simple status only) |
**Recommended approach:**
1. Primary: DNS resolution via `node:dns/promises` — `dns.resolve(domain)`. If resolves → registered. If NXDOMAIN → likely available.
2. Enhancement: RDAP query to `https://rdap.org/domain/{domain}` for accurate registration status.
For MVP, DNS resolution is sufficient and requires no external dependencies.
### TLD Variant Checking
User enters "beispiel", system checks:
- beispiel.de
- beispiel.com
- beispiel.net
- beispiel.org
Default TLDs stored in module config, configurable per tenant via module settings.
### API Endpoint Design
```
POST /modules/domaincheck/check
Body: { domain: "beispiel" }
Response: {
domain: "beispiel",
results: [
{ tld: "de", fqdn: "beispiel.de", status: "registered" },
{ tld: "com", fqdn: "beispiel.com", status: "available" },
{ tld: "net", fqdn: "beispiel.net", status: "registered" },
{ tld: "org", fqdn: "beispiel.org", status: "available" }
]
}
```
### Frontend Component Structure
```
modules/domaincheck/
├── DomaincheckCard.tsx — compact card for category page
├── DomaincheckPanel.tsx — expanded view
├── components/
│ ├── DomainInput.tsx — input field + submit button
│ └── ResultList.tsx — TLD results with green/red badges
└── actions.ts — server action to call API
```
## Category Page Pattern
Modules grouped by category on a shared page:
```
/modules/domain-tools → shows all Domain-Tools modules as cards
/modules/domain-tools/domaincheck → expanded domaincheck view
```
Category page layout:
- Grid of module cards (similar to dashboard widgets)
- Each card shows: icon, name, short description, "Open" button
- Cards are lazy-loaded based on tenant activation
## Integration Points
### With Existing Auth System
- Module endpoints protected by existing JwtAuthGuard
- Tenant context from existing middleware (tenantId in JWT)
- Module activation checked via ModuleGuard (new)
### With Existing Prisma/RLS
- TenantModuleActivation uses tenantId for RLS
- Module metadata is global (no tenant isolation needed)
- Module-specific data (e.g., domaincheck history) would be tenant-scoped
### With Frontend Shell
- Module category links added to sidebar (Phase 4 handles full sidebar integration)
- For Phase 3: direct route `/modules/domaincheck` accessible after login
- AppShell wraps module pages (header + sidebar visible)
## Risks & Mitigations
| Risk | Impact | Mitigation |
|------|--------|-----------|
| DNS lookup rate limiting | Slow checks | Parallel lookups with Promise.allSettled, timeout per check |
| Module lazy loading complexity | Build issues | Start with simple next/dynamic, test in Docker build |
| Module SDK breaking changes | Future modules break | Version field in registry, SDK follows semver |
| Category page with only 1 module | Looks empty | Design card to look good standalone, placeholder for future modules |
## Validation Architecture
### Unit Tests
- ModuleRegistryService: CRUD operations, activation per tenant
- DomaincheckService: DNS resolution mocking, TLD variant generation
- ModuleGuard: activation check logic
### Integration Tests
- Module activation/deactivation via API
- Domaincheck endpoint with real DNS (selected safe domains)
- Tenant isolation of module activations
### E2E Verification
- Admin activates domaincheck module
- User navigates to module, enters domain
- Results display with correct free/registered status
---
## RESEARCH COMPLETE