161 lines
10 KiB
Markdown
161 lines
10 KiB
Markdown
|
## Домашнее задание №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 баллов
|