Design a Todo List Object Model
NotionSoftware Engineer · OnsiteLast updated
Tests object-oriented design, domain modeling, and API design skills by implementing an extensible, hierarchical Todo list model with task dependency management and filtering.
Problem Statement
Design an object-oriented domain model and service interface for a hierarchical Todo List application (similar to nested checklist blocks in Notion).
Your design should model tasks, support arbitrary nesting of subtasks, enforce status transition rules, and allow querying/filtering tasks by various attributes.
Functional Requirements
- Task Management:
- Create tasks with a
title, optionaldescription,priority(LOW,MEDIUM,HIGH), optionaldue_date, and zero or moretags. - Tasks can have subtasks, supporting arbitrary levels of nesting.
- A task can be deleted, which recursively deletes all its subtasks.
- Create tasks with a
- Status Propagation:
- Tasks have a status:
PENDING,IN_PROGRESS, orCOMPLETED. - When a parent task is marked
COMPLETED, all its descendant subtasks must also be markedCOMPLETED. - If a subtask is marked
PENDINGorIN_PROGRESS, all its ancestor tasks must transition out ofCOMPLETED(e.g., toIN_PROGRESS).
- Tasks have a status:
- Querying and Filtering:
- Provide a search/query API that filters tasks by status, tags, priority, or due date range.
- Support sorting by due date or priority.
Interface
Design the underlying entities and implement the TodoListService class:
from enum import Enum
from typing import List, Optional, Set
from datetime import datetime
class Priority(Enum):
LOW = 1
MEDIUM = 2
HIGH = 3
class Status(Enum):
PENDING = "PENDING"
IN_PROGRESS = "IN_PROGRESS"
COMPLETED = "COMPLETED"
class TodoListService:
def create_task(
self,
title: str,
description: str = "",
priority: Priority = Priority.MEDIUM,
due_date: Optional[datetime] = None,
tags: Optional[Set[str]] = None,
parent_id: Optional[str] = None
) -> str:
"""Creates a new task (or subtask) and returns its generated task_id."""
pass
def update_status(self, task_id: str, new_status: Status) -> None:
"""Updates status and propagates changes to descendants/ancestors."""
pass
def delete_task(self, task_id: str) -> None:
"""Deletes a task and all of its descendants."""
pass
def query_tasks(
self,
status: Optional[Status] = None,
tag: Optional[str] = None,
priority: Optional[Priority] = None,
sort_by: Optional[str] = None
) -> List["Task"]:
"""Filters tasks across the workspace based on provided criteria."""
pass
Example
service = TodoListService()
parent_id = service.create_task("Launch Feature", priority=Priority.HIGH)
child_id = service.create_task("Write Unit Tests", parent_id=parent_id)
# Completing parent completes child
service.update_status(parent_id, Status.COMPLETED)
assert service.get_task(child_id).status == Status.COMPLETED
# Reopening child reopens parent
service.update_status(child_id, Status.IN_PROGRESS)
assert service.get_task(parent_id).status == Status.IN_PROGRESS
Edge Cases to Consider
- Deleting a parent node when clients still hold references to subtask IDs.
- Preventing circular parent-child relationships if tasks can be reparented.
- Efficient upward and downward traversals without deep recursion stack overflows.
Hints
Hint 1
Consider using the Composite pattern to treat individual tasks and nested subtask trees uniformly.
Hint 2
Decouple state propagation into separate ancestor-updating and descendant-updating routines to prevent infinite loops.
Hint 3
Design a dedicated Filter or Specification object for querying so the search interface remains open to extension without changing method signatures.
Follow-up questions
- How would you model recurring tasks (e.g., repeats every Monday) without pre-generating infinite future task instances?
- In a collaborative environment like Notion, how would your domain model resolve concurrent edits where one user moves a subtask while another deletes its parent?
- How would you optimize the search and filtering queries if the list grows to hundreds of thousands of tasks per organization?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.