Initial commit of the L'Ami Fiduciaire SaaS platform built on Laravel 12, Vue 3, Inertia.js 2, and Tailwind CSS 4. Story 0.1 (rename folders to declarations in database) is implemented and code-reviewed: migration, rollback, and 6 Pest tests all passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
201 lines
9.9 KiB
Markdown
201 lines
9.9 KiB
Markdown
# 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`
|