Skip to content

Architecture Overview

JournalPHP is built on Laravel 13 with a layered architecture and clear separation of concerns. Understanding the structure helps developers navigate the codebase quickly.

Stack

LayerTechnologies
BackendPHP 8.4+, Laravel 13
FrontendVue 3, Inertia.js 2, Vite, Tailwind CSS 3
DatabaseMySQL 8
Cache / Sessions / QueuesRedis
WebSocketLaravel Reverb
Queue MonitoringLaravel Horizon
SearchLaravel Scout
MediaLocal, Cloudinary, or Yandex Cloud Object Storage

Request Lifecycle

HTTP Request
  → Middleware (auth, admin.access, CheckBanned, …)
    → Controller (validation via Form Request)
      → Action (single business operation)
        → Service / Repository
          → Inertia Response (Vue component + data)

Inertia.js bridges Laravel's server-side routing and Vue's client navigation — pages switch without full reloads.

Application Layers

DirectoryPurpose
app/Http/Controllers/Thin controllers: accept request, delegate
app/Http/Requests/Form Request — input validation
app/Actions/One business operation per class
app/Services/Complex domain logic, integrations
app/Repositories/Data access through interfaces
app/DTO/Typed inter-layer data transfer objects
app/Contracts/Shared capability interfaces
app/Concerns/Traits mixed into models
app/Enums/PHP enumerations (typed constants)
app/Models/Eloquent models
app/Jobs/Queued background tasks
app/Events/Domain events
app/Listeners/Event handlers
app/Observers/Model lifecycle hooks
app/Notifications/Laravel notifications
app/Policies/Authorization policies
app/Console/Commands/Artisan commands
app/Providers/Service providers

Actions

Actions (app/Actions/) — single-responsibility classes:

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

Actions are called from controllers and other services. They are easy to test in isolation.

Services

Services (app/Services/) implement multi-step logic:

DirectoryPurpose
Feed/Pipeline-based feed assembly (Feed.php + MyFeed, NewFeed, PopularFeed, EditorialFeed, OrderFeed filters)
Payments/Payment gateway abstraction
MetaBuilder/SEO, OpenGraph, JSON-LD generation
Ai/DeepSeek integration (summaries, moderation); keys in admin (/admin-panel/ai-settings), DEEPSEEK_* in .env as fallback
ImageModeration/Yandex Vision API
Uploader/Storage abstraction (Local, Cloudinary, Yandex Cloud Object Storage); driver selected in admin panel
Subscription/Subscription lifecycle
Comment/Comment domain logic
Post/Post block and quiz processing

Repositories

Repositories (app/Repositories/) isolate data access:

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

Interface → implementation bindings are registered in RepositoryServiceProvider.

Config repositories are the single source of truth for config table settings. Values are cached under config.{key}.

Data Transfer Objects (DTOs)

DTOs (app/DTO/) are typed objects for passing data between layers instead of untyped arrays:

php
// Instead of:
$data = ['title' => '...', 'blocks' => [...], 'category_id' => 1];

// Use:
$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,
);

Contracts & Traits

Interfaces (app/Contracts/)

InterfaceCapability
BookmarkableInterfaceBookmarking
ReactionableInterfaceReactions
ReportableInterfaceReporting
BannableInterfaceBanning
IgnorableInterfaceIgnore lists
FollowableInterfaceFollowing
HasMediaMedia attachments
NotifiableInterfaceNotifications
UploaderFile storage

Traits (app/Concerns/)

Implement interfaces and are mixed into models:

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

Feed Pipeline

The feed system (app/Services/Feed/Feed.php) uses Laravel Pipeline:

Feed
  → Pipeline
    → MyFeed (personal)
    → NewFeed (new)
    → PopularFeed (popular)
    → EditorialFeed (editorial)
    → OrderFeed (ordering)
    → WithoutBlackList (ignored users, keywords)
  → cursorPaginate → post collection

Swappable Drivers (Driver Pattern)

Logic from AppServiceProvider has been split into dedicated service providers: MailServiceProvider, MediaServiceProvider, AiServiceProvider, SlowQueryServiceProvider, ImageModerationServiceProvider. Each provider resolves its own interface at runtime:

php
// AI service — DeepSeek or null driver
$this->app->bind(AiServiceInterface::class, fn() => ...);

// Image moderation — Yandex Vision or null
$this->app->bind(ImageModerationServiceInterface::class, fn() => ...);

// File storage — Local / Cloudinary / Yandex Cloud
// Driver and credentials are read from DB settings (Admin Panel → Media)
// config/uploaders.php — driver registry (maps driver name to implementation class)
$uploader = UploaderFactory::make($driver); // $driver resolved via config/uploaders.php

Adding a new provider doesn't require changes at call sites. For storage, create the class and add an entry to config/uploaders.php.

Payment Providers

PaymentProviderManager and PaymentProviderFactory in app/Services/Payments/ implement the Driver Pattern for payment providers. Supported drivers from PaymentProviderDriver: YooKassa and T-Bank. Adding a new provider = implement PaymentProviderInterface + register in the factory.

Routes

FilePurpose
routes/web.phpPublic and authenticated routes
routes/admin.phpAdmin panel routes (prefix: admin-panel)
routes/channels.phpWebSocket broadcasting channels
routes/console.phpArtisan Scheduler

Laravel Sanctum is installed, but there is no dedicated routes/api.php file — external API routes are not separated yet.

Key Models

ModelDomain
UserAccounts, roles, settings; communities are User with type = community (UserType::Community)
PostPosts with JSON blocks, versions, polls; category_id → community (User)
CommentComments with threading, soft delete
TagTags (polymorphic via tagables)
SubscriptionPriceSubscription plans
SubscriptionFeatureSubscription feature flags
UserSubscriptionActive subscriptions
SubscriptionPaymentPayment records
Ban, ContentBanUser blocks
Complaint, ComplaintReasonReports
Chat, MessagePrivate messaging
NotificationUser notifications
ConfigDB-stored settings
MediaUploaded files
AdvertisementAd placements

Database Structure

87+ migrations. Key characteristics:

  • Soft delete on users, comments, posts
  • Denormalized counters (post_counts, post_complaint_counts) for performance
  • Polymorphic relations for tags, reactions, bookmarks, reports

Infrastructure

docker-compose.yml          — development
docker-compose.prod.yml     — production
Dockerfile                  — development image (PHP 8.4 + Node)
Dockerfile.prod             — production image
docker/nginx/               — nginx configs
supervisord.conf            — Horizon + Reverb

Released under the MIT License.