[VESP] Заготовка для SPA сервисов с REST API

Привет, друзья!

Наверное, вы уже обратили внимание, что я не был особо активен в этом году в сообществе. Причина в том, что я набрал кучу работы, с которой смог разобраться вот только что. И работа эта с MODX была никак не связана — я писал собственное решение для сложных проектов, с нуля.



И вот сейчас я готов с вами им поделиться. Но для начала пара важных предостережений:

Во-первых, это не готовое решение, не CMS. Это заготовка для старта нового проекта, в которой уже сделана основная структура.

Во-вторых, она не следует всем модерновым техникам, например используется устаревший паттерн Service Locator. А запросы обрабатывают процессоры через единую точку входа, а не контроллеры с прописанными маршрутами.

Это всё потому, что я из мира MODX, и лично мне так привычно и удобно. Соответственно, когда я придумывал систему «для себя», я сделал так, чтобы было удобно именно мне. Так что, когда вас посетит ощущение «где-то я уже это видел» — так и есть, вы видели это в MODX.

Ну а теперь, если вы готовы, знакомьтесь с VESP:
Vue.js ( + Nuxt.js)
Eloquent ORM
Slim3 micro-framework
Phinx migrations

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

Быстрый старт


1. Клонируем репозиторий VESP на хостинг
2. Переходим в core и переименовываем .env.dist в .env
3. Меняем настройки в .env — нужно указать адрес для запросов в API, подключение к БД и всё такое
4. Запускаем composer install
5. Запускаем ./vendor/bin/phinx migrate — это создаст стандартные таблицы для юзеров
6. Запускаем ./vendor/bin/phinx seed:run — это создаст стандартные записи в таблицах

Серверная часть готова, дальше фронтенд. Его можно разрабатывать на хостинге, или локально. Лично я предпочитаю локально, поэтому:
1. Клонируем репозиторий себе на комп
2. Скачиваем .env с хостинга, или меняем локально — нам нужна настройка SITE_URL
3. Идём в директорию frontend и делаем там npm install && npm run dev

Если всё сделали верно, то запустится локальный сервер разработки по адресу localhost:5112. Открываем его в браузере и проверяем, чтобы запросы уходили на хостинг — там их должно ловить ядро и отвечать. По умолчанию в .env включен параметр CORS, поэтому всё работает с удалёнными запросами.

Как работает ядро


Все запросы должны прилетать на www/index.php, там прописан единственный маршрут для API.
$app->any('/api[/{name:.+}]', function ($request, $response, $args) use ($container) {
  $container->request = $request;
  $container->response = $response;

  return (new Api($container))->process($request, $response, $args);
});

Дальше срабатывает контроллер core/src/Controllers/Api.php. Его задача — найти запрошенный процессор, загрузить юзера (если он авторизован) и запустить процессор.

Все процессоры находятся в core/src/Processors. В корне директории лежат абстрактные классы для наследования, а во вложенных директориях уже рабочие.

Например, пришёл запрос на api/web/test — значит процессор должен лежать в core/src/Processors/Web/Test.php.

Каждый процессор может отвечать на какой-нибудь тип запроса, то есть иметь методы post, put, patch, delete. Каким методом запрос пришёл — такой метод внутри процессора и дёргается. Если процессор должен только что-то отдавать, и метода put там просто нет, то на запрос put будет ошибка.

Проверка прав реализована свойством scope. Если там что-то есть, то этот scope будет проверяться у группы авторизованного пользователя. Причём, у группы может быть как полный scope на действие, например users, так и только на определённые методы — users/get.

Для работы используются модели Eloquent, пишутся они вручную. Таблицы создаются через миграции — смотрите как сделаны таблицы и модели user и user_roles, это понятный пример. Ну и читайте документацию Eloquent, она очень подробная.

Ну и основной класс со всеми сервисами — Container. Он прокидывается в контроллеры и процессоры, позволяя использовать свои сервисы или свойства, например обращаться к $this->container->user. Да-да, совсем как $this->modx->user.

Как работает фронтенд


Это я уже рассказывал в заметке про Nuxt.js.

Но в VESP сразу из коробки подключены нужные модули и настроена авторизация в админку. Так же есть и страницы для работы с пользователями и группами, включая выдачу им нужных scope. Для оформления используется замечательный BootstrapVue с кучей готовых компонентов.

Обратите внимание и на мои компоненты (они лежат в директории components) для простой реализации грида с модальными окнами создания, редактирования и удаления. Всё как в нежно любимом нами ExtJS менеджере MODX.



