Skip to main content

Структура 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 - классы FormRequest
  • Queries - классы Spatie\QueryBuilder
  • Resources - классы JsonResource
  • Tests - тесты 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) например.