Personal Systems Platform
A backend for personal productivity apps—todos, budgets, fitness tracking—built on shared foundations where components compose and extend each other without tight coupling.
Three Principles
-
01
Identity is shared
Person and Group entities live in a central kernel, so every component knows who owns what
- 02
-
03
Composition replaces duplication
Cross-cutting concerns live in platform primitives, not copied into each component
The Layers
(Todos, Budget, Fitness)"] PLATFORM["Platform Primitives
(Clock, EventBus, Hooks)"] IDENTITY["Identity Kernel
(Person, Group)"] APP -->|owner_id| IDENTITY APP -->|uses| PLATFORM style APP fill:#ecfeff,stroke:#0891b2 style PLATFORM fill:#fef3c7,stroke:#d97706 style IDENTITY fill:#f3e8ff,stroke:#9333ea
What to notice: Components in the Application layer reference the Identity kernel through owner_id—a UUID that points to either a Person or Group. Platform primitives provide shared infrastructure: time, messaging, authorization, extensibility.
Non-Goals
PSP is deliberately constrained:
-
✗
Not a monolith
Components cannot share database tables or import each other's models
-
✗
No cross-component joins
You cannot JOIN tasks with budget_plans; use events and local projections instead
-
✗
Eventual consistency by design
Components react to events asynchronously—this is a feature, not a bug
Interaction Patterns
When components need to work together, choose the right mechanism:
| Pattern | When to Use | Example |
|---|---|---|
| Hooks | Synchronous interception, veto/patch, policy-like checks | Budget vetoes task completion |
| Events | Async side effects, fan-out, audit trails | Recurrence triggers on TaskCompleted |
| Policies | Authorization/permission centralization | OwnerOnly policy on delete |
| Queries | Discovery, export, reporting | GDPR data export by owner_id |