Files
familienarchiv/docs/architecture/db/db-orm.puml
Marcel d4b5c14a26 docs(db): add full ORM diagram (db-orm.puml)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 23:44:00 +02:00

433 lines
11 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-orm
' Schema source: Flyway V1V60 (excl. V37, V43 — intentionally removed)
' Schema as of: V60 (2026-05-06)
' ⚠ This is a versioned snapshot. Update when the schema changes significantly.
hide circle
skinparam linetype ortho
' ── Auth ──
package "Auth" {
entity app_users {
id : UUID <<PK>>
--
email : VARCHAR(255) NOT NULL UNIQUE
password : VARCHAR(255) NOT NULL
first_name : VARCHAR(100)
last_name : VARCHAR(100)
birth_date : DATE
contact : TEXT
enabled : BOOLEAN NOT NULL
color : VARCHAR(20) NOT NULL
notify_on_reply : BOOLEAN NOT NULL
notify_on_mention : BOOLEAN NOT NULL
created_at : TIMESTAMP
}
entity user_groups {
id : UUID <<PK>>
--
name : VARCHAR(255) NOT NULL UNIQUE
}
entity app_users_groups {
app_user_id : UUID <<FK>>
group_id : UUID <<FK>>
}
entity group_permissions {
group_id : UUID <<FK>>
--
permission : VARCHAR(255)
}
entity password_reset_tokens {
id : UUID <<PK>>
--
app_user_id : UUID <<FK>>
token : VARCHAR(64) NOT NULL UNIQUE
expires_at : TIMESTAMP NOT NULL
used : BOOLEAN NOT NULL
created_at : TIMESTAMP NOT NULL
}
entity invite_tokens {
id : UUID <<PK>>
--
code : VARCHAR(10) NOT NULL UNIQUE
label : VARCHAR(255)
max_uses : INTEGER
use_count : INTEGER NOT NULL
prefill_first_name : VARCHAR(255)
prefill_last_name : VARCHAR(255)
prefill_email : VARCHAR(255)
expires_at : TIMESTAMP
created_by : UUID <<FK>>
created_at : TIMESTAMP NOT NULL
revoked : BOOLEAN NOT NULL
}
entity invite_token_group_ids {
invite_token_id : UUID <<FK>>
group_id : UUID <<FK>>
}
}
' ── Documents ──
package "Documents" {
entity documents {
id : UUID <<PK>>
--
title : VARCHAR(255) NOT NULL
original_filename : VARCHAR(255) NOT NULL
status : VARCHAR(255) NOT NULL
file_path : VARCHAR(255)
file_hash : VARCHAR(64)
summary : TEXT
transcription : TEXT
meta_date : DATE
meta_location : VARCHAR(255)
meta_document_location : VARCHAR(255)
archive_box : VARCHAR(255)
archive_folder : VARCHAR(255)
sender_id : UUID <<FK>>
metadata_complete : BOOLEAN NOT NULL
script_type : VARCHAR(30) NOT NULL
thumbnail_key : VARCHAR(255)
thumbnail_generated_at : TIMESTAMP
thumbnail_aspect : VARCHAR(16)
page_count : INTEGER
search_vector : tsvector <<computed>>
created_at : TIMESTAMP
updated_at : TIMESTAMP
}
entity document_receivers {
document_id : UUID <<FK>>
person_id : UUID <<FK>>
}
entity document_tags {
document_id : UUID <<FK>>
tag_id : UUID <<FK>>
}
entity document_versions {
id : UUID <<PK>>
--
document_id : UUID <<FK>>
editor_id : UUID <<FK>>
editor_name : VARCHAR(200) NOT NULL
saved_at : TIMESTAMP NOT NULL
snapshot : JSONB NOT NULL
changed_fields : JSONB NOT NULL
}
entity document_annotations {
id : UUID <<PK>>
--
document_id : UUID <<FK>>
page_number : INTEGER NOT NULL
x : DOUBLE PRECISION NOT NULL
y : DOUBLE PRECISION NOT NULL
width : DOUBLE PRECISION NOT NULL
height : DOUBLE PRECISION NOT NULL
color : VARCHAR(20) NOT NULL
polygon : JSONB
file_hash : VARCHAR(64)
created_by : UUID <<FK>>
created_at : TIMESTAMP NOT NULL
}
entity document_comments {
id : UUID <<PK>>
--
document_id : UUID <<FK>>
annotation_id : UUID <<FK>>
block_id : UUID <<FK>>
parent_id : UUID <<FK>>
author_id : UUID <<FK>>
author_name : VARCHAR(200) NOT NULL
content : TEXT NOT NULL
created_at : TIMESTAMP NOT NULL
updated_at : TIMESTAMP NOT NULL
}
entity document_training_labels {
document_id : UUID <<FK>>
--
label : VARCHAR(50) NOT NULL
}
entity comment_mentions {
comment_id : UUID <<FK>>
app_user_id : UUID <<FK>>
}
}
' ── Persons ──
package "Persons" {
entity persons {
id : UUID <<PK>>
--
first_name : VARCHAR(255)
last_name : VARCHAR(255) NOT NULL
alias : VARCHAR(255)
title : VARCHAR(50)
person_type : VARCHAR(20) NOT NULL
notes : TEXT
birth_year : INTEGER
death_year : INTEGER
family_member : BOOLEAN NOT NULL
}
entity person_name_aliases {
id : UUID <<PK>>
--
person_id : UUID <<FK>>
last_name : VARCHAR(255) NOT NULL
first_name : VARCHAR(255)
type : VARCHAR(50) NOT NULL
sort_order : INTEGER NOT NULL
created_at : TIMESTAMPTZ
}
entity person_relationships {
id : UUID <<PK>>
--
person_id : UUID <<FK>>
related_person_id : UUID <<FK>>
relation_type : VARCHAR(30) NOT NULL
from_year : INTEGER
to_year : INTEGER
notes : VARCHAR(2000)
created_at : TIMESTAMPTZ NOT NULL
}
}
' ── Tags ──
package "Tags" {
entity tag {
id : UUID <<PK>>
--
name : VARCHAR(255) NOT NULL UNIQUE
parent_id : UUID <<FK>>
color : VARCHAR(20)
}
}
' ── Transcription ──
package "Transcription" {
entity transcription_blocks {
id : UUID <<PK>>
--
annotation_id : UUID <<FK>>
document_id : UUID <<FK>>
text : TEXT
label : VARCHAR(200)
sort_order : INTEGER NOT NULL
version : INTEGER NOT NULL
source : VARCHAR(10) NOT NULL
reviewed : BOOLEAN NOT NULL
created_by : UUID <<FK>>
updated_by : UUID <<FK>>
created_at : TIMESTAMP NOT NULL
updated_at : TIMESTAMP NOT NULL
}
entity transcription_block_versions {
id : UUID <<PK>>
--
block_id : UUID <<FK>>
text : TEXT NOT NULL
changed_by : UUID <<FK>>
changed_at : TIMESTAMP NOT NULL
}
entity transcription_block_mentioned_persons {
block_id : UUID <<FK>>
person_id : UUID NOT NULL
--
display_name : VARCHAR(200) NOT NULL
}
}
' ── OCR ──
package "OCR" {
entity ocr_jobs {
id : UUID <<PK>>
--
status : VARCHAR(20) NOT NULL
total_documents : INT NOT NULL
processed_documents : INT NOT NULL
error_count : INT NOT NULL
skipped_count : INT NOT NULL
created_by : UUID
progress_message : TEXT
created_at : TIMESTAMPTZ NOT NULL
updated_at : TIMESTAMPTZ NOT NULL
}
entity ocr_job_documents {
id : UUID <<PK>>
--
job_id : UUID <<FK>>
document_id : UUID <<FK>>
status : VARCHAR(20) NOT NULL
error_message : TEXT
current_page : INT
total_pages : INT
created_at : TIMESTAMPTZ NOT NULL
updated_at : TIMESTAMPTZ NOT NULL
}
entity ocr_training_runs {
id : UUID <<PK>>
--
status : VARCHAR(20) NOT NULL
block_count : INT NOT NULL
document_count : INT NOT NULL
model_name : VARCHAR(100) NOT NULL
error_message : TEXT
triggered_by : UUID <<FK>>
person_id : UUID <<FK>>
cer : DOUBLE PRECISION
loss : DOUBLE PRECISION
accuracy : DOUBLE PRECISION
epochs : INT
created_at : TIMESTAMPTZ NOT NULL
completed_at : TIMESTAMPTZ
}
entity sender_models {
id : UUID <<PK>>
--
person_id : UUID <<FK>> UNIQUE
model_path : TEXT NOT NULL
accuracy : DOUBLE PRECISION
cer : DOUBLE PRECISION
corrected_lines_at_training : INT NOT NULL
created_at : TIMESTAMPTZ NOT NULL
updated_at : TIMESTAMPTZ NOT NULL
}
}
' ── Supporting ──
package "Supporting" {
entity notifications {
id : UUID <<PK>>
--
recipient_id : UUID <<FK>>
type : VARCHAR(32) NOT NULL
document_id : UUID
reference_id : UUID
annotation_id : UUID
actor_name : VARCHAR(255)
read : BOOLEAN NOT NULL
created_at : TIMESTAMP NOT NULL
}
entity audit_log {
id : UUID <<PK>>
--
happened_at : TIMESTAMPTZ NOT NULL
actor_id : UUID <<FK>>
kind : VARCHAR(50) NOT NULL
document_id : UUID <<FK>>
payload : JSONB
}
entity geschichten {
id : UUID <<PK>>
--
title : VARCHAR(255) NOT NULL
body : TEXT
status : VARCHAR(32) NOT NULL
author_id : UUID <<FK>>
created_at : TIMESTAMP NOT NULL
updated_at : TIMESTAMP NOT NULL
published_at : TIMESTAMP
}
entity geschichten_persons {
geschichte_id : UUID <<FK>>
person_id : UUID <<FK>>
}
entity geschichten_documents {
geschichte_id : UUID <<FK>>
document_id : UUID <<FK>>
}
}
' 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
document_receivers }o--|| documents : document_id
document_receivers }o--|| persons : person_id
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
' 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
geschichten_documents }o--|| geschichten : geschichte_id
geschichten_documents }o--|| documents : document_id
@enduml