Skip to content

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

JournalPHP построен на Laravel 13 с многоуровневой архитектурой и чётким разделением ответственностей. Понимание структуры поможет быстро ориентироваться в коде.

Стек

СлойТехнологии
BackendPHP 8.4+, Laravel 13
FrontendVue 3, Inertia.js 2, Vite, Tailwind CSS 3
БДMySQL 8
Кэш / Сессии / ОчередиRedis
WebSocketLaravel Reverb
Мониторинг очередейLaravel Horizon
ПоискLaravel Scout
МедиаLocal, Cloudinary или Yandex Cloud Object Storage

Жизненный цикл запроса

HTTP-запрос
  → Middleware (auth, admin.access, CheckBanned, …)
    → Controller (валидация через Form Request)
      → Action (одна бизнес-операция)
        → Service / Repository
          → Inertia-ответ (Vue-компонент + данные)

Inertia.js устраняет разрыв между серверным роутингом Laravel и клиентской навигацией Vue — страницы переключаются без полной перезагрузки.

Слои приложения

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

Actions

Actions (app/Actions/) — классы с единственной ответственностью. Каждый Action инкапсулирует одну операцию:

app/Actions/
├── Post/
│   ├── StorePostAction.php
│   ├── UnpublishPostAction.php
│   └── DeletePostAction.php
├── Comment/
│   ├── StoreCommentAction.php
│   └── DeleteBranchCommentAction.php
├── Subscription/
│   ├── FinalizeSubscriptionPaymentAction.php
│   └── CancelActiveSubscriptionAction.php
└── …

Actions вызываются из контроллеров и других сервисов. Их легко тестировать изолированно.

Сервисы

Сервисы (app/Services/) реализуют многоэтапную логику:

ДиректорияНазначение
Feed/Pipeline-сборка лент (Feed.php + фильтры MyFeed, NewFeed, PopularFeed, EditorialFeed, OrderFeed)
Payments/Абстракция платёжных провайдеров
MetaBuilder/Генерация SEO, OpenGraph, JSON-LD
Ai/Интеграция с DeepSeek (резюме, модерация); ключи в админке (/admin-panel/ai-settings), DEEPSEEK_* в .env — fallback
ImageModeration/Yandex Vision API
Uploader/Абстракция хранилища (Local, Cloudinary, Yandex Cloud Object Storage); драйвер выбирается в админке
Subscription/Жизненный цикл подписок
Comment/Доменная логика комментариев
Post/Обработка блоков и опросов

Репозитории

Репозитории (app/Repositories/) изолируют доступ к данным:

app/Repositories/
├── Post/
│   ├── Interfaces/PostRepositoryInterface.php
│   └── PostRepository.php
├── Config/
│   ├── Interfaces/GetMailConfigRepositoryInterface.php
│   └── GetMailConfigRepository.php
└── …

Привязки интерфейс → реализация регистрируются в RepositoryServiceProvider.

Config-репозитории — единственный источник настроек из таблицы config. Значения кэшируются под ключом config.{key}.

Data Transfer Objects (DTO)

DTO (app/DTO/) — типизированные объекты для передачи данных между слоями вместо нетипизированных массивов:

php
// Вместо:
$data = ['title' => '...', 'blocks' => [...], 'category_id' => 1];

// Используется:
$data = new CreatePostDto(
    title: '...',
    intro: null,
    category_id: 1,
    reply_id: null,
    blocks: '...',
    is_publish: true,
    is_official: false,
    is_adult: false,
    commenting_permissions: PostCommentingPermission::All,
);

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

Интерфейсы (app/Contracts/)

ИнтерфейсВозможность
BookmarkableInterfaceЗакладки
ReactionableInterfaceРеакции
ReportableInterfaceЖалобы
BannableInterfaceБаны
IgnorableInterfaceИгнор-списки
FollowableInterfaceПодписки
HasMediaМедиавложения
NotifiableInterfaceУведомления
UploaderХранилище файлов

