Architecture
This document outlines the architecture of the Meo Mai Moi application, including the tech stack, key technical decisions, and coding standards.
Tech and architecture
- Backend: Laravel 12 + PHP 8.5
- Frontend: React 19 + TypeScript + Vite 7 + Tailwind CSS v4 + shadcn/ui
- Database: PostgreSQL only (all envs). SQLite is not supported.
- Build/Run: Dockerized with multi-stage builds; frontend assets copied into backend image.
- API First: OpenAPI documented with contract testing. The frontend uses Orval to automatically generate typesafe React Query hooks from the backend specification.
- Admin Panel: Filament 5 with comprehensive pet and user management.
Key Technical Decisions
- Authentication: Fortify + Jetstream (API features), session-based via Sanctum for SPA; email verification required by default
- File Storage: Local storage under
storage/app/public - Quality Gates: PHPStan Level 5, Deptrac architecture enforcement
- Background Jobs: Laravel Scheduler + Database Queue (supervisor-managed)
- Pet-User Relationships: Flexible relationship system supporting multiple relationship types (owner/foster/editor/viewer) with temporal tracking
Database Schema
Pet-User Relationship System
The application uses a flexible relationship system to manage connections between pets and users:
pet_relationships table:
user_id: Foreign key to users tablepet_id: Foreign key to pets tablerelationship_type: Enum (owner, foster, editor, viewer)start_date: When relationship beganend_date: When relationship ended (null = active)created_by: User who created this relationship
Relationship Types:
- owner: Full ownership rights including transfer capabilities
- foster: Temporary caretaking access during fostering
- editor: Edit access for pet management assistance
- viewer: Read-only access for monitoring
Key Features:
- Multiple concurrent relationships per pet
- Historical tracking with start/end dates
- Relationship lifecycle management
- Replaces simple ownership with flexible access control
Background Jobs & Scheduling
The application uses Laravel's built-in queue and scheduler systems for background task processing:
Components:
- Scheduler: Runs every minute via supervisor, triggers scheduled commands (e.g., vaccination reminders at 09:00)
- Queue Worker: Processes async jobs from the
jobsdatabase table (emails, push notifications) - Queue Driver: PostgreSQL database (
QUEUE_CONNECTION=database)
Scheduled Tasks (defined in routes/console.php):
- Vaccination reminders: Daily at 09:00
- Birthday reminders: Daily at 08:00
- Chat digests: Every 15 minutes (batches unread messages for email delivery)
Supervisor Programs (in supervisord.conf):
scheduler: Infinite loop runningschedule:runevery 60 secondsqueue-worker: Processes queued jobs with retries and backoff
Local Development:
- Set
QUEUE_CONNECTION=syncin.envfor immediate job execution (no worker needed) - Or run
php artisan queue:workmanually to process queued jobs
Backend Architecture (Laravel)
Layer Structure (Enforced by Deptrac)
Http (Controllers, Middleware, Requests)
↓
Services (Business Logic)
↓
Domain (Models, Enums)Key Patterns:
- Controllers are thin - delegate to Services
- Services contain business logic and orchestration
- Models are data containers with relationships
- Policies use
$user->can(...)for authorization - Use Spatie Permission as single source of truth for RBAC
- Authorization boundary:
- Main app (
/api+ SPA) uses relationship/ownership rules for domain actions (no admin-role shortcut for owner-only flows). - Elevated moderation and operational actions belong to admin surfaces (
/adminor/api/admin/*).
- Main app (
- API Responses: All controllers use
ApiResponseTraitto return a standardized JSON envelope:json{ "success": true, "data": { ... }, "message": "Operation successful" } - Version Header: The
AppVersionHeadermiddleware attachesX-App-Versionto every API response (value fromconfig/version.php). Must be listed incors.phpexposed_headersfor JS access.
Frontend Architecture (React + TypeScript)
Component Structure
src/
├── components/ui/ # shadcn/ui components
├── components/ # Reusable business components
├── pages/ # Route components (don't import other pages)
├── hooks/ # Custom React hooks
├── lib/ # Utilities and configurations
├── api/ # API client and types
└── mocks/ # MSW handlers for testingState Management:
- React Query for server state
- React Hook Form for form state
- AuthProvider for authentication context
- Local state with
useStatefor UI state
App Update Detection:
Two complementary mechanisms ensure users run the latest version:
- API version header (
useVersionCheck): The Axios interceptor readsX-App-Versionfrom every response. On mismatch with the initially seen version, a persistent toast offers Reload / Later (30-min snooze). - PWA service worker (
usePwaUpdate): Detects new frontend asset bundles via SW update cycle (hourly poll + focus events). Shows a similar toast and applies the new service worker only after the user confirms the refresh.
UI Component Library:
- shadcn/ui: All 24 components installed and configured for Tailwind v4
- Copy-paste component architecture (components owned by the project)
- Built on Radix UI primitives for accessibility
- Fully customizable with Tailwind CSS
- Tailwind CSS v4: Modern utility-first CSS framework
- Uses
@import 'tailwindcss'syntax (v4 style) - Dual color system: HSL (backward compat) + OKLCH (modern)
@theme inlinedirective for CSS variable exposure- Custom dark mode with
@custom-variant dark (&:is(.dark *))
- Uses
- lucide-react: Icon library for consistent iconography
Theme System:
- App-owned theme state lives in
frontend/src/components/shared/theme-provider.tsx - DOM and PWA theme side effects are centralized in
frontend/src/lib/theme-runtime.ts - The provider exposes both the saved preference (
light/dark/system) and the resolved effective theme (light/dark) - The shared default theme is
system; both the React provider and the runtime resolve through the samevite-ui-themestorage key - The backend SPA shell (
backend/resources/views/welcome.blade.php.template) applies the initial class,data-theme,color-scheme,theme-color, and manifest before React hydrates to avoid first-paint mismatches across browsers
Custom Form Components:
FormField: Generic form field wrapper with validation displayCheckboxField: Checkbox with label and error handlingFileInput: File upload field with validation- All follow shadcn/ui design patterns with proper accessibility
Security Patterns
- CSRF protection enabled
- Mass assignment protection via
$fillable - File upload validation and sanitization
- Rate limiting on API endpoints
- Input validation via Form Requests
Coding Standards & Best Practices
PHP/Laravel Standards
Code Style:
- Laravel Pint enforces PSR-12 with Laravel conventions
- Run before commit:
./vendor/bin/pint
Naming Conventions:
- Models: Singular PascalCase (
Pet,User,PlacementRequest) - Controllers: PascalCase + Controller suffix (
PetController) - Services: PascalCase + Service suffix (
PetManagementService) - Methods: camelCase (
createPet,updateStatus)
TypeScript/React Standards
Code Style:
- ESLint + Prettier with strict TypeScript rules
- Run before commit:
bun run lint && bun run typecheck
Documentation Standards
Code Comments:
- Explain why, not what
- Document complex business logic
API Documentation:
- OpenAPI annotations on all endpoints
- Keep swagger docs up to date