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