Tutorials Logic, IN info@tutorialslogic.com

PHP Application Architecture: Front Controller, Services, Repositories, and Dependency Injection

Application Boundaries

Architecture decides where a responsibility belongs and which direction dependencies point. A useful small PHP application keeps request parsing in the HTTP layer, business decisions in services or domain objects, and SQL behind focused persistence code.

After this lesson, you can trace a request through a front controller, distinguish controller, service, and repository responsibilities, inject a dependency through a constructor, and avoid turning MVC names into unnecessary layers.

Request Path

Keep public/index.php small. It coordinates bootstrapping and dispatch rather than containing validation, SQL, and page HTML for every feature.

  • The web server sends application requests to one public entry script.
  • The front controller loads configuration and Composer once.
  • A router selects a controller for the method and path.
  • The controller converts transport input into an application call.
  • A service enforces the use case and uses repositories or gateways.
  • The controller converts the result into an HTTP response.

Layer Ownership

Layer Owns Avoid
HTTP Methods, input mapping, status, headers Business rules and raw SQL
Application Use-case coordination and transaction boundary HTML and superglobals
Domain State, invariants, decisions Framework and database details
Persistence Queries and record mapping Authorization and redirects

Constructor Injection

Pass required collaborators into the constructor. This makes dependencies visible, prevents hidden global state, and lets tests replace infrastructure with a focused fake.

Inject a Repository Contract

Inject a Repository Contract
<?php
interface TaskRepository
{
    public function save(string $title): int;
}

final class CreateTask
{
    public function __construct(private TaskRepository $tasks) {}

    public function handle(string $title): int
    {
        $title = trim($title);
        if ($title === '') {
            throw new InvalidArgumentException('Task title is required.');
        }
        return $this->tasks->save($title);
    }
}

The use case knows the capability it needs but not whether PDO, memory, or another adapter implements it.

Composition Root

Create concrete dependencies in one bootstrap location and pass them inward. A dependency-injection container may automate this later, but it should not become a service locator called throughout domain code.

Wire the Use Case

Wire the Use Case
<?php
$pdo = new PDO($dsn, $user, $password, $options);
$tasks = new PdoTaskRepository($pdo);
$createTask = new CreateTask($tasks);

PdoTaskRepository and connection values belong at the application edge; CreateTask remains independent of PDO.

Architecture Scale

  • Start with clear functions and files; introduce a layer when it protects a real boundary.
  • Do not create one interface and one class for every method by reflex.
  • Keep transactions around complete use cases, not individual repository statements.
  • Test domain decisions without booting HTTP or a database; integration-test adapters against real boundaries.

Place Each Responsibility

0 of 2 checked

Q1. Where should an HTTP redirect be selected?

Q2. What is the composition root responsible for?

Try this next

Separate One Use Case

0 of 2 completed

  1. Move validation and persistence coordination from a controller into a CreateTask service with an injected repository.
  2. Map HTTP, application, domain, and PDO code and reverse any domain dependency that points toward infrastructure.
Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.