Table of Contents
18. A Simple Controller — Giving Behavior a Home
— where a request becomes an action
Once we have:
- a Router to decide where a request goes
- a Repository to load data
- Services to coordinate behavior
- Factories to create objects
…we need one more piece:
A place where a 'request'
becomes 'an action'.
That place is a controller.
Not a framework class.
Not a pattern or an architectural ceremony.
Just a small, intentional home for behavior
that responds to a request.
1. What a Controller Really Is
A controller is simply:
A function or method that
receives a request,
then performs an action and returns a response.
That’s it.
A controller is not:
a model
a service
a repository
a view
a domain object
It’s that thin layer between the outside world and our domain.
2. The Smallest Controller We Can Write
Here’s the simplest possible controller:
function about(): string { return 'About page'; }
Our router calls it:
$router->get('/about', fn() => about());
This is the shape:
- input → behavior → output
Everything else is refinement.
3. A Controller With Dependencies
As soon as we need data, the controller becomes a little more interesting:
class UserController { public function __construct( private UserRepository $users ) {} public function show(string $id): array { return $this->users->find($id); } }
Our router now reads like a story:
$router->get('/users/{id}', fn($id) => $userController->show($id));
The controller:
- receives the ID
- asks the repository for the user
- returns the result
Nothing more.
4. Controllers Should Stay Thin
A controller should not:
- validate email formats
- generate UUIDs
- send notifications
- assign tickets
- perform business rules
- talk directly to the database
- create entities
- coordinate multiple objects
All of that belongs in:
- Value Objects
- Factories
- Services
- Repositories
A controller’s job is simply to:
Translate the request
into a domain action.
Translate the result into a response.
Thin.
Predictable.
Human‑readable.
5. A Gentle Example With Behavior
Let’s say we want to register a new user.
Our controller might look like:
class RegistrationController { public function __construct( private UserFactory $factory, private UserRepository $users ) {} public function register(array $data): array { $user = $this->factory->register( $data['name'], $data['email'] ); $this->users->save($user); return ['status' => 'ok']; } }
The controller:
- receives the request data
- delegates creation to the factory
- delegates persistence to the repository
- returns a simple response
It does not do the work
— it coordinates the work.
6. Why This Matters in the AI Era
When we ask AI for a controller, it may:
- put business logic inside the controller
- talk directly to the database
- create entities inline
- skip services and factories
- mix concerns
- generate a full MVC framework
Our literacy lets us steer the conversation:
“Keep the controller thin.”
“Move creation to a factory.”
“Move persistence to a repository.”
“Use a service for this behavior.”
“Return a simple array or string.”
We’re not memorizing MVC
— we’re shaping intention.
7. When to Ask AI for a Controller
The moment when a route needs behavior, not just a string
Here’s what that moment looks like.
We may start with:
“Handle GET /users/{id}.”
AI gives us:
$router->get('/users/{id}', fn($id) => $userRepository->find($id));
Then we ask:
“Also validate the ID.”
AI adds logic.
Then we ask:
“Also log the request.”
More logic.
Then we ask:
“Also return JSON.”
Even more logic.
We feel the friction.
we feel the noise.
we feel the concept.
This is the moment to say:
“Give me a UserController with a show() method. Keep the controller thin and delegate everything else.”
AI will produce a clean controller.
This is the literacy we’re learning:
When a route needs behavior, we ask for a controller.
When the behavior grows, we move it into services.
8. The Mental Model in One Sentence
A controller is the doorway between
the outside world and our domain
— thin, intentional, and calm.
Once we see this shape,
we can collaborate with AI on any application structure
— from a tiny script to a full framework —
without losing clarity.
Tony de Araujo —New York
