Skip to Content
API v2Architecture

Architecture

Overview

Housr API v2 is a Laravel 12 application using the nwidart/laravel-modules package to organise code into self-contained domain modules. Each module is a mini-Laravel app with its own controllers, models, services, routes, migrations, and tests.

Directory Structure

Housr-API2/ ├── app/ # Shared application code │ ├── Auth/Guards/ # Custom auth guards (LegacyJwtGuard) │ ├── Http/ │ │ ├── Controllers/ # Root-level controllers (Contact, DemoUser, Outbound) │ │ ├── Middleware/ # Global middleware │ │ └── Requests/ # Root-level form requests │ ├── Models/ # Shared models (User, FeatureFlag, GlobalVariable, etc.) │ ├── Providers/ # Service providers (App, LegacyAuth) │ ├── Services/ # Shared services (Firebase, User, Notification, Contact) │ └── Traits/ # Shared traits (IsHousrTeamMember) ├── Modules/ # Domain modules (see below) ├── config/ # Laravel + module configuration ├── database/ # Root migrations, factories, seeders ├── routes/ # Root API routes (contact, demo user, outbound) ├── tests/ # Root-level tests + base test classes ├── docker/ # Docker build context ├── deploy/ # Nginx, Supervisor, PHP-FPM config └── modules_statuses.json # Module enable/disable flags

Module Structure

Each module under Modules/ follows this layout:

Modules/ModuleName/ ├── app/ │ ├── Http/ │ │ ├── Controllers/ # Module controllers │ │ ├── Requests/ # FormRequest validation classes │ │ ├── Resources/ # API Resources (JSON transformation) │ │ ├── Middleware/ # Module-specific middleware │ │ └── Integrations/ # Saloon connectors (e.g., Oakmans) │ ├── Models/ # Eloquent models │ ├── Services/ # Business logic services │ ├── Actions/ # Single-purpose action classes │ ├── Enums/ # PHP enums │ ├── Exceptions/ # Custom exceptions │ ├── Filters/ # Spatie Query Builder custom filters │ ├── Sorts/ # Spatie Query Builder custom sorts │ ├── Queries/ # Complex query builders │ ├── Factories/ # Non-Eloquent factory classes │ ├── Policies/ # Authorization policies │ ├── Traits/ # Module-specific traits │ ├── Interfaces/ # Contracts/interfaces │ ├── Contracts/ # Alternative name for interfaces │ ├── Providers/ │ │ ├── ModuleServiceProvider.php │ │ ├── RouteServiceProvider.php │ │ └── EventServiceProvider.php │ ├── Mail/ # Mailable classes │ ├── Jobs/ # Queue jobs │ ├── Console/Commands/ # Artisan commands │ ├── Listeners/ # Event listeners │ └── Widgets/ # Widget classes (Screens/Properties) ├── config/ │ └── config.php # Module configuration ├── database/ │ ├── migrations/ # Module migrations │ ├── factories/ # Eloquent model factories │ └── seeders/ # Database seeders ├── resources/views/ # Blade templates (emails, etc.) ├── routes/ │ └── api.php # Module API routes └── tests/ ├── Feature/ # Feature tests └── Unit/ # Unit tests

Request Flow

The standard request lifecycle:

HTTP Request → Middleware (CORS, ForceJson, PropagateContext, Auth) → Route (module RouteServiceProvider) → Controller → FormRequest (validation) → Service (business logic) → Model (data access) → Resource (JSON transformation) → JSON Response

Key Architectural Decisions

Service Layer Pattern

Controllers are thin — they accept a FormRequest, delegate to a Service class, and return a Resource. Services contain the business logic and are injected via constructor dependency injection.

Controller → FormRequest (validates) → Service (processes) → Resource (formats)

Modules Own Their Routes

Each module has a RouteServiceProvider that registers its own routes with a prefix. For example:

  • Properties: Routes prefixed with /properties
  • Bills: Routes prefixed with /api/bills
  • Events: Routes prefixed with /events

The root routes/api.php handles only cross-cutting concerns (contact forms, demo users, health checks).

Shared vs Module Models

  • Shared models (app/Models/): User, FeatureFlag, GlobalVariable, University, PropertyOperator, etc. These represent tables from the shared v1 database.
  • Module models (Modules/*/app/Models/): Domain-specific models owned by that module. For example House, V2Contract, Event, RideshareProfile.

Region Handling (US vs UK)

The config('app.USA') flag (from USA env var) toggles region-specific behaviour throughout the codebase:

  • Different database query logic (UK uses MySQL 8 spatial features, US uses MySQL 5.7 compatible queries)
  • Different features enabled/disabled (bills pricing, lease terms, building details)
  • Different API error documentation URLs

Lazy Loading Prevention

Model::preventLazyLoading() is enabled in non-production environments to catch N+1 query issues during development.

Observability

The PropagateContextMiddleware attaches a correlation_id (generated per request) and trace_id (from X-Trace-ID header or generated) to Laravel’s Context. These propagate into all log entries for request tracing.

Strict Types

Architecture rules enforced via Pest:

  • All App\ classes must use declare(strict_types=1)
  • No die, dd, or dump calls
  • PHP and security presets are applied
Last updated on