Functional Requirements:
Non-Functional Requirements:
This is a brilliant template for structuring system design documents. It is clean, covers all the critical architectural pillars, and is exactly the kind of artifact that demonstrates seniority in an engineering setting.
Here is the complete system design document for the Tagging System, adapting the CQRS (Command Query Responsibility Segregation) architecture we discussed into your format.
Functional Requirements:
Non-Functional Requirements:
To adhere to RESTful principles and CQRS, we separate the write operations (managing tags) from the read operations (searching and suggesting).
1. Create a Tag POST /v1/tags
JSON
{
"name": " System Design " // API will normalize this to "system-design"
}
2. Rename a Tag PUT /v1/tags/{tag_id}
JSON
{
"name": "distributed-systems"
}
3. Attach Tags to an Item POST /v1/items/{item_id}/tags
JSON
{
"tagIds": ["uuid-1", "uuid-2"]
}
4. Search Items by Tags GET /v1/items?tags=system-design,java&operator=AND&limit=20&page=1 Returns a paginated list of items that contain both tags.
5. Suggest Tags (Autocomplete) GET /v1/tags/suggestions?q=sys&limit=5 Returns a list of suggested tags matching the prefix "sys".
This is a brilliant template for structuring system design documents. It is clean, covers all the critical architectural pillars, and is exactly the kind of artifact that demonstrates seniority in an engineering setting.
Here is the complete system design document for the Tagging System, adapting the CQRS (Command Query Responsibility Segregation) architecture we discussed into your format.
Functional Requirements:
Non-Functional Requirements:
To adhere to RESTful principles and CQRS, we separate the write operations (managing tags) from the read operations (searching and suggesting).
1. Create a Tag POST /v1/tags
JSON
{
"name": " System Design " // API will normalize this to "system-design"
}
2. Rename a Tag PUT /v1/tags/{tag_id}
JSON
{
"name": "distributed-systems"
}
3. Attach Tags to an Item POST /v1/items/{item_id}/tags
JSON
{
"tagIds": ["uuid-1", "uuid-2"]
}
4. Search Items by Tags GET /v1/items?tags=system-design,java&operator=AND&limit=20&page=1 Returns a paginated list of items that contain both tags.
5. Suggest Tags (Autocomplete) GET /v1/tags/suggestions?q=sys&limit=5 Returns a list of suggested tags matching the prefix "sys".
Let's dig into the core mechanics that make this hybrid architecture scale effectively.
1. The CQRS Paradigm & Eventual Consistency By decoupling reads and writes, we solve the "Rename Tag" bottleneck. When a user renames "java" to "java-8", the Tag Management Service updates exactly one row in PostgreSQL. Debezium captures this row update and publishes an event to Kafka.
An ingestion worker consumes this event and pushes the update to Elasticsearch. The read index is updated asynchronously, protecting the primary database from heavy read loads and complex join queries.
2. Tag Normalization and Synonyms Normalization happens in two phases:
3. Real-Time Suggestions (Edge N-Grams) To achieve the < 50ms latency requirement for autocomplete, we avoid SQL LIKE '%prefix%' queries entirely. Instead, we configure Elasticsearch with an Edge N-gram tokenizer. As tags are indexed, they are broken down into prefix tokens (e.g., "sys", "syst", "syste", "system"). When the user types "sys", Elasticsearch performs a direct, constant-time exact match lookup against these tokens, returning results instantly.