High-Level Architecture
This is the C4 Level 2 view — the containers inside the Vetty platform and how they collaborate.
Containers
Component responsibilities
React SPA (ui/)
Runs entirely in the browser, served as static assets (built with react-scripts).
- Global state — two React Contexts:
AuthContext(current user, session) andTenantContext(active clinic + list of available clinics). Wraps the tree atsrc/App.js. - Service layer —
src/services/contains an HTTP adapter and a Mock adapter that both satisfy the same interface (auth,patients,appointments,invoices,pharmacy,suppliers,purchaseOrders,reminders,clinical,admin,tenants). Which adapter is used is decided at build/runtime throughREACT_APP_API_MODE. - Normalizers —
src/lib/normalizers.jsconverts raw Laravel responses into UI-friendly shapes (e.g. calculating pet age fromdate_of_birth). - Module switcher —
App.jskeeps the active module in state (dashboard,patients,appointments,billing,inventory, etc.) and renders the corresponding*Modulecomponent. A lightweight path check (/reset-password) routes the password-reset gate without introducing a full client router.
Mobile apps — vetty (vetyy/) and vetdoctor (vetdoctor/)
Two Flutter apps sharing the same architectural shape:
core/api/api_client.dart— nativehttpwrapper that attaches the bearer token fromAuthStore, parses JSON envelopes ({data: …}or raw), and throws a typedApiExceptionwithstatusCodeon non-2xx.core/auth/auth_store.dart— aChangeNotifier-backed store that persiststoken+user(andtenantin vetdoctor) viashared_preferences. Screens rebuild vialistenable: author aRootthat watchesauth.isAuthenticated.- Feature folders —
features/auth,features/pets(vetty only),features/shares,features/providers(vetty),features/appointments(vetdoctor),features/home. Each feature owns its thin service class plus a handful of screens. - Theming —
core/theme.dartusesColorScheme.fromSeedwith different seeds per app so the two are instantly distinguishable: vetty is emerald (0xFF059669), vetdoctor is indigo (0xFF4F46E5).
vetty is a consumer pet-owner app (bottom-nav: Pets + Nearby, profile sheet for sign-out). vetdoctor is a clinician companion: today's schedule, a big Scan-share FAB that opens mobile_scanner, and recently-scanned pets on the dashboard.
Laravel API (backend/routes/api.php)
- Flat v1 API at
/api/v1. - Routes split into three blocks: system, auth, and tenant-scoped (
/tenant/{tenant}/…) plus an owner portal block (/owner/me/*). - Tenant-scoped routes are wrapped with the
ResolveTenantMembershipmiddleware which resolves the{tenant}parameter into a model and attaches it to the request.
Filament admin (app/Filament/ + SystemadminPanelProvider)
- Mounted at
/systemadmin, uses thesystemadminauth guard (separateSystemAdminmodel and table). - Provides CRUD UIs for every core entity: SystemAdmins, Tenants, Users, Clients, Pets, Appointments, Invoices, Payments, Reminders.
- Has an
OverviewDashboardwidget with high-level platform metrics.
OpenAPI documentation
- Controllers are annotated with Swagger-PHP attributes (
#[OA\…]). App\OpenApi\OpenApiSpecdefines the top-level spec (tags: System, Auth, Tenants, Clinic, Owner).php artisan openapi:generatewritesstorage/app/api-docs/openapi.json.- The file is served at
GET /docs/openapi.json(seeroutes/web.php).
Request lifecycle
Here's what happens when the React SPA calls POST /api/v1/tenant/17/appointments:
Data flow — dashboard fetch
Why this shape
- Versioned URI API (
/api/v1) — gives a clean break if the contract changes without breaking existing pet-owner clients. - Filament side-car — Vetty operators and power users get a full CRUD UI "for free", without the React team having to build an admin console.
- Thin React router via state — modules are large and self-contained, so state-driven switching is simpler than a route-per-module client router. (A future migration to
react-routeris straightforward because the modules are already decoupled.) - Adapter-pattern service layer — the same UI can run fully offline against
mockservices for demos, testing and frontend-first development.