Theme
Lesson 03 — Model engineering work, not screens
Outcome
Implement the first pure domain types for Project, Task, and Acceptance Criterion without SQLite or RPC concerns.
Why this comes now
Screens are tempting to model first (“Kanban column”, “review pane”), but that makes UI structure leak into business state. We want the same objects to support Kanban, List, Tree, CLI, and future clients.
Understand
A domain type should represent a concept and protect rules that remain true regardless of UI or storage. IDs are stable; titles are mutable. A Task owns its engineering goal and status, not provider protocol state.
Use plain Go structs and methods. Do not turn “DDD” into ceremony. The test is whether a rule has one obvious home.
Build the real project
- Add domain packages/types for Project ID, Task ID, Feature ID (even if Feature behavior comes later), Task, and AcceptanceCriterion.
- Add constructors that reject invalid required fields.
- Use explicit enum-like typed strings/constants for status/modes.
- Keep persistence tags/adapters minimal; domain constructors should not require a DB handle.
- Write table-driven tests for valid/invalid construction.
Completion gate
Domain tests pass without opening files, DBs, or network sockets. You can instantiate a Task in a unit test with no infrastructure.
Pitfalls to avoid
Do not create TaskViewModel in the core. Do not put agent PID/session fields directly on Task. Do not use Task title as identity.
References
docs/architecture/DOMAIN_MODEL.md, Go Code Review Comments.
Checkpoint
Record any field you were tempted to add but could not explain as Task-owned. That is useful design evidence.