Files
familienarchiv/docs/architecture/db/db-relationships.puml
Marcel b9bed19610 feat(geschichte): bound title and JOURNEY intro at all three layers
Title: requireTitle() turns the VARCHAR(255) DB bound into a friendly 400
(GESCHICHTE_TITLE_TOO_LONG). JOURNEY intro: MAX_INTRO_LENGTH = 4000 in
bodyForType() plus a V75 CHECK as atomic backstop (defensive clamp first,
STORY bodies exempt) — GESCHICHTE_INTRO_TOO_LONG. Both codes wired through
ErrorCode.java, errors.ts, getErrorMessage and de/en/es. DB-layer boundary
pins added: exactly-2000 note insert (V74 CHECK) and 4000/4001 intro
inserts against real Postgres. Docs: error-code lists, db puml diagrams.

The matching maxlength attributes land with the component commits.

Review round 3: Markus (b), Nora (1), Sara (DB 2000 boundary).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 08:28:08 +02:00

138 lines
4.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@startuml db-relationships
' Schema source: Flyway V1V69 (excl. V37, V43 — intentionally removed)
' Schema as of: V69 (2026-05-27)
' ⚠ This is a versioned snapshot. Update when the schema changes significantly.
' Note: V69 adds columns only (persons.source_ref, tag.source_ref, document
' precision/attribution fields); no new FK relationships, so this diagram is unchanged.
hide circle
skinparam linetype ortho
left to right direction
' ── Auth ──
package "Auth" {
entity app_users
entity user_groups
entity app_users_groups
entity group_permissions
entity password_reset_tokens
entity invite_tokens
entity invite_token_group_ids
}
' ── Documents ──
package "Documents" {
entity documents
entity document_receivers
entity document_tags
entity document_versions
entity document_annotations
entity document_comments
entity document_training_labels
entity comment_mentions
}
' ── Persons ──
package "Persons" {
entity persons
entity person_name_aliases
entity person_relationships
}
' ── Tags ──
package "Tags" {
entity tag
}
' ── Transcription ──
package "Transcription" {
entity transcription_blocks
entity transcription_block_versions
entity transcription_block_mentioned_persons
}
' ── OCR ──
package "OCR" {
entity ocr_jobs
entity ocr_job_documents
entity ocr_training_runs
entity sender_models
}
' ── Supporting ──
package "Supporting" {
entity notifications
entity audit_log
entity geschichten
entity geschichten_persons
entity journey_items
}
' Auth relationships
app_users_groups }o--|| app_users : app_user_id
app_users_groups }o--|| user_groups : group_id
group_permissions }o--|| user_groups : group_id
password_reset_tokens }o--|| app_users : app_user_id
invite_tokens }o--|| app_users : created_by
invite_token_group_ids }o--|| invite_tokens : invite_token_id
invite_token_group_ids }o--|| user_groups : group_id
' Document relationships
documents }o--o| persons : sender_id (ON DELETE SET NULL)
document_receivers }o--|| documents : document_id
document_receivers }o--|| persons : person_id (ON DELETE CASCADE)
document_tags }o--|| documents : document_id
document_tags }o--|| tag : tag_id
document_versions }o--|| documents : document_id
document_versions }o--o| app_users : editor_id
document_annotations }o--|| documents : document_id
document_annotations }o--o| app_users : created_by
document_comments }o--|| documents : document_id
document_comments }o--o| document_annotations : annotation_id
document_comments }o--o| transcription_blocks : block_id
document_comments }o--o| app_users : author_id
document_comments }o--o| document_comments : parent_id
document_training_labels }o--|| documents : document_id
comment_mentions }o--|| document_comments : comment_id
comment_mentions }o--|| app_users : app_user_id
' Person relationships
person_name_aliases }o--|| persons : person_id
person_relationships }o--|| persons : person_id
person_relationships }o--|| persons : related_person_id
' Tag self-reference
tag }o--o| tag : parent_id
' Transcription relationships
transcription_blocks }o--|| document_annotations : annotation_id
transcription_blocks }o--|| documents : document_id
transcription_blocks }o--o| app_users : created_by
transcription_blocks }o--o| app_users : updated_by
transcription_block_versions }o--|| transcription_blocks : block_id
transcription_block_versions }o--o| app_users : changed_by
transcription_block_mentioned_persons }o--|| transcription_blocks : block_id
transcription_block_mentioned_persons }o--|| persons : person_id (ON DELETE CASCADE)
' OCR relationships
ocr_job_documents }o--|| ocr_jobs : job_id
ocr_job_documents }o--|| documents : document_id
ocr_training_runs }o--o| app_users : triggered_by
ocr_training_runs }o--o| persons : person_id
sender_models ||--|| persons : person_id
' Supporting relationships
notifications }o--|| app_users : recipient_id
audit_log }o--o| app_users : actor_id
audit_log }o--o| documents : document_id
geschichten }o--o| app_users : author_id
geschichten_persons }o--|| geschichten : geschichte_id
geschichten_persons }o--|| persons : person_id
journey_items }o--|| geschichten : geschichte_id (ON DELETE CASCADE)
journey_items }o--o| documents : document_id (ON DELETE SET NULL)
note right of journey_items : partial UNIQUE (geschichte_id, document_id)\nWHERE document_id IS NOT NULL (V74)
note right of geschichten : CHECK length(body) <= 4000\nfor type = JOURNEY (V75)
@enduml