php-template/docs/architecture.md
Snider ef4379a76f
Some checks are pending
CI / PHP 8.2 (push) Waiting to run
CI / PHP 8.3 (push) Waiting to run
CI / PHP 8.4 (push) Waiting to run
CI / Assets (push) Waiting to run
docs: add comprehensive documentation for core-template
Add documentation covering architecture, modules, getting started,
and security considerations for the Core PHP Framework starter template.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 21:21:11 +00:00

10 KiB

title description updated
Architecture Technical architecture of core-template - the starter template for Core PHP Framework applications 2026-01-29

Architecture

core-template is the official starter template for building applications with the Core PHP Framework. It provides a pre-configured Laravel 12 application with the modular monolith architecture, Livewire 3, and Flux UI integration.

Overview

core-template/
├── app/
│   ├── Http/Controllers/      # Traditional controllers (rarely used)
│   ├── Models/                # Application-wide Eloquent models
│   ├── Mod/                   # Feature modules (your code goes here)
│   └── Providers/             # Service providers
├── bootstrap/
│   ├── app.php                # Application bootstrap with Core providers
│   └── providers.php          # Additional providers
├── config/
│   └── core.php               # Core framework configuration
├── public/
│   └── index.php              # Web entry point
├── resources/
│   ├── css/app.css            # Tailwind entry point
│   ├── js/app.js              # JavaScript entry point
│   └── views/                 # Global Blade views
├── routes/
│   ├── web.php                # Fallback web routes
│   ├── api.php                # Fallback API routes
│   └── console.php            # Console command routes
└── tests/
    ├── Feature/               # HTTP/Livewire feature tests
    └── Unit/                  # Unit tests

Bootstrap Process

The application bootstrap (bootstrap/app.php) registers Core PHP Framework providers:

return Application::configure(basePath: dirname(__DIR__))
    ->withProviders([
        \Core\LifecycleEventProvider::class,  // Event system
        \Core\Website\Boot::class,            // Website components
        \Core\Front\Boot::class,              // Frontend (Livewire, Flux)
        \Core\Mod\Boot::class,                // Module discovery
    ])
    ->withRouting(...)
    ->withMiddleware(function (Middleware $middleware) {
        \Core\Front\Boot::middleware($middleware);
    })
    ->create();

Provider Loading Order

  1. LifecycleEventProvider - Sets up the event-driven architecture
  2. Website\Boot - Registers website-level functionality
  3. Front\Boot - Configures Livewire and frontend middleware
  4. Mod\Boot - Discovers and loads modules from configured paths

Module System

Modules are self-contained feature packages that register via lifecycle events. This is the core architectural pattern of the framework.

Module Paths

Configured in config/core.php:

'module_paths' => [
    app_path('Core'),     // Local framework overrides (EUPL-1.2)
    app_path('Mod'),      // Your application modules
    app_path('Website'),  // Website-specific code
],

Module Structure

Each module lives in app/Mod/{ModuleName}/ with a Boot.php entry point:

app/Mod/Blog/
├── Boot.php              # Event listeners (required)
├── Models/
│   └── Post.php          # Eloquent models
├── Routes/
│   ├── web.php           # Web routes
│   └── api.php           # API routes
├── Views/
│   └── posts/
│       └── index.blade.php
├── Livewire/
│   └── PostListPage.php  # Livewire components
├── Migrations/
│   └── 2025_01_01_create_posts_table.php
└── Tests/
    └── PostTest.php

Boot.php Pattern

The Boot.php class declares which lifecycle events it responds to:

<?php

declare(strict_types=1);

namespace App\Mod\Blog;

use Core\Events\WebRoutesRegistering;
use Core\Events\ApiRoutesRegistering;
use Core\Events\AdminPanelBooting;
use Core\Events\ConsoleBooting;

class Boot
{
    /**
     * Event listeners - class is only instantiated when events fire
     */
    public static array $listens = [
        WebRoutesRegistering::class => 'onWebRoutes',
        ApiRoutesRegistering::class => 'onApiRoutes',
        AdminPanelBooting::class => ['onAdminPanel', 10],  // With priority
        ConsoleBooting::class => 'onConsole',
    ];

    public function onWebRoutes(WebRoutesRegistering $event): void
    {
        // Register routes
        $event->routes(fn() => require __DIR__.'/Routes/web.php');

        // Register view namespace (accessed as 'blog::view.name')
        $event->views('blog', __DIR__.'/Views');
    }

    public function onApiRoutes(ApiRoutesRegistering $event): void
    {
        $event->routes(fn() => require __DIR__.'/Routes/api.php');
    }

    public function onAdminPanel(AdminPanelBooting $event): void
    {
        // Register admin navigation
        $event->navigation('Blog', 'blog.admin.index', 'newspaper');

        // Register admin resources
        $event->resource('posts', PostResource::class);
    }

    public function onConsole(ConsoleBooting $event): void
    {
        // Register artisan commands
        $event->commands([
            ImportPostsCommand::class,
        ]);
    }
}

