Skip to main content

System Overview

This document describes the Laravel + Filament system architecture — how the four subsystems are structured, how requests flow through the stack, and how this maps to the existing C4 container diagram.


The Four Subsystems

┌──────────────────────────────────────────────────────────────────┐
│ Laravel Octane (Swoole) │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ │
│ │ Admin Panel │ │ Client Portal │ │ Partner Portal │ │
│ │ /admin/* │ │ /portal/* │ │ /partner/* │ │
│ │ │ │ │ │ │ │
│ │ Internal staff │ │ CLIENT_USERS │ │ PARTNER_ │ │
│ │ (lawyers, │ │ Read matters, │ │ ATTORNEY_USERS │ │
│ │ billing, admin)│ │ pay invoices │ │ Submit time │ │
│ │ │ │ │ │ │ │
│ │ Filament panel │ │ Filament panel │ │ Filament panel │ │
│ │ Session auth │ │ Session auth │ │ Session auth │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬─────────┘ │
│ │ │ │ │
│ └───────────────────┴────────────────────┘ │
│ │ │
│ ┌────────────────▼───────────────┐ │
│ │ REST API Layer │ │
│ │ /api/v1/* │ │
│ │ │ │
│ │ Implements OpenAPI contracts │ │
│ │ Sanctum token authentication │ │
│ │ Used by mobile / integrations │ │
│ └────────────────┬───────────────┘ │
│ │ │
│ ┌────────────────────────────▼──────────────────────────────┐ │
│ │ Domain Services Layer │ │
│ │ │ │
│ │ RateResolutionService InvoiceGeneratorService │ │
│ │ RbacCheckerService TenantResolverService │ │
│ │ DocumentService │ │
│ └────────────────────────────┬──────────────────────────────┘ │
│ │ │
└───────────────────────────────┼──────────────────────────────────┘

┌───────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────┐ ┌────────────┐ ┌────────────┐
│ PostgreSQL│ │ Redis │ │ S3 / R2 │
│ (primary) │ │ Cache + │ │ Documents │
│ │ │ Sessions + │ │ │
│ │ │ Queues │ │ │
└───────────┘ └────────────┘ └────────────┘

External: Stripe (payments) · Meilisearch (search) · SMTP (email)

Subsystem 1 — Admin Panel (/admin)

Users: Internal staff — lawyers, paralegals, billing administrators, firm admins.

Auth: Laravel guard web backed by the users table, linked to FIRM_USER_PROFILES. Filament's built-in login page handles authentication.

Tenancy: The firm is resolved from the URL slug (/admin/{firm-slug}/...). Filament's HasTenants interface on LawFirm automatically scopes all Eloquent queries to the current firm.

What it provides:

  • Full CRUD for matters, clients, time entries, invoices, documents
  • Billing workflows: generate invoice, record payment, issue credit note
  • HR: staff profiles, org units, performance reviews, bonus awards
  • RBAC management: role assignment, access grant administration
  • Partner management: partner firms, partner attorneys, partner bills
  • Reporting widgets: billable hours this month, outstanding invoices, active matters

Subsystem 2 — Client Portal (/portal)

Users: CLIENT_USERS — clients who have been invited to the portal.

Auth: Laravel guard client backed by the users table, linked via CLIENT_USERS to CLIENT_CONTACTS. A client can only see records associated with their own client record.

What it provides (read-heavy, limited write):

  • View their active matters and matter status
  • Download invoices and documents shared with them
  • Pay outstanding invoices (Stripe integration)
  • View and respond to appointment invitations

Subsystem 3 — Partner Portal (/partner)

Users: PARTNER_ATTORNEY_USERS — attorneys from partner firms who collaborate on shared matters.

Auth: Laravel guard partner backed by the users table, linked via PARTNER_ATTORNEY_USERS to PARTNER_ATTORNEYS.

What it provides:

  • View matters they are assigned to via MATTER_PARTNER_ATTORNEYS
  • Submit time entries for billing
  • View partner bills issued to their firm
  • Download matter documents they have access to

Subsystem 4 — REST API Layer (/api/v1)

Users: Mobile apps, external integrations, third-party consumers.

Auth: Laravel Sanctum API tokens (Bearer token in Authorization header). Tokens are scoped with named abilities matching the permission codes in the PERMISSIONS table.

Contract: Implements the same OpenAPI specification defined in /openapi/user-apis.yaml and /openapi/admin-api.yaml. Any client that works against the original API spec works against this implementation without changes.


Request Flow

Filament Panel Request

Browser
│ HTTPS POST /admin/{firm}/matters

Octane Worker (in memory)
│ 1. TenantMiddleware resolves LAW_FIRMS from slug → sets app tenant
│ 2. FilamentAuthMiddleware validates session → resolves User + FirmUserProfile
│ 3. PolicyMiddleware checks Laravel Policy for the resource action

Filament Livewire Component
│ 4. Form validated, Eloquent model saved (scoped to tenant)
│ 5. Domain service called if business logic involved
│ 6. Activity logged via spatie/activitylog observer
│ 7. Notification broadcast via Reverb

Response rendered (Livewire diff, not full page)

API Request (Sanctum)

Mobile App
│ HTTPS POST /api/v1/matters
│ Authorization: Bearer {token}

Octane Worker
│ 1. SanctumMiddleware validates token → resolves User
│ 2. TenantMiddleware resolves firm from token's user FK to firm_user_profiles
│ 3. PolicyMiddleware checks Laravel Policy

API Controller
│ 4. Request validated (Form Request class)
│ 5. Domain service called
│ 6. Activity logged
│ 7. JSON response via API Resource class

JSON Response

Background Job Flow

Trigger (e.g., "Generate Invoice" Filament Action)


GenerateInvoiceJob dispatched to Redis queue


Horizon worker picks up job

├── InvoiceGeneratorService::generate(Matter $matter)
│ ├── Fetches approved time entries
│ ├── Resolves rates via RateResolutionService
│ ├── Creates Invoice + InvoiceLineItems (append-only)
│ └── Marks time entries as invoiced

├── spatie/laravel-pdf generates PDF → uploads to S3

└── Notification dispatched → Reverb → browser

Deployment Architecture

                    ┌──────────────┐
│ Nginx / │
HTTPS ────►│ Caddy │
│ (reverse │
│ proxy) │
└──────┬───────┘

┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Octane │ │ Octane │ │ Octane │
│ Worker 1 │ │ Worker 2 │ │ Worker N │
└──────────┘ └──────────┘ └──────────┘

├──► PostgreSQL (primary + read replica)
├──► Redis (cache + sessions + queues)
├──► S3-compatible storage
└──► Meilisearch

┌──────────┐
│ Horizon │ (separate process, same codebase)
│ Queue │──► PostgreSQL, Redis, S3
│ Worker │
└──────────┘

┌──────────┐
│ Reverb │ (WebSocket server, same codebase)
│ WebSocket│──► Redis (pub/sub)
└──────────┘

Octane, Horizon, and Reverb all run from the same Laravel application codebase. They are separate OS processes but share the same config/, app/, and database/ code.