Systemarchitektur: Jotter (arc42)
Dieses Dokument beschreibt die Architektur von Jotter anhand des standardisierten arc42-Templates.
1. Einführung und Ziele
Jotter is eine lokale (Local-First), nicht-kommerzielle Projektmanagement-Software. Sie dient als freie Alternative zu Cloud-basierten Kanban-Boards wie Trello oder MS Planner.
1.1 Anforderungsübersicht
- Anti-Aufgabenflut: Aggressives Filtern von Aufgaben (z. B. Ausblenden der "Erledigt"-Spalte), um einer visuellen Überforderung entgegenzuwirken.
- Dateibasierte Portabilität: Speicherung in menschenlesbaren Markdown-Dateien als primäres Dateiformat, damit Aufgaben auch außerhalb der App nutzbar bleiben.
- Geschwindigkeit: Verzögerungsfreie Drag-and-Drop-Operationen, Filterungen und Suchvorgänge.
1.2 Qualitätsziele
- Datensouveränität & Compliance: Keine Cloud-Synchronisation, rein lokales Arbeiten auf eigenen Speichermedien.
- Robustheit: Der Zustand der indizierten SQLite-Datenbank muss jederzeit vollständig und fehlerfrei aus den Markdown-Dateien rekonstruiert werden können.
- Minimale Latenz: Jede Interaktion in der Benutzeroberfläche muss sich extrem schnell und flüssig anfühlen, selbst bei über 1000 Aufgaben.
2. Randbedingungen
- Plattformunabhängigkeit: Vorkompilierte ausführbare Binärdateien müssen für Linux, macOS und Windows zur Verfügung gestellt werden.
- Offline-First: Die Anwendung muss ohne jegliche Internetverbindung lokal lauffähig sein.
- Keine Abhängigkeiten beim Start: Vorkompilierte Einzeldateien müssen ohne vorinstallierte Python- oder Node-Laufzeitumgebungen auf dem Host-System lauffähig sein.
3. Kontextabgrenzung
flowchart TD
User([Benutzer-Browser]) <-->|localhost:58271| Jotter[Jotter-App]
Jotter <-->|Lesen/Schreiben| FS[(Lokales Dateisystem)]- Benutzer: Interagiert über einen modernen Webbrowser mit Jotter.
- Jotter-App: Liefert das Frontend-Paket aus und stellt eine lokale REST-API bereit.
- Lokales Dateisystem: Enthält die Aufgaben des Benutzers, die als
.md-Dateien in einer strukturierten Ordnerstruktur organisiert sind.
4. Lösungsstrategie
Jotter nutzt das "Ephemeral Index" Entwurfsmuster (flüchtiger Index), um die Vorteile einer relationalen Datenbank (schnelle Suchen, Sortierungen und Verknüpfungen) mit der Langlebigkeit von einfachen Textdateien zu kombinieren:
flowchart TD
API[Go Chi Backend] <-->|Schreibe JSON Frontmatter| Files[(Markdown-Dateien)]
API <-->|Lesen / Schreiben| DB[(SQLite-Index)]
DB -.->|Vollständig rekonstruiert aus| Files- Einzige Quelle der Wahrheit (Single Source of Truth, SSoT): Die Markdown-Dateien (
.md). Aufgabetitel, Spalte, Position, Tags und Fälligkeitsdatum werden im YAML-Frontmatter der Datei gespeichert, während Beschreibungen und Notizen im Markdown-Textkörper liegen. - Der Index-Dienst: Eine flüchtige SQLite-Datenbank. Beim Start scannt und analysiert das Backend die Markdown-Dateien und baut eine relationale Tabelle für schnelle API-Zugriffe auf.
- Auto-Rekonstruktion: Wenn die SQLite-Datenbank gelöscht oder beschädigt wird, baut das System sie beim nächsten Start automatisch wieder aus den Textdateien auf.
5. Bausteinsicht
flowchart LR
subgraph Frontend [Frontend SPA - Vue 3]
UI[Kanban UI Komponenten] <--> Store[Pinia Store]
Store <--> Client[API Client]
end
subgraph Backend [Backend Server - Go Chi / Wails]
direction TB
Router[API Router / chi.Router] <--> Handlers[Layered Handlers / Controller]
Handlers <--> Services[Domain Services / Business Layer]
Services <--> DBRepo[Database Repositories]
Services <--> FileRepo[File Repositories]
DBRepo <--> Database[(SQLite DB Index)]
end
Client <-->|REST API / CORS| Router
FileRepo <-->|Lesen / Schreiben| Disk[(Lokale Festplatte .md)]5.1 Frontend (Vue 3 Single Page Application)
- Kanban UI-Komponenten: Vue-Komponenten (
BoardView.vue,TaskCard.vue), gestaltet mit Tailwind CSS. - Pinia Store: Verwaltet clientseitige Einstellungen (wie lokale Präferenzen und Ansichten), die mit dem
localStoragedes Browsers synchronisiert werden. - API Client: Kommuniziert mit den Routen des Backends.
5.2 Backend (Go Chi / Wails)
Jotter verwendet eine klare, mehrschichtige Architektur, die in modulare Feature-Pakete unterteilt ist (internal/features/...): project, bucket, task, settings und system. Jedes Paket folgt einer strikten Trennung in drei Schichten (äquivalent zu Controllers, Services und Repositories in Spring Boot):
- Handlers (Controller-Schicht):
- Registriert feature-spezifische REST-Endpunkte (
RegisterRoutes). - Fungiert als Einstiegspunkt für HTTP-Anfragen.
- Analysiert Anfrageparameter und dekodiert Payloads in Go-Structs (DTOs - Data Transfer Objects).
- Übersetzt domänenspezifische Rückgaben oder Fehler in HTTP-Statuscodes und JSON-Antworten.
- Registriert feature-spezifische REST-Endpunkte (
- Services (Business-Logik / Domänenschicht):
- Enthält die reine Geschäftslogik, Eingabevalidierungen und Regelprüfungen.
- Koordiniert Repository-übergreifende Operationen (z. B. das synchrone Halten von Festplattendateien und dem SQLite-Index).
- Steuert erweiterte Dateisystemoperationen wie Multipart-Dateianhänge, Aufgabenlisten-Filterungen und automatische Aufbewahrungsfristen.
- Repositories (Datenzugriffsschicht / Persistenz):
- Database Repository (SQLite Repositories): Kommuniziert über strukturierte SQL-Abfragen direkt mit dem lokalen SQLite-Index (
modernc.org/sqlite). - File Repository (Disk Repositories): Interagiert direkt mit dem Dateisystem des Host-Rechners, um Markdown-Dateien, Konfigurationsdateien (
projects.json) und Dateianhänge zu schreiben und zu lesen.
- Database Repository (SQLite Repositories): Kommuniziert über strukturierte SQL-Abfragen direkt mit dem lokalen SQLite-Index (
6. Laufzeitsicht
6.1 Server-Start und Initialisierung
Beim Starten durchläuft Jotter eine Synchronisationsphase, um den Datenbank-Index exakt an die lokalen Dateien anzugleichen:
sequenceDiagram
participant Main as main_server.go / main_desktop.go
participant Bootstrap as internal/app/bootstrap.go
participant DB as internal/db/db.go
participant SysSvc as system.Service (internal/features/system)
participant FileRepo as system.FileRepository (internal/features/system)
participant DBRepo as system.DBRepository (internal/features/system)
participant Disk as Lokale Festplatte (.md)
Main->>Bootstrap: Bootstrap(dataDir, dbPath)
Bootstrap->>DB: InitDB()
DB-->>Bootstrap: DB initialisiert (SQLite Schema bereit)
Bootstrap->>SysSvc: SyncDBWithFiles()
SysSvc->>SysSvc: Service + Repositories instanziieren
SysSvc->>FileRepo: LoadProjectsFile() & ReadDir()
FileRepo->>Disk: Lese projects.json und Projekt-Ordner
Disk-->>FileRepo: Verzeichnisse & Dateien zurückgeben
FileRepo-->>SysSvc: Projektkonfigurationen & Aufgabenliste
SysSvc->>SysSvc: YAML-Frontmatter der .md-Dateien parsen
SysSvc->>DBRepo: Lösche & Bulk-Insert von Spalten/Aufgaben/Projekten
DBRepo-->>SysSvc: Synchronisation abgeschlossen
SysSvc-->>Bootstrap: Anzahl synchronisierter Dateien zurückgeben
Bootstrap-->>Main: Server ist betriebsbereit7. Verteilungssicht
Jotter wird in zwei unterschiedliche Binärdateien verpackt:
jotter-desktop(GUI): Eine vollständige Desktop-Anwendung, verpackt mit Wails. Sie öffnet ein natives Webview-Fenster und führt das eingebettete Frontend aus.jotter-server(Server): Ein leichtgewichtiges CLI-Binary, das einen standardmäßigen HTTP-Server startet und das Frontend für jeden modernen Webbrowser im lokalen Netzwerk bereitstellt.
Gemeinsame Merkmale:
- Asset-Einbettung: Das fertig gebaute Frontend-SPA-Paket (
dist/) wird mittelsgo:embeddirekt in das Go-Binary einkompiliert und nativ ausgeliefert. - Innere Logik: Beide Distributionen teilen sich exakt denselben Go-Code aus den
internal/Paketen, was ein absolut identisches Verhalten garantiert.
8. Git-Synchronisations-Logik
Jotter behandelt jedes Projektverzeichnis als potenzielles eigenständiges Git-Repository. Die Logik ist in internal/features/common/git.go implementiert und wird sequentiell für alle konfigurierten Projekte während einer Synchronisation ausgeführt.
Der Ablauf pro Projekt:
- Erkennung: Das Backend fragt die Datenbank nach allen Projekten mit eingerichteter
git_remoteURL ab. - Auto-Setup: Für jedes Projekt wird geprüft, ob ein
.gitOrdner existiert. Falls nicht, werden automatischgit initandgit remote add originausgeführt. - Commit: Führt
git add .undgit commitim jeweiligen Projekt-Unterverzeichnis aus. - Fetch & Merge: Holt Änderungen vom
originab und versucht einen sicheren Merge (git pull --rebase). - Konflikt-Isolation: Konflikte werden pro Projekt isoliert behandelt. Hat Projekt A einen Konflikt, wird dessen Merge abgebrochen, während Projekt B dennoch fehlerfrei synchronisiert wird.
- Push: Erfolgreiche Zusammenführungen werden an das jeweilige Remote-Repository hochgeladen.
Diese Architektur ermöglicht ein selektives Teilen, bei dem unterschiedliche Boards mit verschiedenen Teams geteilt oder auch komplett lokal gehalten werden können.
9. Datenmodell
9.1 Markdown YAML Frontmatter
Jede Aufgabendatei wird nach dem Muster [id]-[title-slug].md benannt. Die Metadaten werden im YAML-Frontmatter serialisiert:
---
id: 1042
project_id: default
title: Authentifizierung reparieren
bucket: todo
position: 2000.0
tags:
- backend
- auth
due_date: 2026-06-30
priority: high
created_at: 2026-06-04T12:00:00Z
---
Hier folgen die Inhaltsbeschreibungen der Aufgabe in Standard-Markdown.10. API & Swagger-Dokumentation
Jotter verfügt über eine vollautomatische OpenAPI 2.0 (Swagger) Spezifikationsgenerierung.
- OpenAPI-Annotationen: Jeder Handler/Controller im Go-Backend ist vollständig mit Attributen wie
@Summary,@Description,@Tags,@Accept,@Produce,@Param,@Success,@Failureund@Routerversehen. - Swagger-UI-Endpunkt: Bei Ausführung von
jotter-serverist das Swagger-UI standardmäßig unterhttp://localhost:58271/swagger/index.htmlerreichbar. - Spezifikation aktualisieren: Nach Änderungen an den Go-Handlern kann die Dokumentation über folgendes npm-Skript regeneriert werden:bashDies ruft den
npm run swagger:generateswagCLI-Generator auf, analysiert die Kommentare im Go-Quellcode und aktualisiert die JSON- und YAML-Dateien im Verzeichnisinternal/docs/.
