itleague/microservice

General library for microservices

1.1.8 2021-01-19 15:24 UTC

This package is auto-updated.

Last update: 2022-05-19 15:35:35 UTC


README

Latest Stable Version Total Downloads Latest Unstable Version License

Описание

Библиотека упрощает и ускоряет разработку новых микросервисов, особенно при использовании базы postgres. В основном состоит из хелперов и абстрактных классов, в которых уже прописана базовая логика большинства микросервисов: многоязычность, кэширование ответов, работа с полями типа file, аутентификация пользователя, валидация входящих данных, обработка ошибок и формирование ответов.

Установка

composer require itleague/microservice

Включает следующие библиотеки

Многоязычность

Полностью готовый функционал для работы с т. н. переводимыми полями. В первую очередь необходимо создать таблицу для хранения списка используемых языков:

php artisan microservice:languages-table

Будет создана новая миграция для таблицы. Один из языков определяется как язык по умолчанию.

Далее, для любой сущности можно определить некий набор полей, значения которых отличаются для разных языков, вынести их в отдельную таблицу. Значения на запрашиваемом языке будут автоматически подтягиваться в поле translation базовой сущности. При отсутствии необходимых переводов подтягиваются значения на языке по умолчанию.

Для корректной работы функционала необходимо в модели базовой сущности подключить трейт ITLeague\Microservice\Traits\Models\Translatable, а в модели с переводимыми полями — трейт ITLeague\Microservice\Traits\Models\ModelForTranslatedAttributes

Язык возвращаемых сущностей определяется заголовком запроса Accept-Language. Также в ответе присутствует заголовок Content-Language, который содержит двухбуквенный код используемого языка.

Заполнение переводимых значений происходит прямой передачей всего набора полей в базовую сущность. Необходимо только определить fillable поля моделей.

Для удобства фильтрации и сортировки к базовой модели добавлены scopes whereTranslatable, orderByTranslatable и orderByDescTranslatable. Также для базовой модели есть scope joinTranslatableTable, который джоинит к запросу таблицу с переводимыми полями и таблицу с языками. Стоит учитывать, что эти scopes решают только самые простые случаи. Для каких-то более сложных запросов можно брать за основу содержимое этих методов и писать своё.

Миграции

Основная документация по расширению laravel-pg-extensions есть тут. Я взял собственный форк этого репозитория и добавил некоторые методы:

  • primaryUuid: добавляет первичный ключ типа uuid с автоматической генерацией значений
  • immutable: делает значения в колонке неизменяемыми. Особенно актуально для первичных ключей типа uuid
  • dropImmutable: удаляет триггер неизменности значений колонки
  • array: создаёт поле типа ARRAY. Вместе с ней добавляется хранимая процедура для преобразования строки в формате json (которую может сформировать Laravel) в данные в формате ARRAY для PostgreSQL.
  • watchUpdate/watchInsert: добавляет поля create_at, created_by, updated_at, updated_by и триггер, который обновляет соответствующие даты средствами БД. Рекомендуется добавлять метод к таблицам основных сущностей
  • watchDelete: добавляет поля deleted_at, deleted_by и триггер, который запрещает удаление записей из таблицы. Также рекомендуется добавлять к таблицам основных сущностей
  • addWatchColumns: добавляет поля create_at, created_by, updated_at, updated_by без триггера. Имеет смысл вызывать отдельно, когда нужно сделать необязательными поля created_by и updated_by
  • addSoftDeleteColumns: просто добавляет поля deleted_at и deleted_by
  • touchParent: метод добавляет триггер, который меняет дату родительской записи при обновлении текущей записи с foreign key. Вызывается непосредственно для ForeignKeyDefinition.
  • enum: создаёт поле типа ENUM (а не текстовое поле с constraint, как было изначально). В моделях работать с ним можно как с обычной строкой. В PostgreSQL с типом ENUM есть некоторые ограничения при работе со списком доступных значений (см. документацию PostgreSQL)

Работа с полями типа array

На текущий момент реализованы только корректное чтение и запись данных в поля типа ARRAY. Для использования функционала необходимо в свойстве casts модели указать необходимый тип массива. Тип можно выбрать из готовых наследников класса ITLeague\Microservice\Casts\ArrayCast или написать свой по подобию. Также к модели необходимо подключить трейт ITLeague\Microservice\Traits\Models\WithArrayAttribute. После этого со значением можно работать как с обычным php-массивом.

Работа с полями типа file

В базе поля типа file хранятся в виде UUID файла в хранилище. В ответах такие поля возвращаются в виде метаданных самого файла (см. документацию для сервиса storage). Всё, что необходимо для задания такого типа полей - у модели заполнять свойство file параметрами: permission, sizes, force, is_multiple.

При force равном true не будет происходить подтверждения файла при обновлении значения поля.

Если is_multiple равно true, то поле базы должно быть типа uuid[]. Работа с этим полем в модели ровно такая же, как с обычным массивом. Информацию по миграциям для таких полей см. в соответствующем разделе.

Важно! Сохранять модели с файловыми полями могут только авторизованные пользователи.

Кэширование ответов

Для кэширования ответов используется паттерн Repository с декоратором. Ответы для всех get-запросов кэшируются. Кэш сбрасывается при обновлении/добавлении/удалении/восстановлении сущности. Есть возможность указать время кэширования и доп. сущности, кэш которых будет сбрасываться вместе с текущим.

Для правильной работы необходимо для каждой сущности создать интерфейс, класс репозитория и класс декоратора Caching. Наследоваться можно от обычного интерфейса ITLeague\Microservice\Repositories\Interfaces\RepositoryInterface или его Restorable версии, которая включает в себя ещё и метод restore для восстановления удалённой сущности.