Трейты (app/Concerns/)

Реализуют интерфейсы и подключаются к моделям:

Bannable, Followable, HasBookmarks, HasFollowers, HasIgnores, Ignorable, Reportable, Bookmarkable, Reactionable

Pipeline лент

Система лент (app/Services/Feed/Feed.php) использует Laravel Pipeline:

Feed
  → Pipeline
    → MyFeed (персональная)
    → NewFeed (новые)
    → PopularFeed (популярная)
    → EditorialFeed (редакторская)
    → OrderFeed (сортировка)
    → WithoutBlackList (игнорируемые пользователи, ключевые слова)
  → cursorPaginate → коллекция постов

Сменные драйверы (Driver Pattern)

Логика из AppServiceProvider вынесена в выделенные сервис-провайдеры: MailServiceProvider, MediaServiceProvider, AiServiceProvider, SlowQueryServiceProvider, ImageModerationServiceProvider. Каждый провайдер разрешает свой интерфейс в рантайме:

php
// AI-сервис — DeepSeek или null-драйвер
$this->app->bind(AiServiceInterface::class, fn() => ...);

// Модерация изображений — Yandex Vision или null
$this->app->bind(ImageModerationServiceInterface::class, fn() => ...);

// Хранилище файлов — Local / Cloudinary / Yandex Cloud
// Драйвер и учётные данные берутся из настроек БД (Админ-панель → Медиа)
// config/uploaders.php — реестр драйверов (класс-реализация для каждого имени)
$uploader = UploaderFactory::make($driver); // $driver читается из config/uploaders.php

Добавление нового провайдера не требует изменений в вызывающем коде. Для хранилища достаточно создать класс и добавить запись в config/uploaders.php.

Платёжные провайдеры

PaymentProviderManager и PaymentProviderFactory в app/Services/Payments/ реализуют Driver Pattern для платёжных провайдеров. Поддерживаются драйверы из PaymentProviderDriver: YooKassa и T-Bank. Добавление нового провайдера — реализация PaymentProviderInterface + регистрация в фабрике.

Маршруты

ФайлНазначение
routes/web.phpПубличные и аутентифицированные маршруты
routes/admin.phpМаршруты Админ-панели (prefix: admin-panel)
routes/channels.phpWebSocket-каналы Broadcasting
routes/console.phpArtisan Scheduler

Laravel Sanctum установлен, но отдельного файла routes/api.php нет — внешний API пока не выделен.

Ключевые модели

МодельДомен
UserАккаунты, роли, настройки; сообщества — User с type = community (UserType::Community)
PostПосты с JSON-блоками, версии, опросы; category_id → сообщество (User)
CommentКомментарии с ветками, soft delete
TagТеги (полиморфная связь через tagables)
SubscriptionPriceТарифные планы
SubscriptionFeatureFeature-флаги подписки
UserSubscriptionАктивные подписки
SubscriptionPaymentЗаписи платежей
Ban, ContentBanБлокировки
Complaint, ComplaintReasonЖалобы
Chat, MessageЛичные сообщения
NotificationУведомления
ConfigНастройки из БД
MediaЗагруженные файлы
AdvertisementРекламные блоки

Структура БД

87+ миграций. Ключевые особенности:

  • Soft delete на users, comments, posts
  • ULID → BigInt конверсия первичных ключей (в истории миграций)
  • Денормализованные счётчики (post_counts, post_complaint_counts) для производительности
  • Полиморфные связи для тегов, реакций, закладок, жалоб

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

docker-compose.yml          — разработка
docker-compose.prod.yml     — продакшен
Dockerfile                  — образ для разработки (PHP 8.4 + Node)
Dockerfile.prod             — образ для продакшена
docker/nginx/               — конфиги nginx
supervisord.conf            — Horizon + Reverb

Released under the MIT License.