Task Management
Design a Trello-shaped tool: projects contain boards, boards contain tasks, tasks move through statuses and get assigned to users. It reads like a straightforward CRUD app, so the prompt is really testing whether you notice the one rule that isn't CRUD: a task can't jump straight from "not started" to "done".
Requirements
Functional
- A
Projectcontains one or moreBoards; aBoardcontainsTasks. - A task has a status -
TODO,IN_PROGRESS, orDONE- and can be assigned to aUser. - Status changes are constrained:
TODO -> IN_PROGRESS -> DONE, andDONE -> TODO(reopening) is allowed, butTODO -> DONEdirectly is not. - A user can list every task assigned to them, filtered by status, across every project they belong to.
Non-functional
- Assignment logic (who can a task be assigned to - anyone, or only board members) needs
to change per board without touching
Taskitself. - Listing "my tasks by status" must not force a linear scan of every task in every project; it should be answerable straight from an index kept in sync as tasks change.
Design
Status transitions are the one place this domain has real rules, so they get pulled out of
Task and into a TaskStatus enum that owns its own canTransitionTo() check - the same
"is this jump legal" question the fee strategy in
Parking Lot answers for pricing, just applied to a state
enum here instead of a pluggable strategy, because the rule set (three statuses, two
directions) is fixed rather than swappable.
- 1Assignment always goes through the board - it owns the policy for who is eligible.
- 2The board checks its own assignment policy before touching the task at all.
- 3Only once the policy approves does the task record its assignee.
- 4A status change is requested directly on the task, since legality only depends on the current status.
- 5The task delegates the legality check to the enum value itself rather than hard-coding the transition table.
- 6Whichever field changed, the board refreshes that user’s per-status index so lookups stay O(1).
Board maintains the assignable-member policy and a TaskIndex per user that gets updated
on every assignment or status change, so "my tasks in progress" is a lookup, not a scan.
Class diagram
Code
Design decisions
TaskStatus.canTransitionTo()lives on the enum, not inTask.setStatus(). Keeping the legality check next to the values it governs means adding a fourth status (BLOCKED, say) means updating one enum's transition table, not hunting through every method that ever callssetStatus.Task.assign()asks the board, not the user, whether an assignment is allowed. The rule "who can this go to" is a property of the board (a personal to-do board allows anyone; a company board might restrict to members), not of the task or the user, so it's the board'sAssignmentPolicythat gets consulted - swapping policies never touchesTask.- Each user's task list is a maintained index (
Map<Status, Set<Task>>per user), not a filter over every task on every read. The index is updated exactly where a task's assignee or status changes - two call sites - which is cheap enough to keep synchronous and avoids ever re-scanning a project to answer "what's assigned to me." - What's missing for a real system: cross-project task search needs a
denormalized index outside any single
Board(this design's per-user index is board-scoped and would need merging across boards), and concurrent status updates from two clients need optimistic locking onTask.version- both cut here to keep the transition-and-assignment logic the whole focus.