v1.0
Docs / Introduction

CI4 Modern Architecture

Welcome to the comprehensive documentation for the CI4 Modern Architecture Blueprint. This guide explains every custom pattern, design decision, and implementation detail used in this CodeIgniter 4 application.

Table of Contents

  1. What Is This?
  2. Who Is This For?
  3. Problems This Solves
  4. Quick Start
  5. Documentation Map

1. What Is This?

Technical Definition

This is a production-ready architecture blueprint for CodeIgniter 4 that implements:

Simple Analogy

Think of a standard CI4 app like a house with one big room:

This architecture is like a house with proper rooms:

Standard CI4 App: This Architecture: ┌──────────────────────┐ ┌──────────────────────┐ │ │ │ ┌────┐ ┌────┐ ┌────┐ │ │ Everything mixed │ →→→ │ │Admin│ │Web │ │API │ │ │ together in one │ │ └────┘ └────┘ └────┘ │ │ place │ │ ┌──────────────────┐ │ │ │ │ │ Domain (logic) │ │ │ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │ │ │ Infrastructure │ │ └──────────────────────┘ └──────────────────────┘

2. Who Is This For?

If You Are...This Helps You By...
Building a medium-large appProviding clear organization from day one
Working in a teamEnabling parallel work on different modules
Planning for microservicesMaking future extraction easy via modular design
Tired of fat controllersMoving business logic to proper Domain layer
Wanting testable codeSeparating concerns for easy unit testing

3. Problems This Solves

Problem 1: Fat Controllers

Before: Controllers do everything

class UserController extends BaseController
{
    public function register()
    {
        // Validation (100 lines)
        // Email checking (20 lines)
        // Password hashing (10 lines)
        // Database insert (30 lines)
        // Send welcome email (40 lines)
        // Return response (10 lines)
    }
    // Total: 210 lines in ONE method!
}

After: Controller is thin, delegates to services

class UserController extends BaseController
{
    #[Route('/register', methods: ['POST'])]
    public function register()
    {
        $result = $this->userService->register($this->request->getPost());
        return $this->response->setJSON($result);
    }
    // Total: 5 lines!
}

Problem 2: Scattered Business Rules

Before: Same validation in multiple places

// In RegisterController
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { ... }

// In ProfileController (duplicated!)
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { ... }

// In AdminUserController (duplicated again!)
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { ... }

After: Single source of truth in Value Object

// Domain/Shared/ValueObjects/Email.php
class Email
{
    public function __construct(string $email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException("Invalid email");
        }
        $this->value = strtolower($email);
    }
}

// Usage anywhere:
$email = new Email($input);  // Validates automatically

Problem 3: Hard to Test

Before: Need HTTP and database to test

// Testing requires full HTTP request + database
public function testUserRegistration()
{
    $result = $this->call('POST', '/register', [...]);
    // Tests framework + database + business logic all at once
}

After: Pure unit test with mocks

// Test business logic in isolation
public function testUserRegistration()
{
    $mockRepo = $this->createMock(UserRepositoryInterface::class);
    $service = new UserService($mockRepo);
    
    $result = $service->register($data);
    // Tests ONLY business logic, fast and reliable
}

4. Quick Start

Step 1: Understand the Folder Structure

app/
├── Attributes/       # PHP 8 decorators (Route, Trace, Monitor)
├── Core/             # Framework extensions
├── Domain/           # Pure business logic (no framework!)
├── Infrastructure/   # Database, cache, external APIs
├── Modules/          # Feature packages (Admin, Web, Api, Member)
├── Filters/          # HTTP middleware
└── Commands/         # CLI commands

Step 2: Create Your First Feature

# Create a new "Blog" module in Web
mkdir -p app/Modules/Web/Blog/Controllers
mkdir -p app/Modules/Web/Blog/Views

Step 3: Add a Controller with Route Attribute

<?php
namespace App\Modules\Web\Blog\Controllers;

use App\Controllers\BaseController;
use App\Attributes\Route;

class BlogController extends BaseController
{
    #[Route('/blog', methods: ['GET'])]
    public function index()
    {
        return view('Modules/Web/Blog/Views/index');
    }
}

Step 4: Routes Are Auto-Registered!

# Check routes - your /blog route is there
docker-compose exec php php spark routes | grep blog

# Output:
# GET    /blog    BlogController::index

5. Documentation Map

Here's what each section covers:

PageWhat You'll LearnRead This If...
InstallationDocker setup, requirements, troubleshootingYou're setting up the project
ArchitectureHigh-level structure, layers, request flowYou want the big picture
ModulesCreating features, module structureYou're adding new features
Domain LayerEntities, Value Objects, Services, PoliciesYou're writing business logic
InfrastructureRepositories, Cache, Database accessYou're connecting to external systems
Frontend ArchitectureFrontend structure, build process, JS/CSSYou're working on the UI/UX layer
AttributesRoute, Middleware, Trace, MonitorYou want to use PHP 8 attributes
RoutingHow route scanning and caching worksYou're debugging route issues
ComponentsReusable view partials, layoutsYou're building UI
MonitoringPerformance tracking, tracing, PrometheusYou need production observability
TimelineBuild order, what was created whenYou want to understand the evolution
PrefetchHow the instant navigation system worksYou want to understand the speed
Recommended Reading Order

If you're new: Architecture → Modules → Domain → Infrastructure

If you just want to code: Modules → Attributes → Components

ESC

Start typing to search the documentation