Также уже готов абстрактный класс ITLeague\Microservice\Http\Controllers\ResourceController (и Restorable версия), в котором есть 5 методов для CRUD api.

Шина сообщений на основе RabbitMQ

RabbitMQ используется для асинхронной передачи сообщений между сервисами. Один сервис может отправить событие в шину с помощью фасада MicriserviceBus

MicroserviceBus::push('event_name', $data)

Другой сервис может подписаться на это событие

MicroserviceBus::listen('event_name', Handler::class)

Важно! Подписка на события должна происходить после регистрации провайдера библиотеки!

В качестве обработчика события может выступать любой класс с интерфейсом ITLeague\Microservice\Http\Bus\EventHandler.

Запуск воркера для прослушивания событий у сервиса происходит командой из laravel-queue-rabbitmq

php artisan rabbitmq:consume

Воркер желательно перезапускать после каждого коммита. Также для стабильной работы необходимо подключить супервизор.

Базовый класс для запросов в другие сервисы

В библиотеке есть абстрактный класс ITLeague\Microservice\Http\Services\BaseService, в котором можно использовать методы client() (возвращает объект http-клиента для отправки запросов) и query() (непосредственно отправляет запрос и обрабатывает от сервиса).

В качестве примера использования можно посмотреть фасад Storage в библиотеке. Но для корректной работы класса требуется только наличие трёх настроек: base_uri, prefix и timeout

Остальной функционал базового класса сущности

Использование метода getUnfilledAttributes

При создании/обновлении каждой сущности все значения полей, которые не входят в набор fillable-полей, помещаются в массив unfilled. Его можно получить методом getUnfilledAttributes и использовать, например, в событии saved для заполнения связанных сущностей. Именно так, кстати, заполняются переводимые поля сущности. Посмотреть пример можно в трейте ITLeague\Microservice\Traits\Models\Translatabe

Создание дополнительных фильтров и сортировок сущности

По умолчанию любую сущность можно фильтровать по её идентификатору. В конструкторе модели можно задать дополнительные фильтры и сортировки. Для этого необходимо заполнить свойства модели filters и sorts соответственно. Ключами являются названия полей, значениями - closure с описанием логики фильтра и сортировки.

Также необходимо добавить новые фильтры и сортировки в правила валидации сущности (см. далее).

Запрос из базы только необходимых связей сущности

Для всех запрашиваемых сущностей есть возможность указать только необходимые в ответе поля. И если из таблицы сущности в базе выбираются все поля, то связанные сущности из других таблиц запрашивать необязательно, если они не требуются в ответе. Для этого у сущности существует свойство eagerLoad, в котором перечисляются все отношения сущности. Если какое-то из этих отношений не требуется в get-запросе, то оно не будет доставаться из базы.

Аутентификация

Для каждого запроса обрабатываются заголовки x-authenticated-userid и x-authenticated-scope. В результате запрос либо остаётся неавторизованным, либо создаётся экземпляр класса ITLeague\Microservice\Models\User, унаследованный от GenericUser. В дальнейшем аутентификацию можно проверять фасадом Auth.

Также добавлен полный доступ ко всем эндпоинтам для супер-админа.

Валидация входящих данных

Идея в том, чтобы валидировать все входящие данные до передачи их в бд. Для этого у каждой сущности есть свойство rules со следующими ключами:

  • store - набор правил валидации для сохранения сущности. Включает в себя переводимые поля и остальные поля, которые попадают в unfilled.
  • update - набор правил для обновления сущности. Обычно отличается от store отсутствием правила для первичного ключа и отсутствием правила required.
  • filter - набор правил валидации фильтра, который используется при запросе списка сущностей. Фильтр по первичному ключу уже задан для всех сущностей, но для использования его необходимо также указать в правилах. Для удобства в валидатор добавлены правила array_or_integer и array_or_string:length.
  • sort - правило, ограничивающее список полей, по которым возможно сортировать список сущностей. Для удобства есть правило sort_in:field_1,field_2....

Также есть валидация параметра fields. Никакие правила специально задавать не нужно, используется набор ключей соответствующего ресурса сущности. Необходимо только указать для ресурса трейт ITLeague\Microservice\Traits\FilterableResource и обернуть возвращаемый методом toArray массив в метод fields.

Формирование ответов

Для единообразного формирования ответов используется трейт ITLeague\Microservice\Traits\ApiResponse, который можно подключить для любого класса, возвращающего ответы. В основном это контроллеры и хендлер обработки ошибок.

Обработка ошибок

Для обработки всех исключений уже подключен ITLeague\Microservice\Exceptions\Handler. Пока нет возможности добавлять в него свои типы исключений.

Если переменная APP_DEBUG установлена в true, то используется штатный рендер ошибок Lumen. Иначе ошибки выводятся в виде json-объекта.

Файловое хранилище

В классе ITLeague\Microservice\Http\Services\StorageService реализованы все методы для работы с файловым хранилищем. Доступ к нему может осуществляться через фасад ITLeague\Microservice\Facades\Storage` За доп. информацией по ним см. документацию сервиса storage

Базовый абстрактный класс для тестов

К базовому классу TestCase от Lumen добавлены свойства Faker\Factory faker, user/admin/superAdmin (экземпляры класса ITLeague\Microservice\Models\User с соответствующими правами). Также повешен постоянный 204 ответ на обращения к файловому хранилищу. Ещё есть стандартная структура возвращаемых ошибок.