Initial commit

This commit is contained in:
NortPerm
2023-10-30 15:21:12 +03:00
committed by GitHub
commit 13e2575b4e
149 changed files with 3961 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
## Домашнее задание №12 «Заготовка сервиса Календарь»
Необходимо реализовать скелет сервиса «Календарь», который будет дорабатываться в дальнейшем.
Описание того, к чему мы должны придти, представлено [в техническом задании](./CALENDAR.MD).
---
В репозитории представлена заготовка сервиса, позволяющая понять, что вообще происходит и получить вектор для дальнейшей доработки.
Этот код можно менять/удалять/добавлять каким-угодно способом по усмотрению разработчика.
---
На данный момент сервис будет состоять из следующих логически выделенных частей:
### 0) «Точка входа», запускающая сервис
Обычно располагается в `cmd/`. В `main()` происходит инициализация компонентов сервиса
(клиент к хранилищу, логгер, конфигурация и пр.), конструирование главного объекта-сервиса на их
основе и его запуск. Допускается использование https://github.com/spf13/cobra.
### 1) Конфигурирование сервиса
При необходимости создать отдельный пакет, отвечающий за работу с конфигом.
Сервис должен иметь конфиг, считываемый из файла. Для этого потребуется:
* обработка аргументов командной строки;
* чтение файла конфигурации формата json, yaml и пр. на выбор разработчика;
* заполнение программной структуры конфига из файла (Unmarshal и пр. способы);
* допустимо, если все действия выше за вас делает сторонний модуль.
Конфигурация понадобится для инициализации различных компонентов системы,
её возможные поля будут рассмотрены далее.
Соответственно сервис будет запускаться командой вида
```bash
./calendar --config=/path/to/config.yaml
```
где `--config` - путь к файлу конфигурации.
В репозитории должен присутствовать образец конфига.
### 2) Логирование в сервисе
При необходимости создать отдельный пакет, отвечающий за работу с логером.
Параметры, которыми необходимо инициализировать логгер:
* log_level - уровень логирования (error / warn / info / debug);
* любые другие на усмотрение разработчика.
Логгер может быть как глобальной переменной, так и компонентом сервиса.
### 3) Работа с хранилищем
Создать отдельный пакет, отвечающий за работу с хранилищем.
Создать интерфейс хранилища событий, состоящий из методов для работы с ним:
* добавление события в хранилище;
* изменение события в хранилище;
* удаление события из хранилища;
* листинг событий;
* пр. на усмотрение разработчика.
Описание сущности и методов представлено в [ТЗ](./CALENDAR.MD).
Создать объекты ошибок, соответствующие бизнес ошибкам, которые необходимо выделить.
Например, `ErrDateBusy` - данное время уже занято другим событием.
Создать две реализации интерфейса выше:
* **in-memory**: храним события в памяти (т.е. просто складываем объекты в словари/слайсы, не забывая про критические секции);
* **sql**: храним события в полноценной СУБД путем использования SQL-запросов в соответствующих методах.
Вынести в конфиг параметр, отвечающий за то, какую из реализаций использовать при старте.
Для работоспособности второй реализации необходимо:
* установить СУБД (например PostgreSQL) локально (или сразу через Docker, если знаете как);
* создать базу данных и пользователя для проекта календарь;
* реализовать схему данных (таблицы, индексы) в виде отдельного SQL или go-файла (файл миграции)
и сохранить его в репозиторий;
* применять миграции руками или на старте сервиса;
* вынести настройки подключения к БД в конфиг проекта.
Полезные библиотеки:
* https://github.com/jmoiron/sqlx
* https://github.com/pressly/goose#go-migrations
**Использовать ORM (например, gorm) не допускается**.
Календарь должен использовать хранилище через интерфейс.
### 4) Запуск простого HTTP-сервера
Запуск календаря должен стартовать HTTP-сервер. `host` и `port` сервера вынести в конфиг.
На данном этапе сервер не должен быть связан с бизнес логикой приложения и должен иметь
только один "hello-world" endpoint ("/", "/hello", etc.).
Информация об обработанном запросе должна выводиться в log-файл:
* IP клиента;
* дата и время запроса;
* метод, path и версия HTTP;
* код ответа;
* latency (время обработки запроса, посчитанное, например, с помощью middleware);
* user agent, если есть.
Пример лога:
```text
66.249.65.3 [25/Feb/2020:19:11:24 +0600] GET /hello?q=1 HTTP/1.1 200 30 "Mozilla/5.0"
```
### 5) Юнит-тесты
Минимальный обязательный набор - тесты на **in-memory** реализацию хранилища (на основную логику, бизнес-ошибки и конкуррентно-безопасность).
Остальные тесты на усмотрение разработчика.
### 6) Makefile
Проект должен иметь в корне файлы go.mod и Makefile, последний должен описывать команды:
* `make build` - скомпилировать бинарный файл сервиса;
* [`make run`] - опционально, собрать и запустить сервис с конфигом по умолчанию;
* `make test` - запустить юнит-тесты (с флагом -race);
* `make lint` - запустить golangci-lint (при необходимости добавить свой `.golangci.yml`);
* [`make migrate`] - опционально, если миграции применяются руками;
* пр. на усмотрение разработчика
### Об архитектуре
Проект должен следовать:
* https://github.com/golang-standards/project-layout
* https://golang.org/doc/effective_go.html#package-names
* https://rakyll.org/style-packages/
* https://en.wikipedia.org/wiki/Dependency_injection
Важно понять, что в Go нет серебряной пули по архитектуре.
Ссылки ниже могут дать полезные концепции, но не стоит слепо следовать им:
* https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1
* https://www.ardanlabs.com/blog/2017/02/package-oriented-design.html
* https://github.com/marcusolsson/goddd
* чистая архитектура (clean architecture):
- https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
- https://medium.com/@zhashkevych/чистая-архитектура-на-golang-cccbfdc95eba
Используйте стандартный layout, соблюдайте направление зависимостей, выделяйте пакеты по концепции,
не забывайте про внедрение зависимостей через интерфейсы и у вас всё получится!
Не забываем про стайл гайд, например:
* https://github.com/uber-go/guide/blob/master/style.md
* https://github.com/cristaloleg/go-advice
### В данном ДЗ не нужно
* Реализовывать HTTP, GRPC и пр. API к микросервису.
* Писать .proto-файлы.
Это всё будет позже.
### Критерии оценки
- Makefile заполнен и пайплайн зеленый - 1 балл
- Понятность и чистота кода (включая факт, что проект разбит
на пакеты по определенной логике) - до 2 баллов
- Реализовано конфигурирование сервиса - 1 балл
- Используется логгер и он настраивается из конфига - 1 балл
- Реализовано хранилище:
- in-memory - 1 балл
- sql + миграции - 2 балла
- Запускается простой HTTP-сервер, мидлвара удовлетворяет ТЗ - 1 балл
- Присутствуют юнит-тесты - 1 балл
#### Зачёт от 7 баллов

