Как разработать веб-приложение уровня Enterprise Application с нуля
Авторы подписки «Огонь» поделились личным опытом и рассказали, как разработать Enterprise Application с нуля. Код — внутри.
7К открытий8К показов
План
- Вводная
- Бизнес-требования
- Требования к архитектуре
- Требования к веб-фреймворку
- Выбор веб-фреймворка
- Начало работы над проектом
Вводная
Всем привет, сегодня мы начинаем серию статей о том, как разработать коммерческое веб-приложение с нуля. В этом материале будут продемонстрированы решения, используемые командой разработки подписки «Огонь» Ogon.ru.
Алексей Соломонов
СТО подписки «Огонь»
Прежде чем начать писать код, давайте поймем, что мы хотим получить в итоге. Для этого составим бизнес-требования и технические требования.
Бизнес-требования
В рамках серии статей мы разработаем интернет-магазин. Давайте разложим функционал магазина на несколько простых пользовательских историй. Я, как пользователь сайта интернет-магазина, могу:
- Зарегистрироваться на сайте.
- Авторизоваться на сайте.
- Посмотреть список товаров.
- Посмотреть карточку товара.
- Положить товар в корзину.
- Управлять товарами в корзине.
- Оплатить товары в корзине.
- Получить чек на почту.
Требования к архитектуре
Как мы видим, весь функционал раскладывается на три предметные области или домена:
- Пользователи.
- Товары.
- Заказы.
Разделение всего функционала на домены является одним из принципов DDD. Разделять проект на домены хорошо, потому что:
- Домен описывается набором сущностей, характерных для него.
- Домен имеет набор сервисов, которые управляют этими сущностями.
- Домены общаются между собой через сервисы.
Архитектура, разработанная на принципах DDD, имеет очевидные преимущества:
- Бизнес-логика пишется один раз внутри домена и переиспользуется другими. Значит изменение логики либо не затронет остальные части проекта, либо будут дешевле с точки зрения времени и денег.
- Все домены имеют схожую структуру, значит разработчикам будет легче переключаться между доменами и разбираться, что там происходит.
Таким образом, мы получаем проект, который проще разрабатывать и тестировать потому, что каждый домен является центром силы, раз он делает только свою часть работы. Исходя из того, что мы выделили три домена, нам необходимо реализовать три микросервиса:
- Пользователи.
- Товары.
- Заказы.
Команда разработки Ogon.ru начинает новую фичу с проектирования API и схемы СУБД, т.к. готовая фича — это работающая и серверная, и клиентская часть. Кроме того, фича должна соответствовать критериям приемки, значит и тестировщики должны быть сразу вовлечены в процесс. Другими словами, надо организовать работу N человек, а для этого надо с чего-то начать. Давайте начнем с API, но сначала поймем, что мы хотим от веб-фреймворка.
Требования к веб-фреймворку
Во время разработки API Ogon.ru мы использовали свой положительный опыт из прошлых проектов и рекомендации REST API Tutorial. Таким образом, веб-фреймворк должен поддерживать:
- Маршрутизацию запросов по схеме.
- Маршрутизацию запросов по заголовкам.
- Маршрутизацию по URL-пути.
- Переменные в URL-пути.
- HTTP-методы.
- Контекст запроса.
- Middleware.
- Получение запросов в JSON, XML и т. д.
- Встроенную десериализацию запросов и ответов.
- Генерацию документации для фронтэндщиков, тестировщиков и наших партнеров.
- Минимизацию трафика между микросервисами.
Выбор веб-фреймворка
Проведя несложный поиск, можно легко убедиться, что:
- Веб-фреймворков много.
- Все они в той или иной степени реализуют все наши требования, кроме документации и минимизации трафика.
Как уже говорилось ранее, над фичей работают много человек, с фичей могут работать партнеры, значит документирование API становится важной задачей. Для документирования API можно воспользоваться:
На наш взгляд RAML и API Blueprint слабо представлены в golang, т.к. количество проектов, использующих эти технологии, невелико по сравнению с OpenAPI. Строить коммерческое веб-приложение на непопулярных технологиях — это риски для компании и для команды разработки, т.к. при самом плохом раскладе придется поддерживать выбранную технологию. Мы остановили свой выбор на OpenAPI, и тут на помощь нам пришел GRPC в реализации Go Protobuf и GRPC-Gateway.
GRPC используется в ситуациях, когда микросервисы общаются между собой внутри сети. GRPC-Gateway оборачивает GRPC-методы в красивое REST API. В качестве альтернативы можно было взять:
- Go Swagger, но сам swagger более многословный, чем protobuf, вдобавок не хочется гонять JSON внутри сети, поэтому он нам не подошел.
- Swag, но этот проект выглядит костыльным решением для команд, которым потребовалось документировать свое API через полгода разработки на своем любимом фреймворке. Есть риски, что документация API на основе комментариев в коде и сама реализация разойдутся, потому что разработчик забыл их актуализировать, при этом нет никаких инструментов валидации этой ситуации.
Начало работы над проектом
Теперь давайте потрогаем технологии руками и для начала:
- Установим зависимости:
a. protoc
b. docker
c. golang
d. buf, после 24 февраля введена блокировка для России, решается установкой VPN
e. make - Создадим структуру проекта на основании популярного решения.
- Настроим генерацию сервисов и сущностей из protobuf.
Генерация сервисов и сущностей из protobuf
Установим зависимости для GRPC и GRPC-Gateway:
Настроим buf
- Создадим enterprise-application/buf.yaml
- Создадим enterprise-application/buf.gen.yaml
- Выполним buf mod update
Теперь мы готовы описывать сервисы, используя рекомендации buf и дополнительные опции protopatch, которые сделают генерацию кода более лаконичной.
Давайте опишем сервисы и сущности корзины enterprise-application/api/proto/v1/order.proto:
enterprise-application/api/proto/v1/order_service.proto:
По аналогии с сервисом корзины описываем:
- Микросервис продуктов и его сущности.
- Микросервис пользователей и его сущности.
Генерируем golang-код:
buf generate
В результате мы должны получить сгенерированный код.
Теперь давайте приступим к реализации бизнес-логики, начиная с микросервиса продуктов, как самого простого.
Внутренняя архитектура микросервиса будет основана на принципах DDD:
- Доменный слой — описание сущностей, интерфейсов сервисов и репозиториев.
- Инфраструктурный слой — реализация интерфейсов репозиториев и сервисов.
- Слой приложения — реализация GRPC-сервиса.
Доменный слой
В рамках этой статьи мы опустим описание сущностей и репозиториев, т.к. на текущий момент не работаем с каким-либо постоянных хранилищем. Опишем сервис для управления продуктами.
pkg/product/domain/service/product.go:
Обратите внимание, что имя пакета отличается от имени папки из-за того, что в каждом домене будут свои сервисы, соответственно будут конфликты при импорте между order/domain/service и product/domain/service. С помощью такого простого трюка современные IDE будут импортировать пакеты и сразу вешать на него алиас.
Микросервисы заказов и пользователей реализуются и собираются аналогичным способом.
Теперь необходимо собрать все воедино и проверить работоспособность, для этого соберем docker-compose.yml.
deployments/docker-compose.yml:
Поднимаем наш проект
docker-compose -p enterprise_application -f deployments/docker-compose.yml up -d --build --force-recreate
Пропишем 127.0.0.1 enterprise.application.local в /etc/hosts.
Введем в адресную строку браузера http://enterprise.application.local и увидим нашу документацию в виде swagger-файла:
Swagger-файл — это не только наглядный, но и рабочий документ, который позволяет провалиться в конкретный метод, выполнить запрос и получить ответ:
Итак, в рамках данной статьи нам удалось
- Получить бизнес-требования.
- На их основании выработать требования к архитектуре.
- Подобрать веб-фреймворк.
- Реализовать микросервисы.
- Собрать и развернуть микросервисы на локальном стенде.
- Подготовить документацию по API.
Код проекта представлен в репозитории.
В следующей статье мы научимся:
- Работать с middleware.
- Настраивать приложения с помощью конфигурационного файла и переменных окружения.
- Автоматически внедрять зависимости.
- Мокать и тестировать код.
7К открытий8К показов