taskly · Board und Zeiterfassung
Das Taskly-Kanban-Board im dunklen Modus: Aufgabenkarten verteilt über die Spalten Open, To-Do, In Progress, Testing und Done.

Überblick

Projekt- und Vorgangsverfolgung mit Kanban-Board, Epics, Zeiterfassung, Auswertungen und Benachrichtigungen in Echtzeit, auf .NET 10 und Angular 22.

Jemand sichtet die über Nacht eingegangenen Karten, zieht drei aufs Board und startet einen Timer. Alles darunter sorgt dafür, dass zwei Leute, die denselben Task bearbeiten, nie unbemerkt eine Änderung verlieren.

  • Issues mit Priorität, Status, Labels, Epics und einer Position auf dem Board
  • Board, Backlog, Epics und erledigte Issues für jedes Projekt
  • Zeiterfassung, Kalender und Auswertungsdiagramme
  • Live-Benachrichtigungen, Fristerinnerungen und eine wöchentliche Zusammenfassung
Vier Akteure, gruppiert nach ihrer Aufgabe.
Auf einen Blick
RolleAlleinentwickler: Backend, Frontend, Infrastruktur und Werkzeuge
AufbauZwölf .NET-Projekte, ein Angular-22-Client, eine Verwaltungsanwendung und ein Deploy-CLI
DatenEF Core auf SQL Server, Soft Delete, Concurrency über Row Version
EchtzeitSignalR, mit einer Gruppe je Benutzer und acht Hintergrunddiensten
Tests3.666 Backend-Testmethoden in 13 Projekten, 2.241 Frontend-Specs
Öffentliches RepositoryEine Demo-Variante: keine Anmeldung, localStorage statt der API

Architektur

Sechs Schichten, eine Richtung und Controller, die nur weiterreichen.

Jeder Pfeil zeigt nach unten; die Admin-App ist ein Client der API, keine Hintertür.
  • Ein Projekt darf nur die Schicht darunter referenzieren, deshalb lässt sich jeder Handler gegen einen Fake testen.
  • Ein Controller baut ein Command oder eine Query und reicht sie weiter. Berechtigungen kommen aus Attributen, nicht aus Prüfungen im Handler.
  • Der Angular-Client besteht aus Standalone-Komponenten auf Signal Stores, einer pro Domänenbereich, serverseitig gerendert.
  • Die CQRS-Schicht, den Result-Typ und einen kleinen Mapper habe ich selbst geschrieben: günstiger selbst zu pflegen als zu konfigurieren.

Domänendesign

Zwei Aggregate und zwei Regeln, die die Datenbank erzwingt, statt sie einer Konvention zu überlassen.

Die Tabellen hinter dem Board. SD steht für Soft Delete, RV für eine Row Version.
  • Project und TaskItem sind die Aggregatwurzeln. Handler laden und speichern nur diese.
  • Jeder Speichervorgang schickt die Row Version mit, die der Client gelesen hat. Eine Änderung an einer veralteten Kopie wird deshalb mit 409 abgelehnt.
  • Ein globaler Query-Filter macht Löschen zu einem Flag. Endgültiges Löschen ist nur Administratoren erlaubt.
  • Eine Specification pro Entität hält Abfragen aus den Handlern heraus.

Daten- und Kontrollfluss

Eine Karte auf dem Board verschoben, verfolgt bis in den Browser der anderen Person, die sie sieht.

Gegen den Stand des Verschiebenden geprüft, dann allen mit offenem Board gezeigt.

Eine Board-Spalte lädt alle Karten und Subtasks in einem einzigen Batch-Request, nicht einen pro Karte. Acht Hintergrunddienste schicken dieselben Commands wie die Controller, so endet eine Testphase, ohne dass jemand etwas anklickt.

Teststrategie

3.666 Backend-Testmethoden und 2.241 Frontend-Specs, mit dem Handler als Einheit.

  • Ein Handler wird mit einem Fake-Repository und einem Principal getestet und über sein Result geprüft.
  • Routing, Auth oder die Datenbank machen daraus per Definition einen Integrationstest.

Was ich ändern würde

  • Tasks und Workflows haben dieselbe Struktur, sind im Code aber auseinandergelaufen. Eine generische Umsetzung würde rund tausend Zeilen einsparen.
  • Der Reflection-Mapper passte bei zehn DTOs, bei fünfzig nicht mehr: Er ist langsamer, und eine Umbenennung fällt erst zur Laufzeit auf.
  • Subtasks schicken ihre Row Version noch nicht mit und melden ihre Verschiebungen nicht. Die Tasks zeigen schon, wie es geht.
  • Beim Umsortieren entsteht ein UPDATE pro Karte innerhalb einer Transaktion. Mit einem einzigen CASE-Statement wäre es ein Roundtrip.
  • Berechtigungen liegen in der API und werden im Client gespiegelt. Schickt man sie mit der Session mit, laufen API und Client nicht mehr auseinander.