View File

@@ -0,0 +1,36 @@
## Домашнее задание №13 «API к Календарю»
Необходимо реализовать HTTP и GRPC API для сервиса календаря.
Методы API в принципе идентичны методам хранилища и [описаны в ТЗ](./CALENDAR.MD).
Для GRPC API необходимо:
* создать отдельную директорию для Protobuf спецификаций;
* создать Protobuf файлы с описанием всех методов API, объектов запросов и ответов (
т.к. объект Event будет использоваться во многих ответах разумно выделить его в отдельный message);
* создать отдельный пакет для кода GRPC сервера;
* добавить в Makefile команду `generate`; `make generate` - вызывает `go generate`, которая в свою очередь
генерирует код GRPC сервера на основе Protobuf спецификаций;
* написать код, связывающий GRPC сервер с методами доменной области (бизнес логикой);
* логировать каждый запрос по аналогии с HTTP API.
Для HTTP API необходимо:
* расширить "hello-world" сервер из [ДЗ №12](./12_README.md) до полноценного API;
* создать отдельный пакет для кода HTTP сервера;
* реализовать хэндлеры, при необходимости выделив структуры запросов и ответов;
* сохранить логирование запросов, реализованное в [ДЗ №12](./12_README.md).
Общие требования:
* должны быть реализованы все методы;
* календарь не должен зависеть от кода серверов;
* сервера должны запускаться на портах, указанных в конфиге сервиса.
**Можно использовать https://grpc-ecosystem.github.io/grpc-gateway/.**
### Критерии оценки
- Makefile заполнен и пайплайн зеленый - 1 балл
- Реализовано GRPC API и `make generate` - 3 балла
- Реализовано HTTP API - 2 балла
- Написаны юнит-тесты на API - до 2 баллов
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@@ -0,0 +1,48 @@
## Домашнее задание №14 «Кроликизация Календаря»
Необходимо реализовать "напоминания" о событиях с помощью RabbitMQ (кролика).
Общая концепция описана в [техническом задании](./CALENDAR.MD).
Порядок выполнения ДЗ:
* установить локально очередь сообщений RabbitMQ (или сразу через Docker, если знаете как);
* создать процесс Планировщик (`scheduler`), который периодически сканирует основную базу данных,
выбирая события о которых нужно напомнить:
- при запуске процесс должен подключаться к RabbitMQ и создавать все необходимые структуры
(топики и пр.) в ней;
- процесс должен выбирать сообытия для которых следует отправить уведомление (у события есть соотв. поле),
создавать для каждого Уведомление (описание сущности см. в [ТЗ](./CALENDAR.MD)),
сериализовать его (например, в JSON) и складывать в очередь;
- процесс должен очищать старые (произошедшие более 1 года назад) события.
* создать процесс Рассыльщик (`sender`), который читает сообщения из очереди и шлёт уведомления;
непосредственно отправку делать не нужно - достаточно логировать сообщения / выводить в STDOUT.
* настройки подключения к очереди, периодичность запуска и пр. настройки процессов вынести в конфиг проекта;
* работу с кроликом вынести в отдельный пакет, который будут использовать пакеты, реализующие процессы выше.
Процессы не должны зависеть от конкретной реализации RMQ-клиента.
В результате компиляции проекта (`make build`) должно получаться 3 отдельных исполняемых файла
(по одному на микросервис):
- API (`calendar`);
- Планировщик (`calendar_scheduler`);
- Рассыльщик (`calendar_sender`).
Каждый из сервисов должен принимать путь файлу конфигурации:
```bash
./calendar --config=/path/to/calendar_config.yaml
./calendar_scheduler --config=/path/to/scheduler_config.yaml
./calendar_sender --config=/path/to/sender_config.yaml
```
После запуска RabbitMQ и PostgreSQL процессы `calendar_scheduler` и `calendar_sender`
должны запускаться без дополнительных действий.
### Критерии оценки
- Makefile заполнен и пайплайн зеленый - 1 балл
- Работа с RMQ выделена в отдельный пакет, код не дублируется - 1 балл
- Реализован Планировщик:
- отсылает уведомления о выбранных событиях - 2 балла
- удаляет старые события - 1 балл
- Реализован Рассыльщик - 2 балла
- Можно собрать сервисы одной командой (`make build`) - 1 балл
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@@ -0,0 +1,53 @@
## Домашнее задание №15 «Докеризация и интеграционное тестирование Календаря»
Данное задание состоит из двух частей.
Не забываем про https://github.com/golang-standards/project-layout.
### 1) Докеризация сервиса
Необходимо:
* создать Dockerfile для каждого из процессов (Календарь, Рассыльщик, Планировщик);
* собрать образы и проверить их локальный запуск;
* создать docker-compose файл, который запускает PostgreSQL, RabbitMQ и все микросервисы вместе
(для "неродных" сервисов использовать официальные образы из Docker Hub);
* при желании доработать конфигурацию так, чтобы она поддерживала переменные окружения
(если вы используете библиотеку, то скорее всего она уже это умеет); в противном случае
придется "подкладывать" конфиг сервису с помощью Dockerfile / docker-compose -
при этом можно "заполнять" конфигурационный файл из переменных окружения, например
```bash
$ envsubst < config_template.json > config.json
```
* если миграции выполняются руками, а не на старте сервиса, то также в docker-compose
должен запускаться one-shot скрипт, который делает это (применяет SQL миграции,
создавая структуру БД).
* порты серверов, предоставляющих API, пробросить на host.
У преподавателя должна быть возможность запустить весь проект с помощью команды
`make up` (внутри `docker-compose up`) и погасить с помощью `make down`.
HTTP API, например, после запуска должно быть доступно по URL **http://localhost:8888/**.
### 2) Интеграционное тестирование
Необходимо:
* создать отдельный пакет для интеграционных тестов.
* реализовать интеграционные тесты на языке Go; при желании можно использовать
[godog](https://github.com/cucumber/godog) / [ginkgo](https://github.com/onsi/ginkgo), но
обязательным требованием это **не является**.
* создать docker-compose файл, поднимающий все сервисы проекта + контейнер с интеграционными тестами;
* расширить Makefile командой `integration-tests`, `make integration-tests` будет запускать интеграционные тесты;
**не стоит смешивать это с `make test`, иначе CI-пайплайн не пройдёт.**
* прикрепить в Merge Request вывод команды `make integration-tests`.
Преподаватель может запустить интеграционные тесты с помощью команды `make integration-tests`:
- команда должна поднять окружение (`docker-compose`), прогнать тесты и подчистить окружение за собой;
- в случае успешного выполнения команда должна возвращать 0, иначе 1.
### Критерии оценки
- Проект полностью запускается и останавливается с помощью `make up` / `make down` - 3 балла
- Интеграционные тесты запускаются с помощью `make integration-tests`. Команда возвращает верный код ответа - 1 балл
- Интеграционные тесты покрывают бизнес сценарии:
- добавление события и обработка бизнес ошибок - 2 балла
- получение листинга событий на день/неделю/месяц - 2 балла
- отправка уведомлений (необходимо доработать sender так, чтобы он информировал куда-то о статусе уведомления (БД/кролик)) - 2 балла
#### Зачёт от 7 баллов

View File

@@ -0,0 +1,96 @@
## Техническое задание на сервис «Календарь»
### Общее описание
Сервис "Календарь" представляет собой максимально упрощенный сервис для хранения календарных событий и отправки уведомлений.
Сервис предполагает возможность:
* добавить/обновить событие;
* получить список событий на день/неделю/месяц;
* получить уведомление за N дней до события.
Сервис НЕ предполагает:
* авторизации;
* разграничения доступа;
* web-интерфейса.
### Архитектура
Полностью завершенный сервис состоит из 5 процессов.
#### 1) API
API предоставляет собой GRPC и HTTP интерфейсы для пользователей. API реализуют основные методы сервиса.
Так как авторизация и разделение доступа выходят за рамки сервиса, мы предполагаем, что ID пользователя
просто передается через параметры / метаданные / заголовок запроса.
#### 2) Планировщик
Планировщик - это фоновый процесс, который не взаимодействует с пользователем и выполняет периодические задания:
* выбор событий, требующих уведомления и отправка уведомлений в очередь рассыльщику;
* очистка старых (более 1 года назад) событий.
#### 3) Рассыльщик
Рассыльщик - это фоновый процесс, занимающийся отправкой уведомлений.
В рамках задания нет необходимости реальной отправки сообщений, достаточно, если рассыльщик просто будет
записывать их в лог / выводить в STDOUT.
#### 4) СУБД
Реляционная СУБД (например, PostgreSQL) - хранит информацию о событиях.
#### 5) Очередь сообщений
Очередь сообщений (RabbitMQ) - используется для передачи уведомлений от Планировщика Рассыльщику.
### Описание сущностей
#### Событие
Событие - основная сущность, содержит в себе поля:
* ID - уникальный идентификатор события (можно воспользоваться UUID);
* Заголовок - короткий текст;
* Дата и время события;
* Длительность события (или дата и время окончания);
* Описание события - длинный текст, опционально;
* ID пользователя, владельца события;
* За сколько времени высылать уведомление, опционально.
#### Уведомление
Уведомление - временная сущность, в БД не хранится, складывается в очередь для рассыльщика, содержит поля:
* ID события;
* Заголовок события;
* Дата события;
* Пользователь, которому отправлять.
### Описание методов
* Создать (событие);
* Обновить (ID события, событие);
* Удалить (ID события);
* СписокСобытийНаДень (дата);
* СписокСобытийНаНеделю (дата начала недели);
* СписокСобытийНaМесяц (дата начала месяца).
### Конфигурация, логирование, контейнеризация
Рекомендуется собирать проект в виде трех бинарных файлов, по одному на каждый микросервис.
Каждый из сервисов должен принимать путь файлу конфигурации:
```text
./calendar --config=/path/to/calendar_config.yaml
./calendar_scheduler --config=/path/to/scheduler_config.yaml
./calendar_sender --config=/path/to/sender_config.yaml
```
Проект следует оформить в виде набора контейнеров для каждого из микросервисов.
Поскольку запуск будет осуществлять в контейнерах - логи нужно печатать в STDOUT.
Сервисы должны получать настройки (например, адрес и логин в СУБД) через переменные окружения.
При желании можно использовать конфиг viper, с поддержкой переменных окружения.
Развертывание микросервиса должно осуществляться командой `docker-compose up` в директории с проектом.
### Тестирование
Для проекта необходимо реализовать интеграционные тесты, т.е. тесты проверяющие работу на уровне API.
Интеграционные тесты можно создать с помощью BDD, а можно просто с помощью стандартного пакета `testing`
и запускать их в отдельном контейнере в том же compose окружении, что и остальные микросервисы.
В таком случае тесты смогут получать адрес работающего сервиса через переменные окружения, выполнять запросы к нему,
а также проверять состояние базы данных и пр.
### Этапы разработки
Разработка разделена на следующие этапы (ДЗ):
* Разрабатываем скелет сервиса: конфигурирование, работа с базой, простой web-сервер.
* Добавляем HTTP и GRPC API.
* Выделяем Планировщик и Рассыльщик. Интегрируемся с очередью сообщений.
* Создаем Docker-образы, docker-compose конфигурации, пишем интеграционные тесты.
Таким образом, на каждом этапе мы получаем осмысленный проект.