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:
@@ -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
|
||||
Reference in New Issue
Block a user