1. How would you design a REST API for a simple task management application?
Model answer
1. Requirements & scale
Functional Requirements:
- Users can create, read, update, and delete tasks.
- Tasks have attributes such as title, description, due date, and status.
- Users can list all tasks or filter tasks by status or due date.
Non-Functional Requirements:
- The API should be highly available and responsive.
- It should support a moderate number of concurrent users.
- Ensure data consistency for task operations.
Scale Estimates:
- Assume 10,000 active users, each making 10 requests/day.
- Total Requests per Day = 100,000.
- QPS (Queries Per Second) = 100,000 / 86,400 ≈ 1.16 QPS.
- Storage: Assuming each task is 1 KB and each user has 100 tasks, total storage = 10,000 users 100 tasks 1 KB = 1 GB.
2. High-level architecture
flowchart TD
subgraph Client
A[User Device]
end
subgraph Edge/CDN
B[CDN]
end
subgraph Load Balancer
C[Load Balancer]
end
subgraph API / Services
D[Task API Service]
end
subgraph Datastores
E["SQL Database"]
end
subgraph Cache
F[Redis Cache]
end
A -->|HTTP Request| B
B -->|Forward Request| C
C -->|Route Request| D
D -->|Read/Write| F
D -->|Read/Write| E
F -->|Cache Miss| E3. API design
POST /tasks: Create a new task.GET /tasks: Retrieve a list of tasks, with optional filters for status and due date.GET /tasks/{id}: Retrieve a specific task by ID.PUT /tasks/{id}: Update a task by ID.DELETE /tasks/{id}: Delete a task by ID.
4. Data model & storage
Chosen Datastore:
- SQL Database: A relational database is suitable here due to the need for ACID transactions and structured queries.
Key Tables:
- Tasks Table:
task_id(Primary Key)user_id(Foreign Key)titledescriptiondue_datestatus
Partitioning Strategy:
- Partition by
user_idto distribute load evenly and improve query performance.
5. Deep dive
The core functionality of the task management system is CRUD operations on tasks. Let's focus on the flow for creating a task.
sequenceDiagram
participant U as User
participant A as API Gateway
participant S as Task API Service
participant C as Redis Cache
participant D as SQL Database
U->>A: POST /tasks
A->>S: Forward request
S->>D: Insert task into DB
D-->>S: Task ID
S->>C: Update cache with new task
S-->>A: Return success response
A-->>U: Task created6. Scale, bottlenecks & trade-offs
Scaling:
- Horizontal Scaling: Add more instances of the Task API Service and SQL Database replicas to handle increased load.
- Caching: Use Redis to cache frequently accessed tasks to reduce database load and improve response times.
Bottlenecks:
- Database: As the number of tasks grows, database read/write operations could become a bottleneck. Use indexing and partitioning to optimize performance.
- Cache Consistency: Ensure cache invalidation strategies are in place to maintain consistency between the cache and the database.
Trade-offs:
- Consistency vs. Availability: Opt for strong consistency for task operations to ensure users always see the most up-to-date task information.
- SQL vs. NoSQL: SQL is chosen for its ACID properties, which are crucial for maintaining data integrity in task management.
By designing the system with these considerations, we ensure a robust, scalable, and user-friendly task management API.