Skip to content

Архитектура проекта ​

JournalPHP — монолит на Laravel с фронтендом на Vue 3, связанным с сервером через Inertia.js.

Обзор ​

Сервер отдаёт страницы через Inertia: контроллер возвращает имя Vue-страницы и её данные, а переходы между страницами происходят без полной перезагрузки. Бизнес-логика вынесена из контроллеров в Actions, доступ к данным — в репозитории с интерфейсами, интеграции — в сервисы со сменными драйверами. Настройки платформы хранятся в базе данных, поэтому драйверы и ключи интеграций выбираются в админ-панели без правки .env.

Фоновая работа выполняется в том же приложении: очереди обрабатывает Laravel Horizon, WebSocket-сообщения отправляет Laravel Reverb, расписание задач запускает планировщик Laravel. Версии компонентов перечислены в разделе Системные требования.

Стек ​

СлойТехнологии
BackendPHP, Laravel
FrontendVue 3, Inertia.js, Vite, Tailwind CSS, Element Plus
База данныхMySQL
Кэш, сессии, очередиRedis, Laravel Horizon
WebSocketLaravel Reverb, Laravel Echo
ПоискLaravel Scout: база данных, Algolia, Meilisearch

Путь запроса ​

HTTP-запрос
  → Middleware (auth, admin.access, content-ban, has.subscription.feature, …)
    → Controller + Form Request (валидация)
      → Action (одна бизнес-операция)
        → Service / Repository
          → Inertia-ответ (Vue-страница и её данные) или JSON

Маршруты сайта описаны в routes/web.php, маршруты админ-панели с префиксом admin-panel — в routes/admin.php, который подключается из web.php. Каналы WebSocket описаны в routes/channels.php. Расписание задач находится в app/Console/Kernel.php.

Структура app/ ​

ДиректорияНазначение
Http/Controllers/Контроллеры: принимают запрос и делегируют работу
Http/Middleware/Middleware
Http/Requests/Form Request — валидация входных данных
Http/Resources/API Resources — формирование ответов
Actions/Одна бизнес-операция на класс
Services/Доменная логика и интеграции
Repositories/Доступ к данным через интерфейсы
DTO/Типизированные объекты передачи данных
Contracts/, Concerns/Интерфейсы общих возможностей моделей и трейты, которые их реализуют
Enums/PHP-перечисления
Models/Eloquent-модели
Events/, Listeners/Доменные события и их обработчики
Jobs/Фоновые задачи
Notifications/Уведомления Laravel
Observers/Хуки жизненного цикла моделей
Policies/Правила авторизации
Rules/Собственные правила валидации
Helpers/Глобальные функции проверки функций подписки
Console/Commands/Artisan-команды
Providers/Сервис-провайдеры

Фронтенд ​

resources/js/
├── Pages/        # Inertia-страницы сайта
├── components/   # Компоненты сайта
├── editor/       # Модули редактора постов
├── Shared/       # Общий layout и глобальные модальные окна
├── Admin/        # Страницы и компоненты админ-панели
├── app.js        # Точка входа сайта
└── admin.js      # Точка входа админ-панели

Основные стили — resources/css/app.css, CSS-переменные темы — resources/css/variables.css, стили админ-панели — resources/css/admin.css. Tailwind настраивается в tailwind.config.cjs. Цвета по умолчанию для Админ-панель → Настройки → Сайт → Внешний вид заданы в config/appearance.php.

Actions ​

Action — класс с единственным публичным методом execute(), который выполняет одну операцию. Actions сгруппированы по доменам: app/Actions/Post/StorePostAction.php, app/Actions/Comment/StoreCommentAction.php, app/Actions/Search/ImportSearchIndexesAction.php. Их вызывают контроллеры, слушатели, задачи и другие Actions.

Репозитории ​

Репозиторий состоит из интерфейса в подкаталоге Interfaces/ и реализации рядом с ним:

