Initial commit
This commit is contained in:
160
hw12_13_14_15_calendar/docs/12_README.md
Normal file
160
hw12_13_14_15_calendar/docs/12_README.md
Normal 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 баллов
|
||||
36
hw12_13_14_15_calendar/docs/13_README.md
Normal file
36
hw12_13_14_15_calendar/docs/13_README.md
Normal 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 баллов
|
||||
48
hw12_13_14_15_calendar/docs/14_README.md
Normal file
48
hw12_13_14_15_calendar/docs/14_README.md
Normal 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 баллов
|
||||
53
hw12_13_14_15_calendar/docs/15_README.md
Normal file
53
hw12_13_14_15_calendar/docs/15_README.md
Normal 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 баллов
|
||||
96
hw12_13_14_15_calendar/docs/CALENDAR.MD
Normal file
96
hw12_13_14_15_calendar/docs/CALENDAR.MD
Normal 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 конфигурации, пишем интеграционные тесты.
|
||||
|
||||
Таким образом, на каждом этапе мы получаем осмысленный проект.
|
||||
Reference in New Issue
Block a user