Готовим REST API & Symfony 4

Всем доброго времени суток! Немного пройдемся по rest api, больше тезисно чтобы обозначить основные направления для углубления.

REST [https://en.wikipedia.org/wiki/Representational_state_transfer]
напомню лишь, что это архитектурный стиль разработки ПО, который определяет некий набор ограничений для построения веб сервисов (а именно: client–server architecture, statelessness, cacheability, layered system) Все взаимодействие идет поверх HTTP, так что у нас в наличии уже готовые методы запросов и коды ответов

Для примера возьмем сущность Product и на примере распишем возможные эндпоинты в апи:

GET /products - получение списка продуктов
GET /products/{productId} - получение определенного продукта
POST /products - создание нового продукта
PUT /products/{productId} - обновление данных продукта
DELETE /products/{productId} - удаление продукта

Symfony 4 [https://symfony.com]
основные компоненты и их сфера использования (сразу оговорюсь, что в этой области идет непримиримая война, так как есть и другие подходы, которые тоже хороши, но надо уметь готовить DDD + Domain Events, CQRS)

Мы же возьмем стандартную SOA философию симфони:

Entity – сущность, в данном случае анемичная модель (просто представляет данные в базе, всю грязную работу на себя забирает доктрина) Ничего не знает об окружающем мире

Repository – репозиторий, работает только с подконтрольной сущностью, знает как получить, сохранить, где лежит и содержит различные хитрые выборки (если вся логика инкапсулирована, позволяет легко сменить базу данных, например перехать с nosql на релятивную бд)

Service – сервис, ключевой элемент симфони, в котором содержится либо логика работы с сущностью, либо высокоуровневый агрегат нескольких сервисов для решения сквозной бизнес задачи (знает о репозитории подконтрольной сущности и остальных сервисах представляющих свои сущности)

Controller – контроллер, проксирующее звено, получает запрос, отдает в подконтрольный сервис, получает результат и отдает итоговый ответ пользователю (ключевые слова: тонкий, глупый) проще покрыть проект тестами, переиспользовать логику в командах

Command – команда, аналог контроллера, толко для cli (подход такой же)

DTO – шаблон проектирования для передачи данных между слоями/подсистемами/компонентами

теперь ближе к практике, рассмотрим процесс создания новой сущности

/**
 * @param CreateDto $requestDto
 * @return Response
 *
 * @Route("/products", methods={"POST"})
 * @Security(...)
 * @ParamConverter("requestDto", class="App\Dto\Controller\Product\Request\CreateDto")
 */
public function create(CreateDto $requestDto)
{
    $createResultDto = $this->productService->create(
        $requestDto->getName(),
        $requestDto->getAmount(),
        $requestDto->getCategory()
    );

    $response = new CreateDtoResponse();
    $response->setProduct($createResultDto->getProduct());
    $response->setErrorList($createResultDto->getErrorList());

    return $this->buildResponse($response);
}

подробнее рассмотрим request dto:

/**
 * Class CreateDto
 * @package App\Dto\Controller\Product\Request
 */
class CreateDto
{
    /**
     * @var string
     *
     * @Assert\Type("string")
     * @Assert\NotBlank
     */
    protected $name;

    /**
     * @var float
     *
     * @Assert\Type("float")
     * @Assert\NotBlank
     */
    protected $amount;

    /**
     * @var int
     *
     * @Assert\Type("integer")
     * @Assert\NotBlank
     */
    protected $categoryId;

    /**
     * @var Category
     *
     * @Entity("App\Entity\Category")
     */
    protected $category;

    // + getters/setters
}

как видим основная работа делается ParamConverter’ом, он собирает объект без посторонней помощи до момента передачи в контроллер
можно навешивать свои аннотации (чтобы он находу собирал сущности из базы, на примере Category и если сущности нет, запрос упадет с соответствующей ошибкой валидации)
без проблем можно расширить базу assert’ов добавив проверки на уникальность сложных условий и тд и тп

в итоге собранный DTO попадает в контроллер, который отдает данные из него на откуп сервису (причем dto это своего рода контракт, пусть поначалу кажется оверхедом, но при дальнейшем использовании все плюсы станут очевидны)

сервис создает товар, уведомляет ожидающих его и тд и тп тут уже на вкус и цвет, что требует бизнес (не забываем, если промежуточные задачи долгие/нестабильные, отделяем их от основного текущего процесса очередьми и уносим обработку в фон, привет RabbitMQ/Kaffka and etc..)
в конечном итоге сервис отдает тоже DTO, в котором либо содержится результат(в данном случае созданный продукт, если важно отдать его обратно), либо список ошибок, с которыми он столкнулся

дальше это преобразуется в ответ юзеру

Проба пера :)

Всем доброго дня! Это первая запись в блоге.

Основная цель проекта – комплексно осветить дальние и темные уголки fullstack разработки. Основная связка: Symfony + Angular, но возможно будут посты на другие темы: от никсов, до функционального программирования на Scala и тд.

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

А пока надо обжить это место и навести теплую ламповую обстановку..