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`
|