app/Repositories/Post/
├── Interfaces/
│   └── GetPostBySlugRepositoryInterface.php
└── GetPostBySlugRepository.php

RepositoryServiceProvider находит такие пары и автоматически регистрирует привязку «интерфейс → реализация», если реализация называется как интерфейс без суффикса Interface.

Репозитории в app/Repositories/Config/ читают и записывают настройки из таблицы config. При изменении записи модель Config сбрасывает связанные кэши.

DTO ​

DTO в app/DTO/ передают данные между слоями вместо нетипизированных массивов, например CreatePostDto при создании поста.

Контракты и трейты ​

Общие возможности моделей описаны интерфейсами в app/Contracts/ и реализованы трейтами из app/Concerns/:

ИнтерфейсТрейтВозможность
BookmarkableInterfaceBookmarkableДобавление в закладки
ReactionableInterfaceReactionableРеакции
ReportableInterfaceReportableЖалобы
IgnorableInterfaceIgnorableСкрытие пользователем
NotifiableInterfaceNotifiableПодписка на уведомления
FollowableInterfaceHasFollowersНаличие подписчиков
FollowerInterfaceFollowableПодписки на пользователей и темы
BannableInterfaceBannableБаны
HasNotificationsInterfaceHasNotificationsПодписки пользователя на уведомления
HasMedia—Медиавложения
—HasBookmarks, HasIgnoresЗакладки и скрытые объекты пользователя

Ленты ​

Лента (app/Services/Feed/Feed.php) собирается через Laravel Pipeline:

Feed
  → MyFeed           — посты из подписок
  → NewFeed          — свежие посты
  → PopularFeed      — фильтр по периоду
  → EditorialFeed    — редакционные посты
  → OrderFeed        — сортировка
  → WithoutBlackList — исключение скрытых пользователей, постов и ключевых слов
→ cursorPaginate(10)

MyFeed, NewFeed и PopularFeed срабатывают только для своего типа ленты, EditorialFeed — при фильтре editorial. OrderFeed выбирает сортировку по типу ленты, WithoutBlackList применяется ко всем лентам авторизованного пользователя.

Сервисы ​

ДиректорияНазначение
Feed/Сборка лент
Search/Поисковые драйверы: база данных, Algolia, Meilisearch
Payments/Платёжные провайдеры: ЮKassa, T-Bank
Subscription/Оплата и автопродление подписок
Ai/AI-провайдеры и бот в комментариях
Moderation/AI-модерация постов, комментариев и изображений
Uploader/Хранилища файлов: локальный диск, Cloudinary, Yandex Cloud
Gif/Поиск GIF: Tenor, Giphy
MetaBuilder/Мета-теги, OpenGraph и JSON-LD страниц
Seo/Чтение мета-данных внешних ссылок
Post/Блоки постов, опросы, краткое изложение, счётчики
Comment/Формирование ответов с комментариями
User/Бейдж и избранное достижение пользователя
Auth/OAuth и проверка токенов One Tap
Advertisement/Подбор и вывод рекламных блоков
SharedData/Общие данные для всех Inertia-страниц
Admin/Разделы админ-панели

Сменные драйверы ​

Интеграции, которые выбираются в админ-панели, устроены одинаково:

app/Services/Gif/
├── Contracts/GifServiceInterface.php   — контракт
├── AbstractGifService.php              — общий алгоритм драйверов
├── Drivers/                            — реализации
│   ├── TenorGifService.php
│   └── GiphyGifService.php
└── GifManager.php                      — выбор и создание драйвера
  • Контракт: описывает, что умеет любой драйвер
  • Абстрактный класс: содержит общую часть, драйвер реализует только отличия
  • Менеджер: наследует Illuminate\Support\Manager, getDefaultDriver() берёт драйвер из настроек, методы create{Name}Driver() создают драйверы
  • Enum: перечисляет драйверы и их названия для админ-панели