Lifecycle Events

Event When Fired Common Uses
WebRoutesRegistering Web routes loading Public routes, views
ApiRoutesRegistering API routes loading REST endpoints
AdminPanelBooting Admin panel setup Navigation, resources
ClientRoutesRegistering Authenticated SaaS routes Dashboard, settings
ConsoleBooting Artisan bootstrapping Commands, schedules
McpToolsRegistering MCP server setup AI agent tools

Lazy Loading

Modules are discovered at boot time, but their Boot classes are only instantiated when the events they listen to are fired. This means:

  • Console commands don't load web routes
  • API requests don't load admin panel code
  • Unused modules have minimal overhead

Dependency Packages

The template depends on four Core PHP Framework packages:

Package Namespace Purpose
host-uk/core Core\ Foundation: events, modules, lifecycle
host-uk/core-admin Core\Admin\ Admin panel, Livewire modals, Flux UI
host-uk/core-api Core\Api\ REST API, scopes, rate limiting, webhooks
host-uk/core-mcp Core\Mcp\ Model Context Protocol for AI agents

These are loaded as Composer dependencies and provide the framework infrastructure.

Frontend Stack

Livewire 3

Livewire components live within modules:

// app/Mod/Blog/Livewire/PostListPage.php
<?php

declare(strict_types=1);

namespace App\Mod\Blog\Livewire;

use App\Mod\Blog\Models\Post;
use Illuminate\Contracts\View\View;
use Livewire\Component;
use Livewire\WithPagination;

class PostListPage extends Component
{
    use WithPagination;

    public function render(): View
    {
        return view('blog::posts.index', [
            'posts' => Post::latest()->paginate(10),
        ]);
    }
}

Flux UI

Flux Pro components are the standard UI library. Example usage:

<flux:modal name="edit-post">
    <flux:heading>Edit Post</flux:heading>
    <flux:input wire:model="title" label="Title" />
    <flux:textarea wire:model="content" label="Content" />
    <flux:button type="submit">Save</flux:button>
</flux:modal>

Asset Pipeline

Vite handles asset compilation:

  • resources/css/app.css - Tailwind CSS entry point
  • resources/js/app.js - JavaScript entry point
  • Module assets are not automatically included; import them in the main files or use @vite directive

Configuration

Core Framework (config/core.php)

return [
    // Paths to scan for modules
    'module_paths' => [
        app_path('Core'),
        app_path('Mod'),
        app_path('Website'),
    ],

    // Service configuration
    'services' => [
        'cache_discovery' => env('CORE_CACHE_DISCOVERY', true),
    ],

    // CDN configuration
    'cdn' => [
        'enabled' => env('CDN_ENABLED', false),
        'driver' => env('CDN_DRIVER', 'bunny'),
    ],
];

Environment Variables

Key Core-specific environment variables:

Variable Default Description
CORE_CACHE_DISCOVERY true Cache module discovery for performance
CDN_ENABLED false Enable CDN for static assets
CDN_DRIVER bunny CDN provider (bunny, cloudflare, etc.)
FLUX_LICENSE_KEY - Flux Pro license key (optional)

Testing

Tests use Pest PHP and follow Laravel conventions:

// tests/Feature/BlogTest.php
<?php

use App\Mod\Blog\Models\Post;

it('displays posts on the index page', function () {
    $posts = Post::factory()->count(3)->create();

    $this->get('/blog')
        ->assertOk()
        ->assertSee($posts->first()->title);
});

it('requires authentication to create posts', function () {
    $this->post('/blog', ['title' => 'Test'])
        ->assertRedirect('/login');
});

Test Organisation

  • Feature tests - HTTP requests, Livewire components, integration tests
  • Unit tests - Services, utilities, isolated logic
  • Module tests - Can live within the module directory (app/Mod/Blog/Tests/)

Routing

Route Registration

Routes are registered via module events, not the traditional routes/ directory:

// app/Mod/Blog/Routes/web.php
<?php

use App\Mod\Blog\Livewire\PostListPage;
use App\Mod\Blog\Livewire\PostShowPage;
use Illuminate\Support\Facades\Route;

Route::prefix('blog')->name('blog.')->group(function () {
    Route::get('/', PostListPage::class)->name('index');
    Route::get('/{post:slug}', PostShowPage::class)->name('show');
});

The routes/web.php and routes/api.php files are fallbacks for routes that don't belong to any module.

View Namespacing

Module views are namespaced:

{{-- Accessing blog module views --}}
@include('blog::partials.header')

{{-- In a Livewire component --}}
return view('blog::posts.index', [...]);

Namespace Conventions

Path Namespace License
app/Core/ Core\ (local) EUPL-1.2
app/Mod/ App\Mod\ Your choice
app/Website/ App\Website\ Your choice
vendor/host-uk/core/ Core\ EUPL-1.2

The app/Core/ directory is for local overrides of framework classes. Any class you place here will take precedence over the vendor package.