{"id":20533052,"url":"https://github.com/anatoliybr/dwh-service","last_synced_at":"2026-05-05T11:32:57.979Z","repository":{"id":199140747,"uuid":"700512471","full_name":"AnatoliyBr/dwh-service","owner":"AnatoliyBr","description":"REST API server for storing arbitrary metrics (Data Warehouse)","archived":false,"fork":false,"pushed_at":"2024-02-04T17:22:36.000Z","size":397,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-16T13:18:08.122Z","etag":null,"topics":["clean-architecture","data-warehouse","dependency-injection","docker-compose","dockerfile","go","migrations","postgresql","rest-api"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AnatoliyBr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-04T18:23:29.000Z","updated_at":"2023-10-08T20:34:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"3ad4679a-18b1-4935-a8a4-ca35c2214397","html_url":"https://github.com/AnatoliyBr/dwh-service","commit_stats":null,"previous_names":["anatoliybr/dwh-service"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoliyBr%2Fdwh-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoliyBr%2Fdwh-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoliyBr%2Fdwh-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoliyBr%2Fdwh-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AnatoliyBr","download_url":"https://codeload.github.com/AnatoliyBr/dwh-service/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242138805,"owners_count":20078007,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clean-architecture","data-warehouse","dependency-injection","docker-compose","dockerfile","go","migrations","postgresql","rest-api"],"created_at":"2024-11-16T00:18:25.835Z","updated_at":"2026-05-05T11:32:57.931Z","avatar_url":"https://github.com/AnatoliyBr.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Data Warehouse Service\n### Описание\nREST API приложение для хранения **произвольных метрик**, способное принимать в себя данные, хранить и отдавать пользователю.\n\n### Функционал\nДанное серверное (backend) приложение предоставляет **API** с форматом **JSON** для добавления **сервисов**, **метрик** и **событий**, а также получения данных по **ключам событий** за заданный интервал времени.\n\nПод событием понимается получение **списка метрик** от **одного сервиса**.\n\n## Структура REST API\n\n```\nPOST /services - добавление нового сервиса\nGET /services - просмотр отслеживаемых сервисов\n\nPOST /metrics - добавление новой метрики\nGET /metrics - просмотр используемых метрик\n\nPOST /events - добавление нового события\nGET /events - получение данных по идентификатору сервиса и метрики за заданный интервал времени\n```\n\n## Схема базы данных\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"/assets/images/db_schema.png\" width=\"800\"\u003e\n\u003c/p\u003e\n\n## Архитектура\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"/assets/images/architecture.png\" width=\"800\"\u003e\n\u003c/p\u003e\n\n## Запуск и отладка\nВсе команды, используемые в процессе разработки и тестирования, фиксировались в `Makefile`.\n\nДля запуска сервиса достаточно добавить `.env` файл в директорию с проектом и заполнить его данными из [.env.example](/.env.example).\n\nЧтобы поднять проект, необходимо выполнить **две задачи** из `Makefile`:\n\n```bash\nmake compose-build\nmake compose-up\n```\n\n## Примеры запросов\n* [Добавление сервиса](#добавление-сервиса)\n* [Просмотр сервиса](#просмотр-сервиса)\n* [Добавление метрики](#добавление-метрики)\n    * [Типа INT](#типа-int)\n    * [Типа FLOAT](#типа-float)\n    * [Типа DURATION](#типа-duration)\n    * [Типа TIMESTAMP_WITH_TIMEZONE](#типа-timestamp_with_timezone)\n    * [Типа BOOL](#типа-bool)\n    * [Типа STRING](#типа-string)\n* [Просмотр метрики](#просмотр-метрики)\n* [Добавление события](#добавление-события)\n* [Получение данных](#получение-данных)\n\n### Добавление сервиса\nДобавление нового сервиса:\n\n```bash\ncurl --location --request POST http://localhost:8080/services \\\n--data-raw '{\n    \"slug\": \"TODO_APP\",\n    \"details\": \"REST API application for managing task lists (todo lists)\"\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"service_id\": 1,   \n    \"slug\": \"TODO_APP\",\n    \"details\": \"REST API application for managing task lists (todo lists)\"\n}\n```\n\n### Просмотр сервиса\nПросмотр сервиса по идентификатору:\n\n```bash\ncurl --location --request GET http://localhost:8080/services \\\n--data-raw '{\n    \"service_id\": 1\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"service_id\": 1,\n    \"slug\": \"TODO_APP\",\n    \"details\": \"REST API application for managing task lists (todo lists)\"\n}\n```\n\n### Добавление метрики\n\u003e Сервер поддерживает 6 типов: \"INT\", \"FLOAT\", \"DURATION\", \"TIMESTAMP_WITH_TIMEZONE\", \"BOOL\", \"STRING\".\n\n#### Типа INT\nДобавление новой метрики типа \"INT\":\n\n```bash\ncurl --location --request POST http://localhost:8080/metrics \\\n--data-raw '{\n    \"slug\": \"INT_METRIC\",\n    \"metric_type\": \"INT\",\n    \"details\": \"Calculated in integers\"\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"metric_id\": 1,\n    \"slug\": \"INT_METRIC\",\n    \"metric_type\": \"INT\",\n    \"details\": \"Calculated in integers\"\n}\n```\n\n#### Типа FLOAT\nДобавление новой метрики типа \"FLOAT\":\n\n```bash\ncurl --location --request POST http://localhost:8080/metrics \\\n--data-raw '{\n    \"slug\": \"FLOAT_METRIC\",\n    \"metric_type\": \"FLOAT\",\n    \"details\": \"Calculated in floating point numbers\"\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"metric_id\": 2,\n    \"slug\": \"FLOAT_METRIC\",\n    \"metric_type\": \"FLOAT\",\n    \"details\": \"Calculated in floating point numbers\"\n}\n```\n\n#### Типа DURATION\nДобавление новой метрики типа \"DURATION\":\n\n```bash\ncurl --location --request POST http://localhost:8080/metrics \\\n--data-raw '{\n    \"slug\": \"DURATION_METRIC\",\n    \"metric_type\": \"DURATION\",\n    \"details\": \"Calculated by duration\"\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"metric_id\": 3,\n    \"slug\": \"DURATION_METRIC\",\n    \"metric_type\": \"DURATION\",\n    \"details\": \"Calculated by duration\"\n}\n```\n\n#### Типа TIMESTAMP_WITH_TIMEZONE\nДобавление новой метрики типа \"TIMESTAMP_WITH_TIMEZONE\":\n\n```bash\ncurl --location --request POST http://localhost:8080/metrics \\\n--data-raw '{\n    \"slug\": \"TIMESTAMP_WITH_TIMEZONE_METRIC\",\n    \"metric_type\": \"TIMESTAMP_WITH_TIMEZONE\",\n    \"details\": \"Calculated by timestamps with timezone\"\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"metric_id\": 4,\n    \"slug\": \"TIMESTAMP_WITH_TIMEZONE_METRIC\",\n    \"metric_type\": \"TIMESTAMP_WITH_TIMEZONE\",\n    \"details\": \"Calculated by timestamps with timezone\"\n}\n```\n\n#### Типа BOOL\nДобавление новой метрики типа \"BOOL\":\n\n```bash\ncurl --location --request POST http://localhost:8080/metrics \\\n--data-raw '{\n    \"slug\": \"BOOL_METRIC\",\n    \"metric_type\": \"BOOL\",\n    \"details\": \"Calculated by logical type\"\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"metric_id\": 5,\n    \"slug\": \"BOOL_METRIC\",\n    \"metric_type\": \"BOOL\",\n    \"details\": \"Calculated by logical type\"\n}\n```\n\n#### Типа STRING\nДобавление новой метрики типа \"STRING\":\n\n```bash\ncurl --location --request POST http://localhost:8080/metrics \\\n--data-raw '{\n    \"slug\": \"STRING_METRIC\",\n    \"metric_type\": \"STRING\",\n    \"details\": \"Contains a message\"\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"metric_id\": 6,\n    \"slug\": \"STRING_METRIC\",\n    \"metric_type\": \"STRING\",\n    \"details\": \"Contains a message\"\n}\n```\n\n### Просмотр метрики\nПросмотр метрики по идентификатору:\n\n```bash\ncurl --location --request GET http://localhost:8080/metrics \\\n--data-raw '{\n    \"metric_id\": 1\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"metric_id\": 1,\n    \"slug\": \"INT_METRIC\",\n    \"metric_type\": \"INT\",\n    \"details\": \"Calculated in integers\"\n}\n```\n\n### Добавление события\nДобавление нового события со списком метрик:\n\n```bash\ncurl --location --request POST http://localhost:8080/events \\\n--data-raw '{\n    \"service_id\": 1,\n    \"metrics\": [\n        {\n            \"metric_id\": 1,\n            \"metric_value\": 25\n        },\n        {\n            \"metric_id\": 2,\n            \"metric_value\": 36.6\n        },\n        {\n            \"metric_id\": 3,\n            \"metric_value\": \"1h13m0.5s\"\n        },\n        {\n            \"metric_id\": 4,\n            \"metric_value\": \"2023-10-08T19:14:05Z\"\n        },\n        {\n            \"metric_id\": 5,\n            \"metric_value\": true\n        },\n        {\n            \"metric_id\": 6,\n            \"metric_value\": \"Suspicious activity\"\n        }\n    ]\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"event\": {\n        \"event_id\": 1,\n        \"time_stamp\": \"2023-10-08T20:44:59+03:00\",\n        \"service_id\": 1\n    },\n    \"metrics\": [\n        {\n            \"metric_id\": 1,\n            \"metric_value\": 25\n        },\n        {\n            \"metric_id\": 2,\n            \"metric_value\": 36.6\n        },\n        {\n            \"metric_id\": 3,\n            \"metric_value\": \"1h13m0.5s\"\n        },\n        {\n            \"metric_id\": 4,\n            \"metric_value\": \"2023-10-08T19:14:05Z\"\n        },\n        {\n            \"metric_id\": 5,\n            \"metric_value\": true\n        },\n        {\n            \"metric_id\": 6,\n            \"metric_value\": \"Suspicious activity\"\n        }\n    ]\n}\n```\n\n### Получение данных\nПолучение данных по идентификатору сервиса и метрики за заданный интервал времени:\n\n```bash\ncurl --location --request GET http://localhost:8080/events \\\n--data-raw '{\n    \"service_id\": 1,\n    \"period\": [\n        \"2023-10-06T10:00:00+03:00\",\n        \"2023-10-09T10:00:00+03:00\"\n        ],\n    \"metric_id\": 3\n}'\n```\n\nПример ответа:\n\n```bash\n{\n    \"request\": {\n        \"service_id\": 1,\n        \"period\": [\n            \"2023-10-06T10:00:00+03:00\",\n            \"2023-10-09T10:00:00+03:00\"\n        ],\n        \"metric_id\": 3\n    },\n    \"report\": [\n        {\n            \"time_stamp\": \"2023-10-08T20:41:37Z\",\n            \"value\": \"3m10s\"\n        },\n        {\n            \"time_stamp\": \"2023-10-08T20:42:02Z\",\n            \"value\": \"1h3m9.9s\"\n        },\n        {\n            \"time_stamp\": \"2023-10-08T20:42:19Z\",\n            \"value\": \"33m35s\"\n        },\n        {\n            \"time_stamp\": \"2023-10-08T20:42:36Z\",\n            \"value\": \"13m2.1s\"\n        }\n    ]\n}\n```\n\n## Решения\nВ ходе разработки были сомнения по тем или иным вопросам, которые были решены следующим образом:\n1. Как организовать хранение произвольных метрик, набор которых динамически меняется?\n* **Первый вариант**. При регистрации события записывать каждую метрику в отдельную строку (то есть одна строка = одна метрика). Схема простая, расширяемая, нормализованная, позволяет использовать реляционную БД типа PostgreSQL и **хорошо справляется с добавлением новых метрик**, но **требует преобразования значений метрик разных типов в один (например, в строку)**.\n* **Второй вариант**. Каждой метрике сопоставлять столбец и использовать колоночную БД типа Cassandra. Схема **позволяет эффективнее считывать информацию по ключам**, но **требует изменения схемы БД (добавления новой колонки) при добавлении каждой новой метрики в БД**.\n\n\u003e В данном проекте использовался первый вариант.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanatoliybr%2Fdwh-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanatoliybr%2Fdwh-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanatoliybr%2Fdwh-service/lists"}