ИнтеграцияМенеджерEnumДрайверы
AIAiManagerAiProviderDeepSeek, OpenAI, Gemini, YandexGPT
ПоискSearchManagerSearchDriverБаза данных, Algolia, Meilisearch
Поиск GIFGifManagerGifDriverTenor, Giphy
Хранилище файловUploaderManagerUploaderDriverLocal, Cloudinary, Yandex Cloud
Модерация изображенийImageModerationManagerImageModerationDriverYandex Vision, без проверки
ПлатежиPaymentManagerPaymentProviderDriverЮKassa, T-Bank

DeepSeek, Gemini и YandexGPT используют общий драйвер OpenAiCompatibleService: они совместимы с OpenAI Chat Completions API. OpenAiService наследует его и отличается параметрами запроса.

Контракты AI, хранилища и модерации изображений регистрируются в сервис-провайдерах как scoped: экземпляр создаётся заново для каждого запроса и каждой задачи очереди, поэтому воркеры сразу используют драйвер, выбранный в админ-панели.

Поисковый драйвер реализует два интерфейса: SearchDriverInterface (провайдеры поиска постов и пользователей) и SearchIndexManagementInterface (проверка подключения и импорт индекса). ScoutConfigServiceProvider при загрузке приложения передаёт Scout драйвер и ключи из админ-панели.

Почта устроена иначе: MailServiceProvider подставляет настройки SMTP из админ-панели в конфигурацию mail при загрузке приложения, а транспорт выбирает встроенный MailManager Laravel. После сохранения настроек почты MailSettingsController выполняет queue:restart, и воркеры очередей перезапускаются с новыми настройками, см. Почта.

Поисковые индексы ​

Параметры индексов описаны в config/scout-indexes/:

ФайлСодержимое
algolia-posts.php, algolia-users.phpПоля поиска, ранжирование и фильтрация для Algolia
meilisearch-posts.php, meilisearch-users.phpПоля поиска, фильтры, сортировка, правила ранжирования и допуск опечаток для Meilisearch

В meilisearch-posts.php параметр rankingScoreThreshold (по умолчанию 0.5) отсекает слабые совпадения. После изменения файлов индекс синхронизируется заново — см. Поиск.

Модели ​

МодельНазначение
UserПользователи и темы: тема — это User с type = community
PostПосты: блоки в JSON, category_id — тема, reply_id — исходный пост для ответного
PostVersionHistoryВерсии постов
CommentКомментарии с ветками
Tag, TagableТеги и их связи
Reaction, ReactionItemРеакции и их набор
Achievement, UserAchievementДостижения и их получение пользователями
MonthlyRatingPlaceИтоговые места месячных топов
Chat, MessageЛичные сообщения
NotificationПодписки пользователей на уведомления (таблица notification_subscriptions)
Ban, ContentBan, ViolationBanRuleБаны, запреты на публикацию и правила автоматических запретов
Complaint, ComplaintReasonЖалобы и их причины
SubscriptionPrice, SubscriptionFeature, UserSubscription, SubscriptionPaymentПодписка
AdvertisementРекламные блоки
MediaЗагруженные файлы
ConfigНастройки платформы
Role, UserRole, UserAdminSectionРоли и доступ к разделам админ-панели

База данных ​

  • Пользователи удаляются мягко; окончательное удаление аккаунта откладывается через поле will_be_deleted_at
  • Счётчики постов хранятся в таблице post_counts и пересчитываются командой php artisan post-counts:refresh
  • Теги, реакции, закладки, жалобы и медиа привязываются через полиморфные связи

Инфраструктура ​

docker-compose.yml          — локальное окружение
docker-compose.prod.yml     — продакшен
Dockerfile                  — образ приложения для разработки
Dockerfile.prod             — многоэтапная сборка: ассеты → PHP runtime → nginx
docker/entrypoint-*.sh      — сценарии запуска контейнеров
docker/nginx/               — конфигурации nginx
supervisord.conf            — PHP-FPM, Horizon, планировщик, Reverb

Released under the MIT License.