# 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(personRepo, "PersonRepository", "Spring Data JPA", "See diagram 3c.2. Used here by DocumentService to resolve sender / receiver persons.") Component(tagRepo, "TagRepository", "Spring Data JPA", "See diagram 3c. Used here by DocumentService to find or create tags.") 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, personRepo, "Resolves sender / receivers", "") Rel(docSvc, tagRepo, "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") Rel(personRepo, db, "SQL queries", "JDBC") Rel(tagRepo, 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", "Exposes segmentation, transcription, and review queue projections for the mission-control enrichment workflow.") 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.") } 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, db, "Reads / writes blocks and versions", "JDBC") Rel(annotationSvc, db, "Reads / writes annotations", "JDBC") Rel(commentSvc, db, "Reads / writes comments", "JDBC") Rel(transcriptionQueueSvc, db, "Queue projection queries", "JDBC") ``` ### 3c — People, Users & Group Administration Person, user, and group management, including startup seed data. ```mermaid C4Component title Component Diagram: API Backend — People, Users & Group Administration 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. Also returns all documents sent by a person.") Component(userCtrl, "UserController", "Spring MVC — /api/users", "Returns current user (/me). Creates and deletes users (requires ADMIN_USER permission).") Component(groupCtrl, "GroupController", "Spring MVC — /api/groups", "Lists and manages permission groups.") Component(tagCtrl, "TagController", "Spring MVC — /api/tags", "Lists tags for typeahead.") Component(userSvc, "UserService", "Spring Service", "User CRUD. Encodes passwords with BCrypt. Assigns users to permission groups.") Component(dataInit, "DataInitializer", "CommandLineRunner", "On startup: creates default admin user and groups if none exist. Seeds test data (persons, documents) if DB is empty.") Component(personRepo, "PersonRepository", "Spring Data JPA", "Lists all persons sorted by last name. Supports name search for typeahead.") Component(userRepo, "AppUserRepository", "Spring Data JPA", "Finds users by username. Used by Spring Security and UserService.") Component(groupRepo, "UserGroupRepository", "Spring Data JPA", "Manages permission groups.") Component(tagRepo, "TagRepository", "Spring Data JPA", "Finds or creates tags by name (case-insensitive).") } Rel(frontend, personCtrl, "Person requests", "HTTP / JSON") Rel(frontend, userCtrl, "User requests", "HTTP / JSON") Rel(frontend, groupCtrl, "Group requests", "HTTP / JSON") Rel(frontend, tagCtrl, "Tag requests", "HTTP / JSON") Rel(personCtrl, personRepo, "Reads persons", "") Rel(userCtrl, userSvc, "Delegates to", "") Rel(groupCtrl, groupRepo, "Reads / writes groups", "") Rel(tagCtrl, tagRepo, "Lists tags", "") Rel(userSvc, userRepo, "Reads / writes users", "") Rel(userSvc, groupRepo, "Assigns groups", "") Rel(dataInit, db, "Seeds initial data", "JDBC") Rel(personRepo, db, "SQL queries", "JDBC") Rel(userRepo, db, "SQL queries", "JDBC") Rel(groupRepo, db, "SQL queries", "JDBC") Rel(tagRepo, db, "SQL queries", "JDBC") ``` --- ## Level 3 — Components: Web Frontend The internal structure of the SvelteKit frontend, split into two focused views. ### 3a — Middleware, Auth & Layout Per-request middleware: session validation, i18n, and auth cookie handling. ```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", "Two responsibilities: (1) userGroup handle — reads auth_token cookie, fetches /api/users/me, stores user in event.locals. (2) handleFetch — intercepts all outgoing fetch() calls, injects Authorization header from cookie. Redirects to /login if token absent.") 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.") } 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") ``` ### 3b — Pages & Shared Components Application pages and reusable UI components. ```mermaid C4Component title Component Diagram: Web Frontend — Pages & Shared Components 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 search params (q, from, to, senderId, receiverId, tags), fetches /api/documents/search and /api/persons, returns results. Page: renders search form with full-text, date range, sender/receiver typeahead, tag filters. Displays paginated document list.") Component(docDetail, "/documents/[id]", "SvelteKit Route", "Loader: fetches /api/documents/{id}. Handles 401 redirect to login, 404 error. Page: shows document metadata, file viewer (PDF/image inline), transcription, tags.") Component(docEdit, "/documents/[id]/edit", "SvelteKit Route", "Form with PersonTypeahead for sender/receiver, TagInput for tags, date/location fields. Submits PUT to /api/documents/{id}.") Component(persons, "/persons and /persons/[id]", "SvelteKit Routes", "Lists all persons. Detail page shows person metadata and all documents they sent.") Component(conversations, "/conversations", "SvelteKit Route", "Selects two persons via PersonTypeahead, fetches /api/documents/conversation, displays chronological exchange.") Component(adminPage, "/admin", "SvelteKit Route", "User management UI (create/delete users). Excel import trigger button (calls /api/admin/trigger-import).") Component(apiPersons, "/api/persons (SvelteKit API)", "SvelteKit Server Route", "Proxies GET /api/persons?q=... to backend. Used by PersonTypeahead for typeahead suggestions.") Component(apiTags, "/api/tags (SvelteKit API)", "SvelteKit Server Route", "Proxies GET /api/tags to backend. Used by TagInput for 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", "HTTP / JSON") Rel(homePage, backend, "GET /api/persons", "HTTP / JSON") Rel(docDetail, backend, "GET /api/documents/{id}", "HTTP / JSON") Rel(docDetail, backend, "GET /api/documents/{id}/file", "HTTP / Binary stream") Rel(docEdit, backend, "PUT /api/documents/{id}", "HTTP / Multipart") Rel(conversations, backend, "GET /api/documents/conversation", "HTTP / JSON") Rel(adminPage, backend, "GET/POST/DELETE /api/users", "HTTP / JSON") Rel(adminPage, backend, "POST /api/admin/trigger-import", "HTTP / JSON") Rel(homePage, typeahead, "Uses for sender/receiver filter", "") Rel(docEdit, typeahead, "Uses for sender/receiver 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") ``` --- ## 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
Authorization: Basic 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=
(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
Authorization: Basic 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}
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 ```