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?
Technical Definition
This is a production-ready architecture blueprint for CodeIgniter 4 that implements:
- Domain-Driven Design (DDD) — Business logic isolated from framework concerns
- Modular Monolith — Self-contained feature packages that can become microservices
- Attribute-Based Routing — Routes defined directly on controller methods
- View Components — Reusable UI building blocks (like React/Vue components)
- Built-in Observability — Automatic performance tracking and tracing
Simple Analogy
Think of a standard CI4 app like a house with one big room:
- Kitchen, bedroom, office all in one space
- Works for a studio apartment
- Becomes chaotic as you add more furniture
This architecture is like a house with proper rooms:
- Each room has a purpose (kitchen, bedroom, office)
- Rooms can be renovated independently
- Easier to find things and maintain order
2. Who Is This For?
| If You Are... | This Helps You By... |
|---|---|
| Building a medium-large app | Providing clear organization from day one |
| Working in a team | Enabling parallel work on different modules |
| Planning for microservices | Making future extraction easy via modular design |
| Tired of fat controllers | Moving business logic to proper Domain layer |
| Wanting testable code | Separating 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 automaticallyProblem 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 commandsStep 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/ViewsStep 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::index5. Documentation Map
Here's what each section covers:
| Page | What You'll Learn | Read This If... |
|---|---|---|
| Installation | Docker setup, requirements, troubleshooting | You're setting up the project |
| Architecture | High-level structure, layers, request flow | You want the big picture |
| Modules | Creating features, module structure | You're adding new features |
| Domain Layer | Entities, Value Objects, Services, Policies | You're writing business logic |
| Infrastructure | Repositories, Cache, Database access | You're connecting to external systems |
| Frontend Architecture | Frontend structure, build process, JS/CSS | You're working on the UI/UX layer |
| Attributes | Route, Middleware, Trace, Monitor | You want to use PHP 8 attributes |
| Routing | How route scanning and caching works | You're debugging route issues |
| Components | Reusable view partials, layouts | You're building UI |
| Monitoring | Performance tracking, tracing, Prometheus | You need production observability |
| Timeline | Build order, what was created when | You want to understand the evolution |
| Prefetch | How the instant navigation system works | You want to understand the speed |
If you're new: Architecture → Modules → Domain → Infrastructure
If you just want to code: Modules → Attributes → Components