MB
~ / projects / beauty-directories

Beauty Directories_

High-performance multi-site platform for managing independent directories of beauty clinics, cosmetologists, and cosmetic products. Enables administrators to spawn separate custom domains, configure dynamic EAV-hybrid catalog values, and filter data instantly via a hybrid SSR/SPA Vue architecture.

Laravel 12 PHP 8.4 MoonShine 4.x Vue 3 (Composition) TypeScript 5.8 PostgreSQL 16 Redis 7 & Horizon
280+ domain classes
32 controllers
25 Vue components
87 migrations
123 PHPUnit tests
24 Vue/TS files

// architecture

Decoupled monorepo using simplified Domain-Driven Design (DDD) to isolate domain contexts and business layers, facilitating database flexibility and codebase maintenance.

$ cat architecture.txt
Browser
Caddy / Nginx Reverse Proxy
(HTTP 301 www -> non-www)
Laravel 12 Multi-Site Engine
┌───────────────────┴───────────────────┐
Public Web Routing MoonShine 4.x Admin Panel
(Alpine.js & Blade Views) (Site & Language Management)
│ │
Vue 3 Catalog SPA
(Pinia Filter Store)
│ │
API endpoints
(/api/catalog/items)
└───────────────────┬───────────────────┘
Domain Services
(SiteResolver, SlugResolver, etc.)
Eloquent Models & DB Migrations
PostgreSQL 16
Redis 7 / Horizon
(Asynchronous Jobs)
Telegram Logger Bot
(Real-time Error Tracking)
DDD Separation

Clear partition into Domain (entities & rules), Application (services & logic), and Infrastructure (HTTP wrappers)

Multi-Site Middleware

SiteResolver intercepts request host, normalizes domain, loads settings, and caches the site context for 4 hours

Unified Slug Dispatcher

Polymorphic URL resolution maps endpoints dynamically to avoid hardcoded prefixes, resolving targets in O(1)

// dynamic catalog system

To prevent database bloating across clinics, doctors, and products, a hybrid EAV database architecture was designed to combine search indexability with structural versatility.

Normalized Filters

Options are stored in standard tables for rapid indexing and SQL join performance

JSONB Columns

Non-searchable textual and media metadata is saved inside JSONB structures to reduce table size

Encrypted Settings

Sensitive fields (like API keys) marked with is_secret are encrypted at the database level

// client-side reactive filters

A hybrid SSR/SPA rendering approach was chosen to ensure optimal search engine crawlability and an interactive, client-side SPA navigation experience.

// Hybrid catalog lifecycle
Step 1
Blade SSR First Paint
Pre-renders HTML, meta tags, Schema.org schemas, and first page grid for indexation
Step 2
Vue hydration
Vue component mounts on data-vue="catalog-view", loading initial store states from DOM data attributes
Step 3
SPA Takeover
Vue hides server pagination and manages reactive searches, filters, infinite scroll, and history pushing
Pinia Store Integration

Store handles search parameters, caches results matrices, and controls dynamic query structures

AbortController Abortions

Prevents network race conditions by automatically cancelling active AJAX calls when filters change rapidly

Cascading Geo-Filters

Updating regions immediately recalibrates city availability lists and recalculates directory match counts

Color-Adaptive Skeletons

Glow animations automatically adapt to the specific site's primary color settings loaded dynamically from backend

// seo engine & localization

Fallback translation routing

Uses Spatie Translatable with a custom HasSiteFallbackTranslations trait. If a translation is missing in the requested locale, it cascades from local language -> site default locale -> global locale -> first filled field.

Trailing Slash Enforcement

The routing layer uses localizedRoute() to consistently append trailing slashes. This prevents page duplication and canonical URL conflicts in search engines.

Locale Prefixing

Clean path routing for the default site language (e.g. /contacts), while automatically suffixing custom codes for additional locales (e.g. /uk/contacts).

Feeds & IndexNow

Dynamic generation of XML sitemaps, robots.txt, and RSS feeds per domain. Immediate indexation updates notified via IndexNowController endpoints.

// quality & devops

Test Coverage

$ php artisan test
Larastan Level 9 .... passed
Rector PHP 8.4 ..... passed
PHPUnit 11 tests ..... 123 tests, 0 failures

Envoy Zero-Downtime Deploy

$ envoy run deploy
Git auto-commit & push
SSH remote fetch & hard reset
Composer & NPM dependencies
Optimizations & Cache warming
Production compilation

Telemetry & Alerts

  • Full exception monitoring via Sentry
  • Custom Telegram logger bot broadcasting alert streams
  • Channel routing: errors routed to errors, backups to notifications

Code Formatting

  • Laravel Pint linting enforcing PSR-12 formatting rules
  • ESLint & Prettier validating Vue 3 and TypeScript quality
  • Strict static analysis coverage using Larastan v3 rulesets

// tech stack

Backend
Laravel 12, PHP 8.4
MoonShine 4.x Admin
Horizon / Queue workers
Larastan / Rector / Pint
Frontend
Vue 3 (Composition)
TypeScript 5.8
Pinia state store
Alpine.js, Tailwind v4
Data & Cache
PostgreSQL 16
SQLite (Dev Environment)
Redis 7
EAV-hybrid options mapping
DevOps & Alerts
Laravel Envoy deploy
Telegram alert system
Sentry monitoring
IndexNow API integration
$ project-stats --summary
280+ domain classes
32 controllers
25 Vue components
87 DB migrations
123 PHPUnit tests