Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
The Clean Architecture in PHP
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Kristopher Wilson
January 30, 2014
Technology
4.4k
23
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
The Clean Architecture in PHP
Kristopher Wilson
January 30, 2014
More Decks by Kristopher Wilson
See All by Kristopher Wilson
HHVM Makes Everything Stupid Fast
mrkrstphr
0
150
Doctrine ORM
mrkrstphr
0
250
What's new in PHP OOP?
mrkrstphr
1
330
Other Decks in Technology
See All in Technology
40代で“やっとエンジニアになれた”――閉じた学びを開き、空の青さを知る / 20260628 Naoki Takahashi
shift_evolve
PRO
4
880
クラウドファンディング版StackChan 3体(4体)をインタラクティブな体験型作品にして展示もした話 / スタックチャンお誕生日会2026
you
PRO
0
180
4人目のSREはAgent
tanimuyk
0
180
Oracle Cloud Infrastructure:2026年6月度サービス・アップデート
oracle4engineer
PRO
0
320
AIはどのように 組織のアジリティを変えるのか?
junki
4
1.4k
Agile and AI Redmine Japan 2026
hiranabe
4
480
Zenoh on Zephyr on LiteX
takasehideki
2
110
フルAIで個人開発して学んだあれこれ / yuruai vol.1
isaoshimizu
0
120
ぼっちではじめた登壇が「51名」「241件」の発信に化けた
subroh0508
1
310
作る力から、見極める力へ — AI時代に広がるエンジニアの価値と役割
rince
0
340
AWS Security Agent といっしょに脅威モデリングをやってみよう
amarelo_n24
1
210
SteampipeとExcel Power QueryでAWS構成定義書の作成を自動化する
jhashimoto
0
180
Featured
See All Featured
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
240
Color Theory Basics | Prateek | Gurzu
gurzu
0
370
Ruling the World: When Life Gets Gamed
codingconduct
0
260
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.2k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
950
AI: The stuff that nobody shows you
jnunemaker
PRO
8
730
Un-Boring Meetings
codingconduct
0
320
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
540
Technical Leadership for Architectural Decision Making
baasie
3
420
Paper Plane (Part 1)
katiecoart
PRO
0
9.2k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
740
Speed Design
sergeychernyshev
33
1.9k
Transcript
The Clean Architecture
WE GOTS PROBLEMS
WE LIVE OR DIE BY THE FRAMEWORK
SO MANY COOL LIBRARIES
WE CAN'T TEST ANYTHING
CHANGE BREAKS EVERYTHING
HOW DO WE FIX THESE PROBLEMS?
THE CLEAN ARCHITECTURE Coined by Mr. Uncle Bob
THE ONION ARCHITECTURE Coined by Jeffrey Palermo
SOFTWARE IS COMPOSED OF LAYERS
MVC is a start of those layers: Model View Controller
MVC Kinda Sucks Rails pushed the "fat model, skinny controller"
mantra
MVC Kinda Sucks With only 3 layers, this becomes the
"obese model, skinny controller"
The Solution? More layers, obviously...
DOMAIN MODEL
Domain Model is Our Entities
Domain Model is Plain Old PHP Objects
Domain Model has No dependencies except for PHP
Domain Model is Transferable We should be able to drop
the Domain Model into any PHP project and have it work as intended.
DOMAIN SERVICES
Domain Services is dependent upon the Domain Model and nothing
else.
Domain Services is Plain Old PHP Objects
Domain Services use the Domain Model layer to do things.
Domain Services together, with Domain Services, create the Business Logic
Layer.
Business Logic the rules that define what the application does
and does not
Business Logic is relationships, processes, and data workflow
Business Logic is not processing forms, routing requests or creating
csv files
Business Logic is the stuff that stays the same regardless
of language, framework or interface
Application Services are services provided by your framework.
Application Services is session management, pagination, and authentication
Application Services utilize your Business Logic Layer in the context
of your application (web based vs desktop)
Application Services depends upon the Domain Model and Domain Services
to function
User Interface is stuff the user sees
User Interface is the HTML, JavaScript, CSS, Images, etc that
make up the user experience
User Interface is also the controller that serves the request.
User Interface M VC Data UI
Infrastructure are the mechanisms that give your Domain Model meaning
Infrastructure is responsible for hydrating and persisting the Data Model
Infrastructure connects with external resources to hydrate and persist the
data
Infrastructure vs UI are different layers, but on the same
level of the Onion
Infrastructure vs UI UI (Controllers) CANNOT talk to infrastructure
Infrastructure vs UI Controllers utilizing Infrastructure directly is like supergluing
ourselves to an implementation
External Data Sources old school thought: Databases are central to
an application. Apps revolve around the DB.
External Data Sources old school thought: Databases are central to
an application. Apps revolve around the DB.
External Data Sources Databases simply provide a means of hydrating
your Domain Model
External Data Sources Your Domain Model is central to your
application. It is the core of the Onion.
External Data Sources Our application does not depend on the
database.
External Data Sources Our application depends on data.
External Data Sources Where does that data come from? We
don't care!
External Data Sources API Files Relational Databases NoSQL Variants
External Data Sources Whatever.
So how does this work?
Dependency moves inward
Inversion of Control Inversion of control is taking the control
of dependencies away from the object, and giving it to some third party who provides an object with its dependencies.
Inversion of Control Some things can't know about other things,
but we can use interfaces to inject other things into some things. LOL.
Dependency Injection Simply inject a dependency into an object that
needs it, either view construction or using set methods.
Dependency Injection Previously... class CustomerController extends AbstractController { public function
indexAction() { $repository = new CustomerRepository(new EntityManager()); $customers = $repository->getAll(); return ['customers' => $customers]; } } Controllers shouldn't know what repositories are...
Dependency Injection Instead... class CustomerController extends AbstractController { protected $customersRepository;
public function __construct(CustomerRepositoryInterface $repo) { $this->customerRepository = $repo; } public function indexAction() { $customers = $repository->getAll(); return ['customers' => $customers]; } }
Dependency Injection A class simply asks for some object conforming
to the dependent interface. We don't care what we get, as long as it does what we want.
Domain Model = Entities class Customer extends AbstractEntity { protected
$name; protected $channel; public function setName($name) { $this->name = $name; return $this; } public function getName() { return $this->name; } }
Domain Services ▪ Repositories ▪ Factories ▪ Services
Repositories ▪ Retrieve things from data sources ▪ Store things
in data sources
Repositories But Domain Services are too deep in the Onion
to know about data sources...
Repositories Arm yourself with interfaces!
Repositories Domain Services should provide a contract for the Infrastructure
Layer to adhere
Repositories interface CustomerRepositoryInterface extends RepositoryInterface { /** * @param int
$id * @return AbstractEntity */ public function getById($id); /** * @return array */ public function getAll(); }
Repositories Use Dependency Injection for components that cannot depend on
the Infrastructure Layer directly
Factories Factories create things. They know the business rules around
construction and adhere to them.
Factories As part of the Domain Services layer, they can
only depend on other Domain Services and the Domain Model
Factories class CustomerFactory { // ... public function create() {
$customer = new Customer(); $customer->setType($this->typeRepository->getByCode('R')); $customer->setCreditLimit(0); $customer->setCreditStatus($this->statusRepository->getByCode('P')); return $customer; } }
Factories Domain Services are your Business Logic Layer. They know
how to create things. Your controller doesn't know, but it does know data, and it does know the factory's phone number.
Services Services are classes that do things for you or
figure things out for you. They know about your data, and they know about your business rules.
Services Services are things like billing runs, usage statistic calculators,
permission checks, etc.
Services Works with data, but doesn't create (Factory), retrieve or
persist data (Repository).
Services namespace Uss\Domain\Services\Billing; class Billing { public function generateInvoices(\DateTime $invoiceDate)
{ $orders = $this->ordersRepository->getActiveBillingOrders($invoiceDate); foreach ($orders as $order) { $invoice = $this->invoiceFactory->create($order); $this->invoiceRepository->persist($invoice); } $this->invoiceRepository->flush(); } }
Services namespace Uss\Domain\Services\Billing; class Taxes { public function calculateTaxes(Invoice $invoice)
{ if ($this->isAccountTaxExempt($invoice->getAccount()) { return; } foreach ($invoice->getOrder()->getComponents() as $component) { $invoice->setTaxes( $this->taxRepository->getBy( [ 'type' => $component->getServiceType(), 'geocode' => $component->getLocation()->getGeocode() ] ); ); } } }
UI: Controllers ▪ Accept Data (GET, POST, PUT) ▪ Validate
Data ▪ Use the Domain Services Layer to do things ▪ Return a view, or redirect to another controller
UI: Controllers Do Not ▪ Contain business rules ▪ Access
the database ▪ Do things...
UI: Controllers Should ▪ Have a specific purpose ▪ Not
have many actions ▪ Have very short actions (methods)
UI: Controllers Should SOLID S = Single Responsibility Principle Every
class should have one, and only one, responsibility
UI: Controllers A PartnerController should only be concerned with managing
a Partner (View, Edit, Delete)
UI: Controllers If Partners have Contacts, Documents, and Deals, those
should all each be their own controllers. Single responsibility.
Small Controllers. Why?
class CustomersController extends AbstractActionController { public function __construct( AddressRepositoryInterface $addressRepository,
AgingHistoryRepositoryInterface $agingHistoryRepository, AliasRepositoryInterface $aliasRepository, SocialRepositoryInterface $socialRepository, ContactRepositoryInterface $contactRepository, CustomerRepositoryInterface $customerRepository, StatusRepositoryInterface $statusRepository, MaintSurvTicketRepositoryInterface $ticketRepository ) { $this->addressRepository = $addressRepository; $this->agingHistoryRepository = $agingHistoryRepository; $this->aliasRepository = $aliasRepository; $this->socialRepository = $socialRepository; $this->contactRepository = $contactRepository; $this->customerRepository = $customerRepository; $this->statusRepository = $statusRepository; $this->ticketRepository = $ticketRepository; } }
Have fun testing that.
Have fun refactoring that.
UI: Controllers Controllers are stupid. They should contain no logic
besides retrieving data and knowing which Domain Services to call.
UI: Controllers All business logic should go within the Domain
Services layer. All processing logic should go in the controller.
Dumb Controllers. Why? DRY. If business logic is in the
controller, we can't reuse it. Single Responsibility. A controller is only responsible for responding to requests and dispatching views.
Why are we doing all of this?
FRAMEWORK INDEPENDENCE
DATABASE INDEPENDENCE
EXTERNAL AGENCY INDEPENDENCE
USER INTERFACE INDEPENDENCE
TESTABLE