Структура backend сервиса на языке PHP
Ensi Backend Skeleton (EBS) расширяет стандартный шаблон веб-приложения на фреймворке Laravel добавляя в него дополнительное разделение кода на версии api и домены бизнес-логики.
Спецификация API
Описание api сервиса в формате OAS3 интегрировано в код самого сервиса и используется для генерации серверного кода и клиентских библиотек для работы с сервисом.
Файлы спецификации располагаются в /public/api-docs/v1/
примерно вот так:
public/api-docs
└── v1
├── common_schemas.yaml
├── errors.yaml
├── foo_bar_domain
│ ├── enums
│ │ └── foo_enum.yaml
│ ├── shcemas
│ │ └── fizz_buzz.yaml
│ └── paths.yaml
└── index.yaml
Здесь public/api-docs/v1/index.yaml
это главный файл спецификации, в нём файле описаны пути эндпоинтов,
но описания эндпоинтов вынесены в другие файлы.
Рядом располагаются файлы с общими схемами и папки, по одной на домен бизнес-логики.
Внутри папки домена есть файл paths.yaml
, в котором перечисляются описания эндпоинтов, но без описания структуры объектов.
Структура объектов задаётся в файлах внутри папки schemas
.
Перечисления вынесены в отдельную папку enums
.
Файл index.yaml
регистрируется в нескольких местах:
- в
config/serve-stoplight.php
для показа спецификации через stoplight - в
config/openapi-server-generator.php
для генерации серверного кода - в
config/openapi-client-generator.php
для генерации пакетов клиента к сервису
Сгенерированный серверный код
На основании вышеописанной спецификации генерируются следующие файлы:
- файл регистрации роутов
app/Http/ApiV1/OpenApiGenerated/routes.php
- глобально зарегистрированные перечисления
app/Http/ApiV1/OpenApiGenerated/Enums/
- контроллеры согласно путям, указанным поле
x-lg-handler
описания эндпоинта - заготовки FormRequest в папке
Requests
соседней с папкой контроллера - заготовки pest тестов в папке
Tests
соседней с папкой контроллера
Папка с контроллерами (определяемая через поле x-lg-handler
) задаёт отправную точку для формирования т.н. модуля api.
Структура у модуля следующая:
Controllers
- папка с контроллерамиRequests
- классы FormRequestQueries
- классы Spatie\QueryBuilderResources
- классы JsonResourceTests
- тесты pest для эндпоинтов
Пример:
app/Http/ApiV1/Modules/
├── Banners
│ ├── Controllers
│ ├── Queries
│ ├── Requests
│ ├── Resources
│ └── Tests
└── Landings
├── Controllers
├── Queries
├── Requests
├── Resources
└── Tests
Домены бизнес-логики
Примерно таким же образом организовано разделение доменного кода. Каждый домен является папкой вида app/Domain/<domain-name>
со следующей структурой:
app/Domain/
├── Categories
│ ├── Actions
│ ├── Concerns
│ ├── Jobs
│ ├── Models
│ └── Observers
└── Support
├── Actions
├── Concerns
├── Events
└── Models
Это те же самые папки из обычного шаблона Laravel, их названия говорят сами за себя, разница лишь в том что они разбиты на группы по доменам.
Отдельно стоит упомянуть домен Support
- он не имеет какого-то бизнесс-смысла, а содержит общий для всех доменов код.
Action классы
В сервисах Ensi код организован в т.н. Action-классы. Каждое осмысленное самостоятельное действие выделяется в отдельный класс с методом execute. Эти классы не реализуют никакой интерфейс, каждый action может принимать и возвращать из execute любые параметры которые ему требутся, польза от actions не техничекая, а организационная - точки входа в "большие действия" сервиса расположены в ожидаемом месте и имеют предсказуемый способ вызова.
Action - это M из MVC. Это значит что он не должен заниматься форматированием данных для ответа - для этого есть Resources,
не должен знать о http запросе - параметры запроса передаются как отдельные аргументы метода execute, как DTO или как array в тех случаях
когда это идиома фреймворка, $model->fill($data)
например.