Для авторизации используется @nuxtjs/auth, работает она через токены. Причём, токены пишутся в БД на сервере, чтобы можно было ограничить количество одновременных подключений и отозвать их у отключенного пользователя. Это очень частый вопрос при работе с токенами, так что я включил его в базовый функционал — смотрите метод Container::makeToken().

После окончания локальной разработки собираем проект через npm run build и выгружаем на сервер. Я всё храню прямо в директории frontend в корне, а запросы разруливает Nginx.

if ($request_uri ~ ".*/$") {
    rewrite              ^/(.*)/$    /$1 permanent;
}

location ~ ^/api/(.*)$ {
    rewrite ^/api/(.*)$ /index.php?q=$1;
}

location / {
    root    /home/vesp/frontend/dist/;
    try_files    $uri /200.html;
}

location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|bmp|ttf|woff|woff2|svg)$ {
    root                /home/vesp/frontend/dist/;
    access_log          off;
    log_not_found       off;
    expires             10d;
    break;
}
Собственно, и собираю я фронтенд после разработки тоже на сервере, так быстрее.

Заключение


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

Понятное дело, что это не для сайтов-визиток и магазинов-однодневок. Это для больших сервисов, типа krafti.ru — где всё сделано с нуля под заказчика, работает очень быстро и выдерживает хорошие нагрузки. Например о том, что на сайте была распродажа и за день прошло более 2000 покупок, я узнал только на следующий день от радостных клиентов — а не как обычно, когда сервер начал тупить.

Это потому, что сервер больше не занимается рендерингом, а обрабатывает только API запросы. Всё оформление рисует браузер посетителя, избавляя сервис от лишней нагрузки.

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

Я понимаю, что заметка довольна сумбурная, но как-то более подробно расписать результат почти года работы у меня нет сил. Это-то выложить собирался почти месяц.
Поэтому пишите вопросы по узким местам, буду отвечать и дорабатывать текст по мере необходимости.

Демо-сайт — vesp.bezumkin.ru, логин и пароль modx
Исходный код — github.com/bezumkin/VESP

P.S.
Промо-код для покупки курса на Krafti.ru на 10% — MODX10
Василий Наумкин
05 декабря 2019, 15:29
modx.pro
21
1 933
+36
Поблагодарить автора Отправить деньги

Комментарии: 48

