Backend Structure
backend/
├── app/
│ ├── Filament/
│ │ ├── Resources/
│ │ │ ├── SystemAdmins/…
│ │ │ ├── Tenants/…
│ │ │ ├── Users/…
│ │ │ ├── Clients/…
│ │ │ ├── Pets/…
│ │ │ ├── Appointments/…
│ │ │ ├── Invoices/…
│ │ │ ├── Payments/…
│ │ │ └── Reminders/…
│ │ └── Widgets/OverviewDashboard.php
│ ├── Http/
│ │ ├── Controllers/Api/V1/
│ │ │ ├── AuthController.php
│ │ │ ├── SystemController.php
│ │ │ ├── TenantController.php
│ │ │ ├── Clinic/
│ │ │ │ ├── DashboardController.php
│ │ │ │ ├── ClientController.php
│ │ │ │ ├── PetController.php
│ │ │ │ ├── AppointmentController.php
│ │ │ │ ├── InvoiceController.php
│ │ │ │ └── PatientOnboardingController.php
│ │ │ └── Owner/OwnerPortalController.php
│ │ ├── Middleware/
│ │ │ └── ResolveTenantMembership.php
│ │ ├── Requests/Api/V1/ # FormRequests (validation)
│ │ └── Resources/Api/V1/ # Response shaping
│ ├── Models/
│ │ ├── User.php
│ │ ├── SystemAdmin.php
│ │ ├── Tenant.php
│ │ ├── Client.php
│ │ ├── Pet.php
│ │ ├── Appointment.php
│ │ ├── Invoice.php
│ │ ├── Payment.php
│ │ └── Reminder.php
│ ├── OpenApi/OpenApiSpec.php # Global OpenAPI metadata
│ └── Providers/
│ ├── AppServiceProvider.php
│ └── Filament/SystemadminPanelProvider.php
├── bootstrap/
├── config/
│ ├── auth.php # Guards and providers
│ ├── sanctum.php # Token auth
│ ├── filament.php # Filament defaults
│ └── … (app, cache, database, filesystems, logging, mail, queue, services, session)
├── database/
│ ├── factories/
│ ├── migrations/ # Timestamped schema migrations
│ ├── seeders/DatabaseSeeder.php
│ └── database.sqlite # Optional dev DB
├── public/ # Front controller + index.php
├── resources/ # Views (Filament assets copied here)
├── routes/
│ ├── api.php
│ ├── web.php
│ └── console.php
├── storage/
│ └── app/api-docs/openapi.json # Generated output
├── tests/
├── composer.json
├── composer.lock
├── artisan
├── .env.example
└── phpunit.xml
Folder-by-folder
app/Http/Controllers/Api/V1
Controllers are thin: they invoke FormRequest::validated(), call Eloquent, and return a Resource. Business rules that don't fit in a model live here for now (see Clinic/InvoiceController::recordPayment updating status/balance).
Sub-folders group controllers by audience:
- Root (
AuthController,SystemController,TenantController) — public and cross-cutting endpoints. Clinic/— endpoints consumed by clinic staff (dashboards, pets, clients, appointments, invoices).Owner/— endpoints for the pet-owner portal, scoped to the logged-in owner only.
app/Http/Requests/Api/V1
One FormRequest per write endpoint. Naming pattern: StoreXRequest, UpdateXRequest, UpdateXStatusRequest etc. Each defines:
authorize()— returnstrue(delegated to route middleware) unless the endpoint needs custom policy.rules()— the validation schema.prepareForValidation()— optional normalization (e.g. cast empty strings tonull).
app/Http/Resources/Api/V1
One JsonResource per model. Resources know which fields are safe to expose. They use whenLoaded() to avoid touching relationships the caller didn't request.
app/Http/Middleware
ResolveTenantMembership— the gatekeeper for every/tenant/{tenant}/*request. It:- Binds
{tenant}to aTenantmodel (via route-model binding). - Ensures the authenticated user has an active
tenant_userrow for this tenant. - Attaches the tenant to the request attributes.
- Binds
app/Models
Each model declares $fillable + typed $casts + relationships. Conventions:
- Every model (except
User,SystemAdmin) has atenant_idFK and atenant()relationship. - Timestamps left at Laravel defaults.
passwordcast to'hashed'onUserandSystemAdmin.
app/Filament
Each Filament resource has three pieces:
*Resource.php— entry point that declares the model and the pages it exposes (index,create,edit).Schemas/*Form.php— form definition (fields and layout).Tables/*Table.php— list-view definition (columns, filters, actions).Pages/List*.php,Create*.php,Edit*.php— thin page classes that Filament renders.
app/OpenApi/OpenApiSpec.php
Holds:
#[OA\Info(title: 'Vetty Backend API', version: '1.0.0')]#[OA\Server(url: '/')]#[OA\Tag(...)]tags forSystem,Auth,Tenants,Clinic,Owner.- The shared
ErrorResponseschema.
Methods on individual controllers annotate their own paths.
routes/
api.php— the whole API contract (see Routes for the full map).web.php— two routes:GET /(health/info landing) andGET /docs/openapi.json(serves the generated spec).console.php— theopenapi:generateartisan command that writesstorage/app/api-docs/openapi.json.
database/
migrations/— 12+ files, timestamped. The first two (0001_01_01_000000_*) createusersandsystem_admin. The rest, dated2026_04_15_*, create the Vetty-specific tables.seeders/DatabaseSeeder.php— deterministic sample data for local dev.factories/— Eloquent factories that power tests.
tests/
Feature and unit tests. Uses database/verify.sqlite so the main dev DB isn't touched.