# Architecture — L'Ami Fiduciaire > Generated: 2026-03-08 | Scan Level: Quick ## Executive Summary L'Ami Fiduciaire is a full-stack web application built for fiduciary/accounting firm client management. It provides a multi-tenant workspace system where firms can manage clients, folders (dossiers), and document exchanges with external clients via email-based invitation links. The application follows a **server-driven SPA** architecture using Laravel 12 as the backend with Inertia.js v2 bridging to a Vue 3 frontend. This eliminates the need for a separate API layer — controllers render Vue pages directly via Inertia, while Wayfinder generates TypeScript route functions for type-safe navigation. ## Architecture Pattern **Pattern:** Full-Stack Monolith with Server-Driven SPA (Inertia.js) ``` ┌─────────────────────────────────────────────────────┐ │ Browser (SPA) │ │ Vue 3 + TypeScript + Tailwind CSS 4 + shadcn-vue │ │ Inertia.js Client → Page Components │ └──────────────────────┬──────────────────────────────┘ │ XHR / Inertia Protocol ┌──────────────────────▼──────────────────────────────┐ │ Laravel 12 (PHP 8.4) │ │ ┌─────────────────────────────────────────────┐ │ │ │ Middleware Pipeline │ │ │ │ (Auth, Workspace, Admin, Inertia, etc.) │ │ │ └──────────────────┬──────────────────────────┘ │ │ ┌──────────────────▼──────────────────────────┐ │ │ │ Controllers → Inertia::render('Page', props)│ │ │ └──────────────────┬──────────────────────────┘ │ │ ┌──────────────────▼──────────────────────────┐ │ │ │ Eloquent ORM → Models + Relationships │ │ │ └──────────────────┬──────────────────────────┘ │ └──────────────────────┼──────────────────────────────┘ │ ┌──────────────────────▼──────────────────────────────┐ │ MySQL 8.4 (via Docker/Sail) │ └─────────────────────────────────────────────────────┘ ``` ## Technology Stack | Category | Technology | Version | |----------|-----------|---------| | Backend Framework | Laravel | 12.x | | Backend Language | PHP | 8.2+ (8.4 runtime) | | Frontend Framework | Vue.js | 3.5+ | | Frontend Language | TypeScript | 5.2+ | | SPA Bridge | Inertia.js | v2 | | CSS Framework | Tailwind CSS | 4.x | | UI Components | shadcn-vue (reka-ui) | 2.4+ | | Build Tool | Vite | 7.x | | Database | MySQL | 8.4 | | Authentication | Laravel Fortify | v1 | | Route Generation | Laravel Wayfinder | 0.1.x | | File Management | Spatie Media Library | 11.x | | Activity Logging | Spatie Activity Log | 4.x | | Enums | bensampo/laravel-enum | 6.x | | Testing | Pest | 4.x | | Code Style (PHP) | Laravel Pint | 1.x | | Code Style (JS) | ESLint 9 + Prettier 3 | - | | Containerization | Docker / Laravel Sail | - | | WebSockets | Soketi | latest | | Mail Testing | Mailpit | latest | ## Data Architecture ### Eloquent Models | Model | Table | Key Relationships | |-------|-------|-------------------| | User | users | belongsToMany(Workspace), hasMany(Client) | | Workspace | workspaces | belongsToMany(User) via WorkspaceUser | | WorkspaceUser | workspace_user | Pivot: user_id, workspace_id, role | | Client | clients | belongsTo(Workspace), hasMany(Folder) | | Folder | folders | belongsTo(Client), hasMany(Message), hasMany(FolderInvitation), media | | FolderInvitation | folder_invitations | belongsTo(Folder) — token-based access | | Message | messages | belongsTo(Folder) | ### Business Enums | Enum | Purpose | |------|---------| | ActorType | Distinguishes user types in activity logs | | ClientStatus | Client lifecycle status | | FolderPriority | Folder urgency level | | FolderStatus | Folder workflow status | | FolderType | Category of folder/dossier | | LegalForm | Legal entity type for clients | | MessageType | Type of message in folder | | UserGroup | User permission group | | WorkspaceUserRole | Role within a workspace | ### Database Migrations (16 files) - Core: users, cache, jobs tables - Auth: two-factor columns on users - Multi-tenant: workspaces, workspace_user - Business: clients, folders, folder_invitations, messages - Packages: activity_log, media tables - Extensions: confirmation fields on folders, responsable/suivi fields on clients ## Authentication & Authorization - **Laravel Fortify** provides headless auth: login, registration, password reset, email verification, two-factor authentication (TOTP) - **Custom Middleware:** - `EnsureUserIsAdmin` — Admin-only route protection (users, workspaces management) - `EnsureUserHasWorkspace` — Workspace context enforcement - `ValidateFolderInvitation` — Token-based access for external client portal - `HandleInertiaRequests` — Shares auth/workspace data with frontend - `HandleAppearance` — Theme/appearance persistence ## Route Architecture ### Authenticated Routes (auth + verified) - `GET /dashboard` — DashboardController (invokable) - `POST /workspace/switch` — WorkspaceSwitchController (invokable) ### Workspace-Scoped Routes (auth + verified + workspace) - `CRUD /clients` — ClientController (resource) - `CRUD /folders` — FolderController (resource) - `POST /folders/{folder}/messages` — FolderMessageController - `POST /folders/{folder}/media` — FolderMediaController (upload) - `GET /folders/{folder}/media/{mediaId}` — FolderMediaController (download) ### Admin Routes (auth + verified + admin) - `CRUD /users` — UserController (resource) - `CRUD /workspaces` — WorkspaceController (resource) ### Public Client Portal (/c/ prefix, folder.invitation middleware) - `GET|POST /c/upload/{token}` — Client file upload - `GET|POST /c/confirm/{token}` — Client folder confirmation - `GET|POST /c/refuse/{token}` — Client folder refusal ### Settings Routes (auth / auth+verified) - `GET|PATCH /settings/profile` — Profile management - `DELETE /settings/profile` — Account deletion - `GET|PUT /settings/password` — Password change (throttled) - `GET /settings/appearance` — Appearance/theme settings - `GET /settings/two-factor` — Two-factor authentication management ## Email System 5 folder-related mailables handle client communication: - **FolderInviteMail** — Invitation to access a folder - **FolderFileRequestMail** — Request for file upload - **FolderConfirmationMail** — Folder confirmation notification - **FolderSituationMail** — Folder status update - **FolderTextMessageMail** — Text message notification ## Frontend Architecture ### Component Organization - **Pages** (31 Vue components): Mapped to routes via Inertia, organized by domain (auth, clients, folders, users, workspaces, settings, client portal) - **Layouts** (8 components): AppLayout (sidebar/header variants), AuthLayout (card/simple/split variants), SettingsLayout - **UI Components**: shadcn-vue design system with 20+ component groups (Button, Card, Dialog, Dropdown, Input, Select, Table, Tabs, Tooltip, etc.) - **Business Components**: ClientForm, FolderForm, UserForm, WorkspaceForm, WorkspaceSwitcher, Pagination - **Composables** (4): useAppearance, useCurrentUrl, useInitials, useTwoFactorAuth ### State Management - No client-side store (Redux/Pinia) — state is server-driven via Inertia props - `useForm()` from Inertia handles form state and submission - Composables for shared client-side logic ### Type System - TypeScript strict mode across all frontend code - Type definitions in `resources/js/types/` (auth, navigation, UI types) - Wayfinder generates typed route functions ## Testing Strategy - **Framework:** Pest 4 (PHPUnit 12 compatible) - **Test Suites:** Feature (13 test files) + Unit - **Coverage Areas:** Authentication, settings, dashboard, user groups - **Factories:** Model factories for test data generation - **CI Matrix:** PHP 8.4 and 8.5 ## CI/CD Pipeline ### Lint Workflow (`lint.yml`) - Triggers: push/PR to develop, main, master, workos - Steps: PHP setup → Composer install → npm install → Pint → Prettier → ESLint ### Test Workflow (`tests.yml`) - Triggers: push/PR to develop, main, master, workos - Matrix: PHP 8.4, 8.5 - Steps: PHP setup → Node 22 → Composer install → npm install → env setup → key generation → Vite build → Pest ## Deployment Configuration ### Docker Compose (Laravel Sail) - **laravel.test** — PHP 8.5 application container (port 80, Vite 5173) - **mysql** — MySQL 8.4 database (port 3306) - **mailpit** — Email testing UI (ports 1025, 8025) - **soketi** — WebSocket server / Pusher-compatible (port 6001) ### SSR Support - Inertia SSR is enabled (`inertia.php` → `ssr.enabled = true`) - SSR entry point: `resources/js/ssr.ts` - SSR server URL: `http://127.0.0.1:13714`