Diagram 3b: DocumentService calls PersonService and TagService, not their repositories directly. Replace personRepo/tagRepo cross-ref stubs with personSvc/tagSvc to accurately reflect the layering rule. Diagram 3b.2: TranscriptionService, AnnotationService, and CommentService each use a JPA repository, not JDBC directly. Add TranscriptionBlockRepository, AnnotationRepository, and CommentRepository components and route the service→repo→db chain. TranscriptionQueueService delegates to DocumentService and AuditLogQueryService (no repo of its own); replace the incorrect →db arrow with cross-diagram stubs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
553 lines
36 KiB
Markdown
553 lines
36 KiB
Markdown
# Familienarchiv — C4 Architecture Diagrams
|
|
|
|
> For domain terminology used in these diagrams, see [docs/GLOSSARY.md](../GLOSSARY.md).
|
|
|
|
## Level 1 — System Context
|
|
|
|
Who uses the system and what external systems does it interact with.
|
|
|
|
```mermaid
|
|
C4Context
|
|
title System Context: Familienarchiv
|
|
|
|
Person(admin, "Administrator", "Manages users, triggers bulk imports, reviews and transcribes documents")
|
|
Person(member, "Family Member", "Searches, browses, reads, and transcribes archived documents")
|
|
|
|
System(familienarchiv, "Familienarchiv", "Web application for digitising, organising, and searching family documents")
|
|
|
|
Rel(admin, familienarchiv, "Manages via browser", "HTTPS")
|
|
Rel(member, familienarchiv, "Searches, reads, and transcribes via browser", "HTTPS")
|
|
```
|
|
|
|
---
|
|
|
|
## Level 2 — Containers
|
|
|
|
The deployable units that make up the system and how they communicate.
|
|
|
|
```mermaid
|
|
C4Container
|
|
title Container Diagram: Familienarchiv
|
|
|
|
Person(user, "User", "Admin or family member")
|
|
|
|
System_Boundary(archiv, "Familienarchiv (Docker Compose)") {
|
|
Container(frontend, "Web Frontend", "SvelteKit / Node.js", "Server-side rendered UI. Handles auth session cookies, document search and viewer, transcription editor, annotation layer, family tree (Stammbaum), stories (Geschichten), activity feed (Chronik), enrichment workflow, and admin panel.")
|
|
|
|
Container(backend, "API Backend", "Spring Boot 4 / Java 21 / Jetty", "REST API. Implements document management, search, user auth, file upload/download, transcription, OCR orchestration, and SSE notifications.")
|
|
|
|
Container(ocr, "OCR Service", "Python FastAPI / port 8000", "Handwritten text recognition (HTR) and OCR microservice. Single-node by design — see ADR-001. Reachable only on the internal Docker network; no external port exposed.")
|
|
|
|
ContainerDb(db, "Relational Database", "PostgreSQL 16", "Stores document metadata, persons, users, permission groups, tags, transcription blocks, audit log, and Spring Session data.")
|
|
|
|
ContainerDb(storage, "Object Storage", "MinIO (S3-compatible)", "Stores the actual document files (PDFs, scans). Objects keyed as documents/{UUID}_{filename}.")
|
|
|
|
Container(mc, "Bucket Init Helper", "MinIO Client (mc)", "One-shot container on startup. Creates the archive bucket with private access policy.")
|
|
}
|
|
|
|
Rel(user, frontend, "Uses", "HTTPS / Browser")
|
|
Rel(frontend, backend, "API requests with Basic Auth token", "HTTP / REST / JSON")
|
|
Rel(backend, user, "SSE notifications (server-sent events)", "HTTP / SSE — direct backend-to-browser")
|
|
Rel(backend, db, "Reads and writes metadata and sessions", "JDBC / SQL")
|
|
Rel(backend, storage, "Uploads and streams document files", "HTTP / S3 API (AWS SDK v2)")
|
|
Rel(backend, ocr, "OCR job requests with presigned MinIO URL", "HTTP / REST / JSON")
|
|
Rel(ocr, storage, "Fetches PDF via presigned URL", "HTTP / S3 presigned")
|
|
Rel(mc, storage, "Creates bucket on startup", "MinIO Client CLI")
|
|
```
|
|
|
|
---
|
|
|
|
## Level 3 — Components: API Backend
|
|
|
|
The internal structure of the Spring Boot backend, split into three focused views.
|
|
|
|
### 3a — Security & Authentication
|
|
|
|
How requests are authenticated and write operations are authorised.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: API Backend — Security & Authentication
|
|
|
|
Container(frontend, "Web Frontend", "SvelteKit")
|
|
ContainerDb(db, "PostgreSQL")
|
|
|
|
System_Boundary(backend, "API Backend (Spring Boot)") {
|
|
Component(secFilter, "Security Filter Chain", "Spring Security", "Enforces authentication on all requests. Parses Basic Auth header and validates credentials via BCrypt. Permits password-reset, invite, and register endpoints without authentication.")
|
|
Component(permAspect, "PermissionAspect", "Spring AOP", "Intercepts methods annotated with @RequirePermission. Checks user's granted authorities against the required permission. Throws 401/403 if denied.")
|
|
Component(secConf, "SecurityConfig", "Spring @Configuration", "Configures filter chain: all routes require authentication, CSRF disabled, BCrypt password encoder, DaoAuthenticationProvider with CustomUserDetailsService.")
|
|
Component(userDetails, "CustomUserDetailsService", "Spring Security UserDetailsService", "Loads AppUser by email from DB. Converts group permissions to Spring GrantedAuthority objects. Logs unknown permissions.")
|
|
}
|
|
|
|
Rel(frontend, secFilter, "All requests", "HTTP / Basic Auth header")
|
|
Rel(secFilter, permAspect, "Authenticated requests proceed to guarded methods", "AOP @Around")
|
|
Rel(secConf, userDetails, "Wires as UserDetailsService", "")
|
|
Rel(userDetails, db, "Loads user by email", "JDBC")
|
|
```
|
|
|
|
### 3b — Document Management & Import
|
|
|
|
Document management, file storage, and bulk Excel/ODS import.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: API Backend — Document Management & Import
|
|
|
|
Container(frontend, "Web Frontend", "SvelteKit")
|
|
ContainerDb(db, "PostgreSQL")
|
|
ContainerDb(minio, "MinIO")
|
|
|
|
System_Boundary(backend, "API Backend (Spring Boot)") {
|
|
Component(docCtrl, "DocumentController", "Spring MVC — /api/documents", "CRUD for documents: search, get by ID, update metadata, upload/download file, conversation thread, and batch metadata updates.")
|
|
Component(adminCtrl, "AdminController", "Spring MVC — /api/admin", "Triggers asynchronous Excel/ODS mass import (requires ADMIN permission). Reports import state (IDLE/RUNNING/DONE/FAILED).")
|
|
|
|
Component(docSvc, "DocumentService", "Spring Service", "Core document business logic: store, update, search. Resolves persons and tags, delegates file I/O to FileService, builds dynamic JPA Specifications, and integrates with audit logging.")
|
|
Component(fileSvc, "FileService", "Spring Service", "Wraps AWS SDK v2 S3Client. Uploads files with UUID-keyed paths, computes SHA-256 hash, downloads with content-type detection, and generates presigned URLs for OCR access.")
|
|
Component(massImport, "MassImportService", "Spring Service — @Async", "Reads Excel/ODS files from /import mount. Tracks import state (IDLE/RUNNING/DONE/FAILED) and delegates to ExcelService. Returns immediately; processing runs asynchronously.")
|
|
Component(excelSvc, "ExcelService", "Spring Service", "Parses Excel/ODS workbooks (Apache POI). Column indices configurable via application.properties. Creates/updates document records per row.")
|
|
Component(minioConf, "MinioConfig", "Spring @Configuration", "Creates the S3Client and S3Presigner beans with path-style access for MinIO. Validates MinIO connectivity on startup.")
|
|
|
|
Component(docRepo, "DocumentRepository", "Spring Data JPA", "Queries documents with Specification-based dynamic search, bidirectional conversation thread queries, full-text search with ranking and match highlighting, and transcription pipeline queue projections.")
|
|
Component(docSpec, "DocumentSpecifications", "JPA Criteria API", "Factory for composable predicates: hasText (full-text), hasSender, hasReceiver, isBetween (date range), hasTags (subquery AND/OR logic).")
|
|
}
|
|
|
|
Component(personSvc, "PersonService", "Spring Service", "See diagram 3c.2. Called by DocumentService to resolve sender / receiver persons by ID.")
|
|
Component(tagSvc, "TagService", "Spring Service", "See diagram 3c. Called by DocumentService to find or create tags by name.")
|
|
|
|
Rel(frontend, docCtrl, "Document requests", "HTTP / JSON")
|
|
Rel(frontend, adminCtrl, "Trigger import", "HTTP / JSON")
|
|
Rel(docCtrl, docSvc, "Delegates to", "")
|
|
Rel(adminCtrl, massImport, "Triggers", "")
|
|
Rel(docSvc, fileSvc, "Upload / download files", "")
|
|
Rel(docSvc, docRepo, "Reads / writes documents", "")
|
|
Rel(docSvc, docSpec, "Builds search predicates", "")
|
|
Rel(docSvc, personSvc, "Resolves sender / receivers", "")
|
|
Rel(docSvc, tagSvc, "Finds or creates tags", "")
|
|
Rel(massImport, excelSvc, "Parses Excel/ODS file", "")
|
|
Rel(excelSvc, docSvc, "Creates / updates documents", "")
|
|
Rel(minioConf, fileSvc, "Provides S3Client and S3Presigner beans", "")
|
|
Rel(fileSvc, minio, "PUT / GET / presigned URL objects", "S3 API / HTTP")
|
|
Rel(docRepo, db, "SQL queries", "JDBC")
|
|
```
|
|
|
|
### 3b.2 — Document Transcription Pipeline
|
|
|
|
Annotation-driven transcription: page markup, text blocks, versioning, and comment threads.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: API Backend — Document Transcription Pipeline
|
|
|
|
Container(frontend, "Web Frontend", "SvelteKit")
|
|
ContainerDb(db, "PostgreSQL")
|
|
|
|
System_Boundary(backend, "API Backend (Spring Boot)") {
|
|
Component(transcriptionCtrl, "TranscriptionBlockController", "Spring MVC — /api/transcription", "CRUD for transcription text blocks per document page. Manages sort order, review status, and block version history.")
|
|
Component(annotationCtrl, "AnnotationController", "Spring MVC — /api/documents/{id}/annotations", "CRUD for free-form page annotations with polygon coordinates, colour coding, and file-hash tracking.")
|
|
Component(commentCtrl, "CommentController", "Spring MVC — /api/documents/{id}/comments", "Threaded comment CRUD on transcription blocks with @mention support and notification triggers.")
|
|
|
|
Component(transcriptionSvc, "TranscriptionService", "Spring Service", "Creates and updates transcription blocks from annotation regions. Tracks block versions, sanitizes text with an HTML allow-list, and triggers mentions.")
|
|
Component(transcriptionQueueSvc, "TranscriptionQueueService", "Spring Service", "Assembles segmentation, transcription, and review queue projections by delegating to DocumentService and AuditLogQueryService.")
|
|
Component(annotationSvc, "AnnotationService", "Spring Service", "Manages document page annotations with polygon coordinates. Called by OcrAsyncRunner to persist OCR-generated block boundaries.")
|
|
Component(commentSvc, "CommentService", "Spring Service", "Creates and manages threaded comments with @mention parsing. Triggers NotificationService for REPLY and MENTION events.")
|
|
|
|
Component(blockRepo, "TranscriptionBlockRepository", "Spring Data JPA", "Reads and writes TranscriptionBlock and TranscriptionBlockVersion records.")
|
|
Component(annotationRepo, "AnnotationRepository", "Spring Data JPA", "Reads and writes DocumentAnnotation records.")
|
|
Component(commentRepo, "CommentRepository", "Spring Data JPA", "Reads and writes DocumentComment records.")
|
|
}
|
|
|
|
Component(documentSvc, "DocumentService", "Spring Service", "See diagram 3b. Called by TranscriptionQueueService to assemble pipeline queue projections.")
|
|
Component(auditQuerySvc, "AuditLogQueryService", "Spring Service", "See diagram 3e. Called by TranscriptionQueueService for pipeline activity data.")
|
|
|
|
Rel(frontend, transcriptionCtrl, "Transcription block requests", "HTTP / JSON")
|
|
Rel(frontend, annotationCtrl, "Annotation requests", "HTTP / JSON")
|
|
Rel(frontend, commentCtrl, "Comment requests", "HTTP / JSON")
|
|
Rel(transcriptionCtrl, transcriptionSvc, "Delegates to", "")
|
|
Rel(transcriptionCtrl, transcriptionQueueSvc, "Queries pipeline queues", "")
|
|
Rel(annotationCtrl, annotationSvc, "Delegates to", "")
|
|
Rel(commentCtrl, commentSvc, "Delegates to", "")
|
|
Rel(transcriptionSvc, blockRepo, "Reads / writes blocks and versions", "")
|
|
Rel(annotationSvc, annotationRepo, "Reads / writes annotations", "")
|
|
Rel(commentSvc, commentRepo, "Reads / writes comments", "")
|
|
Rel(transcriptionQueueSvc, documentSvc, "Queries pipeline document state", "")
|
|
Rel(transcriptionQueueSvc, auditQuerySvc, "Queries pipeline activity data", "")
|
|
Rel(blockRepo, db, "SQL queries", "JDBC")
|
|
Rel(annotationRepo, db, "SQL queries", "JDBC")
|
|
Rel(commentRepo, db, "SQL queries", "JDBC")
|
|
```
|
|
|
|
### 3c — Users, Groups & Administration
|
|
|
|
User lifecycle, permission groups, tag management, and authentication endpoints.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: API Backend — Users, Groups & Administration
|
|
|
|
Container(frontend, "Web Frontend", "SvelteKit")
|
|
ContainerDb(db, "PostgreSQL")
|
|
|
|
System_Boundary(backend, "API Backend (Spring Boot)") {
|
|
Component(userCtrl, "UserController", "Spring MVC — /api/users", "Returns current user (/me), creates and deletes users (requires ADMIN_USER), supports user search and profile updates.")
|
|
Component(groupCtrl, "GroupController", "Spring MVC — /api/groups", "Lists and manages permission groups.")
|
|
Component(tagCtrl, "TagController", "Spring MVC — /api/tags", "Lists tags for typeahead, supports tag merge, tree structure, and subtree deletion.")
|
|
Component(inviteCtrl, "InviteController", "Spring MVC — /api/auth/invite", "Creates invite codes and validates them at registration time. Rate-limited via WebConfig interceptor.")
|
|
Component(authCtrl, "AuthController", "Spring MVC — /api/auth", "Handles user registration (POST /register) and password reset token endpoints (/forgot-password, /reset-password).")
|
|
|
|
Component(userSvc, "UserService", "Spring Service", "User CRUD with BCrypt password encoding, group assignment, and audit logging. Orchestrates invite-based registration and password reset tokens.")
|
|
Component(tagSvc, "TagService", "Spring Service", "Tag CRUD with name search, hierarchical tree structure, merge/reparent operations, and recursive subtree deletion.")
|
|
Component(dataInit, "DataInitializer", "CommandLineRunner", "On startup: creates default admin user and groups if none exist. Seeds test data if DB is empty.")
|
|
|
|
Component(userRepo, "AppUserRepository", "Spring Data JPA", "Finds users by email. Supports search by email or display name.")
|
|
Component(groupRepo, "UserGroupRepository", "Spring Data JPA", "Manages permission groups.")
|
|
Component(tagRepo, "TagRepository", "Spring Data JPA", "Finds or creates tags by name (case-insensitive). Supports recursive ancestor/descendant CTE queries and merge/reparent helpers.")
|
|
}
|
|
|
|
Rel(frontend, userCtrl, "User requests", "HTTP / JSON")
|
|
Rel(frontend, groupCtrl, "Group requests", "HTTP / JSON")
|
|
Rel(frontend, tagCtrl, "Tag requests", "HTTP / JSON")
|
|
Rel(frontend, inviteCtrl, "Invite validation", "HTTP / JSON")
|
|
Rel(frontend, authCtrl, "Registration and password reset", "HTTP / JSON")
|
|
Rel(userCtrl, userSvc, "Delegates to", "")
|
|
Rel(groupCtrl, userSvc, "Delegates to", "")
|
|
Rel(tagCtrl, tagSvc, "Delegates to", "")
|
|
Rel(tagSvc, tagRepo, "Reads / writes tags", "")
|
|
Rel(inviteCtrl, userSvc, "Creates and validates invites", "")
|
|
Rel(authCtrl, userSvc, "Registers users, resets passwords", "")
|
|
Rel(userSvc, userRepo, "Reads / writes users", "")
|
|
Rel(userSvc, groupRepo, "Assigns groups", "")
|
|
Rel(dataInit, db, "Seeds initial data", "JDBC")
|
|
Rel(userRepo, db, "SQL queries", "JDBC")
|
|
Rel(groupRepo, db, "SQL queries", "JDBC")
|
|
Rel(tagRepo, db, "SQL queries", "JDBC")
|
|
```
|
|
|
|
### 3c.2 — Persons & Family Graph
|
|
|
|
Person management including family relationship modelling and transitive inference.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: API Backend — Persons & Family Graph
|
|
|
|
Container(frontend, "Web Frontend", "SvelteKit")
|
|
ContainerDb(db, "PostgreSQL")
|
|
|
|
System_Boundary(backend, "API Backend (Spring Boot)") {
|
|
Component(personCtrl, "PersonController", "Spring MVC — /api/persons", "Lists and searches family members. Returns documents sent by or received by a person, correspondent suggestions, and person summary with document counts.")
|
|
Component(relCtrl, "RelationshipController", "Spring MVC — /api/network, /api/persons/{id}/relationships", "CRUD for explicit person relationships and the full family network graph (nodes + edges) used by the Stammbaum view.")
|
|
|
|
Component(personSvc, "PersonService", "Spring Service", "Person CRUD, alias management, and merge operations (reassigns all document sender/receiver references before deleting duplicate persons).")
|
|
Component(relSvc, "RelationshipService", "Spring Service", "Manages explicit directional family relationships (PARENT_OF, SPOUSE_OF, SIBLING_OF, etc.) with optional date ranges and notes.")
|
|
Component(relInference, "RelationshipInferenceService", "Spring Service", "Computes transitive family relationships from explicit edges to infer grandparent/grandchild, aunt/uncle, and other extended-family links for the network graph.")
|
|
|
|
Component(personRepo, "PersonRepository", "Spring Data JPA", "Queries persons with name search (including aliases), correspondent discovery, person summaries with document counts, and merge/reassignment helpers.")
|
|
}
|
|
|
|
Rel(frontend, personCtrl, "Person requests", "HTTP / JSON")
|
|
Rel(frontend, relCtrl, "Relationship and graph requests", "HTTP / JSON")
|
|
Rel(personCtrl, personSvc, "Delegates to", "")
|
|
Rel(relCtrl, relSvc, "Delegates to", "")
|
|
Rel(relCtrl, relInference, "Queries inferred graph", "")
|
|
Rel(personSvc, personRepo, "Reads / writes persons", "")
|
|
Rel(relSvc, db, "Reads / writes relationships", "JDBC")
|
|
Rel(relInference, db, "Reads relationships for inference", "JDBC")
|
|
Rel(personRepo, db, "SQL queries", "JDBC")
|
|
```
|
|
|
|
### 3d — OCR Orchestration
|
|
|
|
How the Spring Boot backend manages OCR jobs, streams results, and trains recognition models.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: API Backend — OCR Orchestration
|
|
|
|
Container(frontend, "Web Frontend", "SvelteKit")
|
|
ContainerDb(db, "PostgreSQL")
|
|
ContainerDb(minio, "MinIO")
|
|
Container(ocrPy, "OCR Service", "Python FastAPI")
|
|
|
|
System_Boundary(backend, "API Backend (Spring Boot)") {
|
|
Component(ocrCtrl, "OcrController", "Spring MVC — /api/ocr", "REST entry point: trigger single or batch OCR jobs, stream progress via SSE, query job status, and manage training runs and per-sender models.")
|
|
Component(ocrSvc, "OcrService", "Spring Service", "Creates OcrJob and OcrJobDocument records, checks Python service health, and delegates async execution to OcrAsyncRunner.")
|
|
Component(ocrBatch, "OcrBatchService", "Spring Service", "Orchestrates multi-document OCR jobs, iterating documents and delegating each to OcrAsyncRunner.")
|
|
Component(ocrAsync, "OcrAsyncRunner", "Spring Component — @Async", "Async worker that streams OCR results from Python page by page, persists transcription blocks and annotations via domain services, and emits progress via SSE.")
|
|
Component(ocrClient, "RestClientOcrClient", "Spring Component", "HTTP client wrapping the Python service: POST /ocr/stream (NDJSON), /train, /segtrain, and /train-sender. Falls back from streaming to batch on 404.")
|
|
Component(ocrTraining, "OcrTrainingService", "Spring Service", "Orchestrates model training: exports training data as ZIP, calls Python /train or /segtrain, persists training metrics in OcrTrainingRunRepository.")
|
|
}
|
|
|
|
Rel(frontend, ocrCtrl, "OCR trigger, status, and progress requests", "HTTP / JSON / SSE")
|
|
Rel(ocrCtrl, ocrSvc, "Single-document jobs", "")
|
|
Rel(ocrCtrl, ocrBatch, "Batch jobs", "")
|
|
Rel(ocrCtrl, ocrTraining, "Training runs", "")
|
|
Rel(ocrSvc, ocrAsync, "Delegates async execution", "")
|
|
Rel(ocrBatch, ocrAsync, "Delegates async execution", "")
|
|
Rel(ocrAsync, ocrClient, "Streams OCR results page by page", "HTTP / NDJSON")
|
|
Rel(ocrTraining, ocrClient, "Sends training data ZIP", "HTTP / multipart")
|
|
Rel(ocrClient, ocrPy, "POST /ocr/stream, /train, /segtrain, /train-sender", "HTTP / REST")
|
|
Rel(ocrAsync, db, "Reads / writes job state, transcription blocks, annotations", "JDBC")
|
|
Rel(ocrAsync, minio, "Generates presigned URLs for PDF fetch", "S3 API")
|
|
Rel(ocrPy, minio, "Fetches PDF via presigned URL", "HTTP / S3 presigned")
|
|
Rel(ocrTraining, db, "Persists training run metrics", "JDBC")
|
|
```
|
|
|
|
### 3e — Supporting Domains
|
|
|
|
Audit logging, dashboard stats, SSE notifications, stories (Geschichten), and cross-cutting exception handling.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: API Backend — Supporting Domains
|
|
|
|
Container(frontend, "Web Frontend", "SvelteKit")
|
|
ContainerDb(db, "PostgreSQL")
|
|
|
|
System_Boundary(backend, "API Backend (Spring Boot)") {
|
|
Component(auditSvc, "AuditService", "Spring Service — @Async", "Writes audit log entries asynchronously via a dedicated TaskExecutor, with transaction-aware logging to prevent deadlocks on concurrent saves.")
|
|
Component(auditQuery, "AuditLogQueryService", "Spring Service", "Queries audit logs for activity feeds, pulse stats, recent contributors, and per-document history. Facade over AuditLogRepository.")
|
|
|
|
Component(dashCtrl, "DashboardController", "Spring MVC — /api/dashboard", "REST endpoints for the user dashboard: recent document resume (/resume), weekly transcription pulse stats (/pulse), and activity feed (/activity) with kind filtering and pagination.")
|
|
Component(statsCtrl, "StatsController", "Spring MVC — /api/stats", "Returns aggregate counts (total persons, total documents) for the UI stats bar.")
|
|
Component(statsSvc, "StatsService", "Spring Service", "Queries aggregate counts: total persons and total documents.")
|
|
Component(dashSvc, "DashboardService", "Spring Service", "Assembles the user dashboard: recent document resume, weekly transcription pulse stats, and activity feed with contributor avatars.")
|
|
|
|
Component(notifCtrl, "NotificationController", "Spring MVC — /api/notifications", "REST and SSE endpoints for notification stream, history with filtering, read/unread state, and per-user preference management.")
|
|
Component(notifSvc, "NotificationService", "Spring Service", "Creates REPLY and MENTION notifications, optionally sends email, marks as read, and pushes events to connected clients via SseEmitterRegistry.")
|
|
Component(sseRegistry, "SseEmitterRegistry", "Spring Component", "In-memory ConcurrentHashMap of Spring SseEmitter instances per user. Handles registration, deregistration, and JSON event broadcasts.")
|
|
|
|
Component(geschCtrl, "GeschichteController", "Spring MVC — /api/geschichten", "CRUD for publishable stories that link persons and documents. Requires BLOG_WRITE permission for write operations.")
|
|
Component(geschSvc, "GeschichteService", "Spring Service", "Manages story lifecycle (DRAFT → PUBLISHED with timestamp). Sanitizes HTML body with an allowlist policy.")
|
|
|
|
Component(exHandler, "GlobalExceptionHandler", "Spring @RestControllerAdvice", "Converts DomainException, validation errors, and generic exceptions to ErrorResponse JSON with machine-readable ErrorCode and HTTP status.")
|
|
}
|
|
|
|
Rel(frontend, dashCtrl, "Dashboard requests", "HTTP / JSON")
|
|
Rel(frontend, statsCtrl, "GET /api/stats", "HTTP / JSON")
|
|
Rel(frontend, notifCtrl, "Notification stream and history", "HTTP / JSON / SSE")
|
|
Rel(frontend, geschCtrl, "Story requests", "HTTP / JSON")
|
|
Rel(dashCtrl, dashSvc, "Delegates to", "")
|
|
Rel(statsCtrl, statsSvc, "Delegates to", "")
|
|
Rel(statsSvc, db, "Reads aggregate counts", "JDBC")
|
|
Rel(dashSvc, auditQuery, "Fetches activity feed and pulse stats", "")
|
|
Rel(notifCtrl, notifSvc, "Delegates to", "")
|
|
Rel(notifCtrl, sseRegistry, "Registers client SSE connection", "")
|
|
Rel(notifSvc, sseRegistry, "Broadcasts events to connected clients", "")
|
|
Rel(geschCtrl, geschSvc, "Delegates to", "")
|
|
Rel(auditSvc, db, "Writes audit_log", "JDBC")
|
|
Rel(auditQuery, db, "Reads audit_log", "JDBC")
|
|
Rel(notifSvc, db, "Reads / writes notifications", "JDBC")
|
|
Rel(geschSvc, db, "Reads / writes geschichten", "JDBC")
|
|
```
|
|
|
|
---
|
|
|
|
## Level 3 — Components: Web Frontend
|
|
|
|
The internal structure of the SvelteKit frontend, split into four focused views.
|
|
|
|
### 3a — Middleware, Auth & Layout
|
|
|
|
Per-request middleware: session validation, i18n, auth cookie handling, and auth pages.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: Web Frontend — Middleware, Auth & Layout
|
|
|
|
Person(user, "User")
|
|
Container(backend, "API Backend", "Spring Boot")
|
|
|
|
System_Boundary(frontend, "Web Frontend (SvelteKit / SSR)") {
|
|
Component(hooks, "hooks.server.ts", "SvelteKit Server Hook", "Four handle layers: (1) handleAuth — redirects unauthenticated users to /login; (2) userGroup — reads auth_token cookie, fetches /api/users/me, stores user in event.locals; (3) handleFetch — injects Authorization header on all outgoing /api/ calls; (4) handleLocaleDetection — sets language cookie from Accept-Language header.")
|
|
Component(i18n, "hooks.ts (Paraglide)", "SvelteKit Client Hook", "Client-side i18n middleware. Detects language from URL and sets the active locale for Paraglide.js translation functions.")
|
|
Component(layout, "+layout.server.ts", "SvelteKit Layout Loader", "Passes event.locals.user down to all child pages so every route has access to the authenticated user.")
|
|
Component(loginPage, "/login", "SvelteKit Route", "Form action: encodes username:password as Base64 Basic Auth token, POSTs to /api/users/me to validate, sets auth_token httpOnly cookie (SameSite=strict, maxAge=86400), redirects to /.")
|
|
Component(logoutPage, "/logout", "SvelteKit Route (server-only)", "Clears the auth_token cookie and redirects to /login.")
|
|
Component(registerPage, "/register", "SvelteKit Route", "Loader validates invite code via GET /api/auth/invite/{code}. Form action: POST /api/auth/register to create the user account.")
|
|
Component(forgotPw, "/forgot-password", "SvelteKit Route", "Form action: POST /api/auth/forgot-password. Always responds with success to prevent email enumeration.")
|
|
Component(resetPw, "/reset-password", "SvelteKit Route", "Form action: POST /api/auth/reset-password with the token from the query string.")
|
|
}
|
|
|
|
Rel(user, hooks, "Every browser request", "HTTPS")
|
|
Rel(hooks, backend, "GET /api/users/me (session check)", "HTTP / Basic Auth")
|
|
Rel(hooks, loginPage, "Redirect if no token", "")
|
|
Rel(hooks, layout, "Stores authenticated user in event.locals", "")
|
|
Rel(loginPage, backend, "POST /api/users/me (auth check)", "HTTP / Basic Auth")
|
|
Rel(registerPage, backend, "GET /api/auth/invite/{code}, POST /api/auth/register", "HTTP / JSON")
|
|
Rel(forgotPw, backend, "POST /api/auth/forgot-password", "HTTP / JSON")
|
|
Rel(resetPw, backend, "POST /api/auth/reset-password", "HTTP / JSON")
|
|
```
|
|
|
|
### 3b — Document Workflows
|
|
|
|
Document search, viewing, editing, enrichment, and the shared components that support them.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: Web Frontend — Document Workflows
|
|
|
|
Person(user, "User")
|
|
Container(backend, "API Backend", "Spring Boot")
|
|
|
|
System_Boundary(frontend, "Web Frontend (SvelteKit / SSR)") {
|
|
Component(homePage, "/ (Home / Search)", "SvelteKit Route", "Loader: parses URL params (q, from, to, senderId, receiverId, tags), fetches /api/documents/search and /api/persons. Renders search form with full-text, date range, sender/receiver typeahead, and tag filters.")
|
|
Component(docDetail, "/documents/[id]", "SvelteKit Route", "Loader: GET /api/documents/{id}. Page: metadata panel, inline file viewer, transcription editor, annotation layer, and comment thread.")
|
|
Component(docEdit, "/documents/[id]/edit", "SvelteKit Route", "Edit form with PersonTypeahead, TagInput, date/location fields. Form action: PUT /api/documents/{id}.")
|
|
Component(docNew, "/documents/new", "SvelteKit Route", "Upload form for a new document. Loader: GET /api/persons. Form action: POST /api/documents with multipart file.")
|
|
Component(docBulkEdit, "/documents/bulk-edit", "SvelteKit Route", "Multi-document metadata editor. Loader: GET /api/documents/incomplete. Requires WRITE_ALL (redirects otherwise). Action: PATCH /api/documents/batch.")
|
|
Component(enrichPage, "/enrich/[id]", "SvelteKit Route", "Guided enrichment workflow. Loader: GET /api/documents/{id}. Progressively saves annotations and transcription blocks.")
|
|
|
|
Component(apiPersons, "/api/persons (SvelteKit API)", "SvelteKit Server Route", "Proxies GET /api/persons?q=... to backend for PersonTypeahead suggestions.")
|
|
Component(apiTags, "/api/tags (SvelteKit API)", "SvelteKit Server Route", "Proxies GET /api/tags to backend for TagInput autocomplete.")
|
|
Component(typeahead, "PersonTypeahead.svelte", "Svelte Component", "Async autocomplete for selecting a person. Debounces input, calls /api/persons?q=.")
|
|
Component(tagInput, "TagInput.svelte", "Svelte Component", "Multi-tag input. Supports free-text entry and selecting existing tags from /api/tags.")
|
|
}
|
|
|
|
Rel(user, homePage, "Searches and browses", "HTTPS / Browser")
|
|
Rel(homePage, backend, "GET /api/documents/search, GET /api/persons", "HTTP / JSON")
|
|
Rel(docDetail, backend, "GET /api/documents/{id}, GET /api/documents/{id}/file", "HTTP / JSON + Binary")
|
|
Rel(docEdit, backend, "PUT /api/documents/{id}", "HTTP / Multipart")
|
|
Rel(docNew, backend, "GET /api/persons, POST /api/documents", "HTTP / JSON + Multipart")
|
|
Rel(docBulkEdit, backend, "GET /api/documents/incomplete, PATCH /api/documents/batch", "HTTP / JSON")
|
|
Rel(enrichPage, backend, "GET/POST /api/transcription, POST /api/documents/{id}/annotations", "HTTP / JSON")
|
|
Rel(homePage, typeahead, "Uses for sender/receiver filter", "")
|
|
Rel(docEdit, typeahead, "Uses for sender/receiver selection", "")
|
|
Rel(docNew, typeahead, "Uses for sender selection", "")
|
|
Rel(docEdit, tagInput, "Uses for tag management", "")
|
|
Rel(typeahead, apiPersons, "Fetches suggestions", "HTTP")
|
|
Rel(tagInput, apiTags, "Fetches existing tags", "HTTP")
|
|
Rel(apiPersons, backend, "GET /api/persons", "HTTP / JSON")
|
|
Rel(apiTags, backend, "GET /api/tags", "HTTP / JSON")
|
|
```
|
|
|
|
### 3c — People, Stories & Discovery
|
|
|
|
Person directory, bilateral conversations, activity feed, stories, family tree, and user profiles.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: Web Frontend — People, Stories & Discovery
|
|
|
|
Person(user, "User")
|
|
Container(backend, "API Backend", "Spring Boot")
|
|
|
|
System_Boundary(frontend, "Web Frontend (SvelteKit / SSR)") {
|
|
Component(personsPage, "/persons and /persons/[id]", "SvelteKit Routes", "Person directory and detail. Detail: metadata, document list sent/received, correspondents, explicit and inferred family relationships.")
|
|
Component(personEdit, "/persons/[id]/edit and /persons/new", "SvelteKit Routes", "Create and edit person forms. Edit: metadata, aliases, explicit relationships. Actions: PUT/POST /api/persons.")
|
|
Component(briefwechsel, "/briefwechsel", "SvelteKit Route", "Bilateral conversation timeline. Selects two persons via PersonTypeahead, fetches GET /api/documents/conversation, displays chronological exchange.")
|
|
Component(aktivitaeten, "/aktivitaeten", "SvelteKit Route", "Unified activity feed (Chronik). Loader: GET /api/dashboard/activity and GET /api/notifications?read=false.")
|
|
Component(geschichten, "/geschichten and /geschichten/[id]", "SvelteKit Routes", "Story list and detail pages. Loader: GET /api/geschichten?status=PUBLISHED.")
|
|
Component(geschichtenEdit, "/geschichten/[id]/edit and /geschichten/new", "SvelteKit Routes", "Story editor with rich text, person and document linking. Actions: PUT/POST /api/geschichten. Requires BLOG_WRITE permission.")
|
|
Component(stammbaum, "/stammbaum", "SvelteKit Route", "Family tree visualisation. Loader: GET /api/network (nodes + edges). Renders interactive D3-based Stammbaum.")
|
|
Component(profilePage, "/profile", "SvelteKit Route", "Current user profile settings. Loader: GET /api/users/me/notification-preferences. Actions: update name/password and notification preferences.")
|
|
Component(userProfile, "/users/[id]", "SvelteKit Route", "Public user profile view. Loader: GET /api/users/{id}.")
|
|
}
|
|
|
|
Rel(user, personsPage, "Browses family members", "HTTPS / Browser")
|
|
Rel(personsPage, backend, "GET /api/persons, GET /api/persons/{id}", "HTTP / JSON")
|
|
Rel(personEdit, backend, "GET /api/persons/{id}, PUT /api/persons/{id}, POST /api/persons", "HTTP / JSON")
|
|
Rel(briefwechsel, backend, "GET /api/documents/conversation", "HTTP / JSON")
|
|
Rel(aktivitaeten, backend, "GET /api/dashboard/activity, GET /api/notifications", "HTTP / JSON")
|
|
Rel(geschichten, backend, "GET /api/geschichten", "HTTP / JSON")
|
|
Rel(geschichtenEdit, backend, "GET/PUT/POST /api/geschichten", "HTTP / JSON")
|
|
Rel(stammbaum, backend, "GET /api/network", "HTTP / JSON")
|
|
Rel(profilePage, backend, "GET/PUT /api/users/me, notification-preferences", "HTTP / JSON")
|
|
Rel(userProfile, backend, "GET /api/users/{id}", "HTTP / JSON")
|
|
```
|
|
|
|
### 3d — Administration & Help
|
|
|
|
Admin panel sub-routes and the transcription help guide.
|
|
|
|
```mermaid
|
|
C4Component
|
|
title Component Diagram: Web Frontend — Administration & Help
|
|
|
|
Person(admin, "Administrator")
|
|
Container(backend, "API Backend", "Spring Boot")
|
|
|
|
System_Boundary(frontend, "Web Frontend (SvelteKit / SSR)") {
|
|
Component(adminUsers, "/admin/users, /admin/users/[id], /admin/users/new, /admin/invites", "SvelteKit Routes", "User directory, create/update/delete users, and manage invite codes. Requires ADMIN_USER permission.")
|
|
Component(adminGroups, "/admin/groups, /admin/groups/[id], /admin/groups/new", "SvelteKit Routes", "Permission group management: create/edit groups and their permission sets.")
|
|
Component(adminTags, "/admin/tags and /admin/tags/[id]", "SvelteKit Routes", "Tag administration: edit tag hierarchy, merge tags, delete subtrees.")
|
|
Component(adminOcr, "/admin/ocr and /admin/ocr/[personId]", "SvelteKit Routes", "Global and per-person OCR configuration. Manages script types and triggers sender model training.")
|
|
Component(adminSystem, "/admin/system", "SvelteKit Route", "System status panel. Triggers Excel/ODS mass import (POST /api/admin/trigger-import). Displays import state.")
|
|
Component(hilfe, "/hilfe/transkription", "SvelteKit Route", "Static transcription style guide for Kurrent and Sütterlin character recognition. No backend calls.")
|
|
}
|
|
|
|
Rel(admin, adminUsers, "Manages users and invites", "HTTPS / Browser")
|
|
Rel(adminUsers, backend, "GET/POST/DELETE /api/users, POST /api/auth/invite", "HTTP / JSON")
|
|
Rel(adminGroups, backend, "GET/POST/PUT/DELETE /api/groups", "HTTP / JSON")
|
|
Rel(adminTags, backend, "GET/PUT/DELETE /api/tags", "HTTP / JSON")
|
|
Rel(adminOcr, backend, "GET/POST /api/ocr (global config and sender training)", "HTTP / JSON")
|
|
Rel(adminSystem, backend, "POST /api/admin/trigger-import, GET /api/admin/import-status", "HTTP / JSON")
|
|
```
|
|
|
|
---
|
|
|
|
## Authentication Flow
|
|
|
|
How a user session is established and maintained across requests.
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
actor User
|
|
participant Browser
|
|
participant Frontend as Frontend (SvelteKit)
|
|
participant Backend as Backend (Spring Boot)
|
|
participant DB as PostgreSQL
|
|
|
|
User->>Browser: Enter username + password
|
|
Browser->>Frontend: POST /login (form action)
|
|
Frontend->>Frontend: Base64 encode "user:password"
|
|
Frontend->>Backend: GET /api/users/me<br/>Authorization: Basic <token>
|
|
Backend->>Backend: Spring Security parses Basic Auth
|
|
Backend->>DB: SELECT user WHERE username=?
|
|
DB-->>Backend: AppUser + groups + permissions
|
|
Backend->>Backend: BCrypt.matches(password, hash)
|
|
Backend-->>Frontend: 200 OK — UserDTO
|
|
Frontend->>Browser: Set-Cookie: auth_token=<base64><br/>(httpOnly, SameSite=strict, maxAge=86400)
|
|
Browser->>Frontend: GET / (next request)
|
|
Frontend->>Frontend: hooks.server.ts reads auth_token cookie
|
|
Frontend->>Backend: GET /api/users/me<br/>Authorization: Basic <token>
|
|
Backend-->>Frontend: 200 OK — user in event.locals
|
|
Frontend-->>Browser: Render page with user context
|
|
```
|
|
|
|
---
|
|
|
|
## Document Upload Flow
|
|
|
|
How a document file moves from a user's browser to MinIO.
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
actor User
|
|
participant Frontend as Frontend (SvelteKit)
|
|
participant Backend as Backend (Spring Boot)
|
|
participant Aspect as PermissionAspect (AOP)
|
|
participant DocSvc as DocumentService
|
|
participant FileSvc as FileService
|
|
participant MinIO
|
|
participant DB as PostgreSQL
|
|
|
|
User->>Frontend: Submit edit form (file + metadata)
|
|
Frontend->>Backend: PUT /api/documents/{id}<br/>multipart/form-data + Authorization header
|
|
Backend->>Aspect: @RequirePermission(WRITE_ALL) check
|
|
Aspect->>Aspect: Verify user has WRITE_ALL authority
|
|
Aspect-->>Backend: Proceed
|
|
Backend->>DocSvc: updateDocument(id, dto, file)
|
|
DocSvc->>DocSvc: Resolve sender Person by ID
|
|
DocSvc->>DocSvc: Resolve/create Tags
|
|
DocSvc->>FileSvc: uploadFile(file, filename)
|
|
FileSvc->>FileSvc: Generate key: documents/{UUID}_{filename}
|
|
FileSvc->>MinIO: PutObject(bucket, key, stream)
|
|
MinIO-->>FileSvc: Success
|
|
FileSvc-->>DocSvc: S3 key
|
|
DocSvc->>DB: UPDATE documents SET file_path=?, status='UPLOADED', ...
|
|
DB-->>DocSvc: OK
|
|
DocSvc-->>Backend: Updated Document entity
|
|
Backend-->>Frontend: 200 OK — Document JSON
|
|
Frontend-->>User: Refreshed document view
|
|
```
|