Avocado
05 декабря 2019, 15:49
+1
Мало, что понял… но очень интересно! )
Я как «Начинающий» Захотел «Пощупать».
Но думаю, на подобные вещи рано заглядываться…
    Алексей Соин
    05 декабря 2019, 15:51
    +1
    Вот это неожиданно!!! Называется к концу рабочего дня решил на modx.pro зайти, а тут Василий жгёт?
      Алексей Соин
      05 декабря 2019, 16:00
      +1
      Это для больших сервисов, типа krafti.ru — где всё сделано с нуля под заказчика, работает очень быстро и выдерживает хорошие нагрузки.
      Нативная интеграция в статью))))
        Василий Наумкин
        05 декабря 2019, 16:03
        +1
        Могу и промокод подогнать со скидкой 10%, если нужно =)
        Руслан Сафин
        05 декабря 2019, 17:06
        0
        Один вопрос, почему не ларавель или люмен?
          Василий Наумкин
          05 декабря 2019, 17:18
          0
          Они навязывают свой подход, который мне не нравится.

          А вот то, что нравится — Eloquent, я с большим удовольствием использую.
            Pavel Zarubin
            05 декабря 2019, 18:22
            0
            На удивление, Eloquent — это почти единственное, что в ларе постоянно критикуют и советуют всем использовать доктрину
              Сергей Шлоков
              05 декабря 2019, 19:29
              +2
              Ровно по одной причине — несоответствие принципам SOLID. Eloquent реализует ActiveRecord шаблон, а Doctrine — DataMapper. Последний в отличие от первого реализует принципы SOLID как и Ларавел.

              Но лично меня это несоответствие ни разу не напрягает. Eloquent очень удобен. Как и Лара )
          Александр Мельник
          05 декабря 2019, 18:45
          0
          интересно. Тем более что сам пытаюсь построить что-то похожее.
          Выбрал связку
          — slim4
          — twig
          — orm doctrine
          — jquery (поскольку очень слаб в нативном js и vuejs)
            Николай Савин
            05 декабря 2019, 19:25
            0
            Это потому, что сервер больше не занимается рендерингом, а обрабатывает только API запросы. Всё оформление рисует браузер посетителя, избавляя сервис от лишней нагрузки.
            А разве в Nuxt.js не рендерятся страницы на стороне сервера, чтобы получить разные URL на выходе?
            UPD. Понял… речь идет об отрисовке. Хотя обычный HTML + JS тоже рендерится браузером не?
              Василий Наумкин
              05 декабря 2019, 20:56
              0
              Даже при включенном серверном рендере ты грузишь только одну страницу, а дальше всё работает у тебя в браузере — все адреса, переходы, всё. Пока снова не обновишь страницу вручную.

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

              HTML никакой не грузится, только картинки и данные JSON.
              Александр Мельник
              05 декабря 2019, 20:54
              0
              Последнее время много попадается информации о том что это мол очень быстро, сервер обрабатывает только апи запросы, возвращает json, а браузер пользователя все отрисовывает.
              А кто-то пробовали это на чем-то высоконагруженном?
              krafti.ru/ мне кажется не показателен, это ведь практически лендинг.
                Василий Наумкин
                05 декабря 2019, 20:58
                +3
                Это, как бы, онлайн магазин по продаже курсов и просмотру с полным функционалом — типа отправки домашних работ, комментариев и т.д. Но увидеть это можно только после покупки курса.



                Запустились 11 сентября, с тех пор уже несколько тысяч заказов и ни одной проблемы. Не знаю, как для тебя, а мне этого вполне достаточно для выводов.
                  Алексей Соин
                  05 декабря 2019, 22:47
                  0
                  а если весь рендеринг теперь лежит на браузере пользователя интересен такой момент, на сколько при таком взаимодействии съедаются ресурсы системы, и насколько должна быть слабая машина, чтобы всё подвисло))
                    Руслан Сафин
                    05 декабря 2019, 22:57
                    0
                    Я бы не стал сильно об этом переживать… Если разработчик более менее с головой, то будет шустрее чем с jquery))) Вебпак об этом позаботится
                      Алексей Соин
                      05 декабря 2019, 23:03
                      +1
                      Не совсем понял, причем тут вебпак и отрисовка с обработкой данных на стороне клиента?
                  Руслан Сафин
                  05 декабря 2019, 22:43
                  0
                  Будете сильно удивлены, но www.ozon.ru/ работает на nuxtJs
                    Алексей Соин
                    05 декабря 2019, 22:50
                    +3
                    с дополнением wappalyzer уже сложно чему то удивляться)))

                      Николай
                      05 декабря 2019, 23:21
                      0
                      Интересно как они решали вопрос с seo
                        Андрей Степаненко
                        06 декабря 2019, 00:42
                        +1
                        там довольно таки просто, все сео обрабатывается на стороне сервера.

                        Надо уже привыкнуть что js работает как обычный php за счет nodejs. А потом тот же js работает как js в привычном понимании уже после загрузки страницы.
                          Николай
                          06 декабря 2019, 01:06
                          0
                          Я просто думал, что контент страницы без обработки js в json приходит отдельно, а уже потом скрипты js парсят json и наполняют html-скелет данными. Это было бы для роботов не очень здорово. А сейчас получше посмотрел, и вижу html-теги с контентом в исходном коде, всё как положено. Но как-то странно, один и тот же текст и в тегах и в json. Короче, пока непонятно как это всё работает)
                            Aborrol
                            06 декабря 2019, 02:09
                            +1
                            Прочитайте про то как работает nuxt.js и как это решает эту проблему, Наумкин как раз недавно выкладывал статью про это modx.pro/development/18695
                          Stanislavsky
                          06 декабря 2019, 03:12
                          +3
                          Для этого есть серверный рендеринг
                        srs
                        srs
                        06 декабря 2019, 09:40
                        +2
                        Магазинус Лебедева тоже на nuxt
                      Sergey Leleko
                      06 декабря 2019, 08:36
                      +4
                      Это прекрасное решение! Я бы сказал дает прям «глоток свежего воздуха» в серые, обыденные будни!
                      Спасибо!
                        Степан Прищепенко
                        06 декабря 2019, 09:25
                        0
                        Я пока не смотрел исходники, но сразу появился вопрос: для чего использовать SLIM который на php если используется nuxt который на nodejs. Почему вместо слима не используется тот же node?
                          Stanislavsky
                          06 декабря 2019, 10:11
                          +3
                          nuxt собирает приложение, используя node js. Бэкенд может быть любым.Главное иметь API к которому будет обращаться приложение на js.
                            Степан Прищепенко
                            06 декабря 2019, 16:13
                            0
                            Гениально, правда это я и сам знаю, я спросил «для чего использовать SLIM...», уточню, чтоб понятнее был вопрос: на хрен использовать PHP если на сервере крутится NODEJS??? Ради Phinx migrations, наработкам, привычке или что еще?
                              Василий Наумкин
                              06 декабря 2019, 17:27
                              +1
                              Не всегда крутится у меня на сервере NodeJS, я стараюсь в статику сгенерировать страницы и дальше отдавать их через Nginx.

                              А даже если и крутится, писать бэкенд на PHP мне гораздо приятнее и привычнее. Eloquent, опять же, не на JS написан.
                        Степан Прищепенко
                        06 декабря 2019, 09:31
                        +1
                        Чет у меня картинки с крафти ползут по 13 и более секунд. Да и вообще размер их больше чем требуется.
                          Артем
                          06 декабря 2019, 16:10
                          0
                          Спасибо за такое крутое решение! Очень интересно было почитать.
                          Василий, есть ли планы вообще уйти от php в сторону node.js?
                          Интересно узнать, что ты думаешь по этому поводу.
                            Василий Наумкин
                            06 декабря 2019, 17:26
                            0
                            Нет таких планов, PHP мне нравится больше.
                              Александр Мельник
                              06 декабря 2019, 18:01
                              +1
                              А на днях посмотрел новинки версии php7.4 так и вообще пришел к выводу, что php и javascript становятся даже по синтаксису близки. В 7,4 появились стрелочные функции, оператор… для объединения массивов.
                            Alexander V
                            06 декабря 2019, 19:52
                            0
                            Интересно. Я подобное делал на Laravel. Вот думаю, не слишком ли это большой оверхед по сравнению с вашим вариантом?
                              Василий Наумкин
                              06 декабря 2019, 20:27
                              0
                              Думаю, большой.

                              Я даже в Slim3 использую только самые базовые классы Response и Request, даже без middlewares.
                                Alexander V
                                06 декабря 2019, 20:35
                                +1
                                С другой стороны, любой хостинг тянет без особых проблем Laravel. Bроде всё есть. Пользователи, middleware, eloquent. Для меня, как любителя пойдет.
                              Сергей
                              06 декабря 2019, 20:08
                              +2
                              Надо писать обучающий курс. Хотя бы как пример как Sendex был курс. Чтобы народ начал въезжать.
                                Олег Сергеевич
                                09 декабря 2019, 09:22
                                0
                                Насколько сложно выпилить Vue и поставить React?
                                  Василий Наумкин
                                  09 декабря 2019, 09:41
                                  +1
                                  Ты серьёзно думаешь, что я выпиливал Vue и ставил React?

                                  Без понятия. Попробуй и расскажи нам.
                                    Владимир Дремучий
                                    09 декабря 2019, 10:33
                                    +1
                                    Да я думаю можно просто очистить папочку frontend и подсмотреть в конфиг чтобы настроить тот же Next/Gatsby
                                      Олег Сергеевич
                                      09 декабря 2019, 10:52
                                      0
                                      Да, спасибо, я глянул исходники, в принципе ничего сложного.
                                      Правда, пока смотрел — понял, что мне не пригодится такая заготовка, даже с Реактом на борту.
                                    Вася
                                    09 декабря 2019, 12:05
                                    0
                                    Все равно не понятно, как эти VUE, NUXT дружат с сео
                                    Василий, ты просто не заморачивался с description под поисковики, или это особенности такого подхода?
                                      srs
                                      srs
                                      09 декабря 2019, 13:18
                                      +2
                                      Есть server side rendering и насколько я понимаю, поисковику без разницы кто отдает отрисованную страницу. По этому с сео проблем точно не должно быть.
                                        Вася
                                        09 декабря 2019, 14:45
                                        0
                                        Спасибо,
                                        Почитаю
                                          Pavel Zarubin
                                          15 декабря 2019, 03:59
                                          0
                                          Вместо сервер сайд рендеринга в больших проектах делают версию для поисковиков. Весь смысл применения реактивных фреймворков ломается о сервер сайд рендеринг
                                            srs
                                            srs
                                            16 декабря 2019, 11:15
                                            +2
                                            Каким образом она ломается?
                                          Иван Бондаренко
                                          09 января 2020, 16:12
                                          +1
                                          Василий, привет! Спасибо за заготовку. Развернул у себя. Буду экспериментировать.
                                            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                                            48