├── architecture ├── core_system │ ├── database │ │ └── url_service │ │ │ ├── cache.io │ │ │ └── database.io │ └── container.puml ├── analytics_system │ ├── database │ │ └── analytics_service │ │ │ ├── cache.io │ │ │ └── database.io │ └── container.puml └── context.puml ├── README.md ├── api └── rest_api.yml └── images └── diagrams ├── context.svg └── containers ├── core_system.svg └── analytics_system.svg /architecture/core_system/database/url_service/cache.io: -------------------------------------------------------------------------------- 1 | Table popular_url { 2 | id string [note: 'Tiny url id'] 3 | long_url string [not null, unique, note: 'Long url'] 4 | } -------------------------------------------------------------------------------- /architecture/analytics_system/database/analytics_service/cache.io: -------------------------------------------------------------------------------- 1 | Table aggregated_url_click { 2 | tiny_url_id string [not null, note: 'Tiny url id'] 3 | count integer [not null, note: 'Count of the clicks'] 4 | } -------------------------------------------------------------------------------- /architecture/core_system/database/url_service/database.io: -------------------------------------------------------------------------------- 1 | // Replication: 2 | // - master-slave (one sync + async) 3 | // - replication factor 3 4 | // 5 | // Sharding: 6 | // - key based by id 7 | 8 | Table url { 9 | id string [primary key, note: 'Tiny url id'] 10 | long_url string [not null, unique, note: 'Long url'] 11 | created_at timestamp [not null, note: 'Creation time'] 12 | } -------------------------------------------------------------------------------- /architecture/analytics_system/database/analytics_service/database.io: -------------------------------------------------------------------------------- 1 | // Replication: 2 | // - master-slave (async) 3 | // - replication factor 3 4 | // 5 | // Sharding: 6 | // - key based by tiny_url_id 7 | 8 | Table url_click { 9 | id id [primary key, note: 'Activity id'] 10 | tiny_url_id string [not null, note: 'Tiny url id'] 11 | clicked_at timestamp [not null, note: 'Time of the click'] 12 | ip_address int [note: 'IP address of the client'] 13 | device string [note: 'Name of the user device'] 14 | } -------------------------------------------------------------------------------- /architecture/context.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml 3 | 4 | Person(user, "User") 5 | Container(coreSystem, "Core system", "Software system", "Handles creation of tiny URL from long URL and redirections from tiny to long URL") 6 | Container(analyticsSystem, "Analytics system", "Software system", "Stores different analytic information about tiny URLs") 7 | 8 | Rel(user, coreSystem, "Creates tiny URL and redirects from tiny to long URL") 9 | Rel(user, analyticsSystem, "Gets different clicks information by tiny URLs") 10 | @enduml -------------------------------------------------------------------------------- /architecture/core_system/container.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml 3 | 4 | Person(user, "User") 5 | Container(loadBalancer, "Load Balancer", "Nginx", "Uses round robin") 6 | Container(analyticsSystem, "Analytics system", "Software system", "Stores different analytic information about tiny URLs") 7 | ContainerQueue(messageQueue, "Events queue", "Kafka", "Message queue for events by tiny URLs") 8 | 9 | System_Boundary(coreSystem, "Core system") { 10 | Container(urlService, "URL Service", "Go", "Handles creation tiny URL and redirects to long URL from tiny URL", $tags="webApp") 11 | ContainerDb(urlDatabase, "URL database", "PgSQL", "Stores long URLs by tiny URLs", $tags="db") 12 | ContainerDb(urlCache, "URL cache", "Tarantool", "Stores popular long URLs by tiny URLs", $tags="db") 13 | } 14 | 15 | Rel(user, loadBalancer, "Creates tiny URL and redirects from tiny URL to long URL", "REST") 16 | Rel(loadBalancer, urlService, "Creates tiny URL and redirects from tiny URL to long URL", "REST") 17 | 18 | Rel(urlService, urlDatabase, "Creates tiny URLs and gets long URLs by tiny URLs") 19 | Rel(urlService, urlCache, "Gets and puts least frequently used URLs") 20 | Rel(urlService, messageQueue, "Publishes click events by tiny URLs") 21 | Rel(analyticsSystem, messageQueue, "Subscribes on click events by tiny URLs") 22 | @enduml -------------------------------------------------------------------------------- /architecture/analytics_system/container.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml 3 | 4 | Person(user, "User") 5 | Container(loadBalancer, "Load Balancer", "Nginx", "Uses round robin") 6 | ContainerQueue(messageQueue, "Events queue", "Kafka", "Message queue for events by tiny URLs") 7 | Container(coreSystem, "Core system", "Software system", "Stores different analytic information about tiny URLs") 8 | 9 | System_Boundary(analyticsSystem, "Analytics system") { 10 | Container(analyticsService, "Analytics Service", "Go", "Handles requests by clicks information and each hour stores aggregated information about click numbers of each tiny URL to cache", $tags="webApp") 11 | ContainerDb(analyticsDatabase, "Analytics database", "ClickHouse", "Stores analytics information about clicks of each tiny URL", $tags="db") 12 | ContainerDb(analyticsCache, "Analytics cache", "Tarantool", "Stores aggregated information about number of clicks by each tiny URL", $tags="db") 13 | } 14 | 15 | Rel(user, loadBalancer, "Gets clicks information by tiny URLs", "REST") 16 | Rel(loadBalancer, analyticsService, "Gets clicks information by tiny URLs", "REST") 17 | 18 | Rel(coreSystem, messageQueue, "Publishes click events by tiny URLs") 19 | Rel(analyticsService, messageQueue, "Subscribes on click events by tiny URLs") 20 | Rel(analyticsService, analyticsDatabase, "Stores click events and also aggregates information about all clicks by tiny URLs") 21 | Rel(analyticsService, analyticsCache, "Gets clicks number of each tiny URL, also stores information about clicks number of each tiny URL") 22 | @enduml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tiny URL - System Design 2 | 3 | Example of the homework for [course by System Design](https://balun.courses/courses/system_design). 4 | TinyURL is a URL shortening web service, which provides short aliases for redirection 5 | of long URLs. 6 | 7 | ### Functional requirements: 8 | 9 | - creates tiny URL from long URL 10 | - redirects from long URL to tiny URL 11 | - gets analytics about tiny URL clicks 12 | 13 | 14 | ### Non-functional requirements: 15 | 16 | - 50 000 000 DAU 17 | - availability 99,95% 18 | - tiny URLs are always stored 19 | - service operation time 5 years 20 | - each user creates one tiny URL per week 21 | - on average, each tiny URL is accessed 20 times a day 22 | - geo distribution is not needed 23 | - no seasonality 24 | 25 | ## Design overview 26 | 27 | For system design I have used [C4 model](https://c4model.com/). The C4 model was created as a way 28 | to help software development teams describe and communicate software 29 | architecture, both during up-front design sessions and when retrospectively 30 | documenting an existing codebase. It's a way to create maps of your code, 31 | at various levels of detail, in the same way you would use something like 32 | Google Maps to zoom in and out of an area you are interested in. 33 | 34 |
35 | Level 1. System context diagram 36 |
37 | 38 |
39 |
40 |
43 | Level 2. Core system container diagram 44 |
45 | 46 |
47 |
48 |
51 | Level 2. Analytics system container diagram 52 |
53 | 54 |
55 |
56 |