Slide 1

Slide 1 text

The Clean Architecture

Slide 2

Slide 2 text

WE GOTS PROBLEMS

Slide 3

Slide 3 text

WE LIVE OR DIE BY THE FRAMEWORK

Slide 4

Slide 4 text

SO MANY COOL LIBRARIES

Slide 5

Slide 5 text

WE CAN'T TEST ANYTHING

Slide 6

Slide 6 text

CHANGE BREAKS EVERYTHING

Slide 7

Slide 7 text

HOW DO WE FIX THESE PROBLEMS?

Slide 8

Slide 8 text

THE CLEAN ARCHITECTURE Coined by Mr. Uncle Bob

Slide 9

Slide 9 text

THE ONION ARCHITECTURE Coined by Jeffrey Palermo

Slide 10

Slide 10 text

SOFTWARE IS COMPOSED OF LAYERS

Slide 11

Slide 11 text

MVC is a start of those layers: Model View Controller

Slide 12

Slide 12 text

MVC Kinda Sucks Rails pushed the "fat model, skinny controller" mantra

Slide 13

Slide 13 text

MVC Kinda Sucks With only 3 layers, this becomes the "obese model, skinny controller"

Slide 14

Slide 14 text

The Solution? More layers, obviously...

Slide 15

Slide 15 text

DOMAIN MODEL

Slide 16

Slide 16 text

Domain Model is Our Entities

Slide 17

Slide 17 text

Domain Model is Plain Old PHP Objects

Slide 18

Slide 18 text

Domain Model has No dependencies except for PHP

Slide 19

Slide 19 text

Domain Model is Transferable We should be able to drop the Domain Model into any PHP project and have it work as intended.

Slide 20

Slide 20 text

DOMAIN SERVICES

Slide 21

Slide 21 text

Domain Services is dependent upon the Domain Model and nothing else.

Slide 22

Slide 22 text

Domain Services is Plain Old PHP Objects

Slide 23

Slide 23 text

Domain Services use the Domain Model layer to do things.

Slide 24

Slide 24 text

Domain Services together, with Domain Services, create the Business Logic Layer.

Slide 25

Slide 25 text

Business Logic the rules that define what the application does and does not

Slide 26

Slide 26 text

Business Logic is relationships, processes, and data workflow

Slide 27

Slide 27 text

Business Logic is not processing forms, routing requests or creating csv files

Slide 28

Slide 28 text

Business Logic is the stuff that stays the same regardless of language, framework or interface

Slide 29

Slide 29 text

Application Services are services provided by your framework.

Slide 30

Slide 30 text

Application Services is session management, pagination, and authentication

Slide 31

Slide 31 text

Application Services utilize your Business Logic Layer in the context of your application (web based vs desktop)

Slide 32

Slide 32 text

Application Services depends upon the Domain Model and Domain Services to function

Slide 33

Slide 33 text

User Interface is stuff the user sees

Slide 34

Slide 34 text

User Interface is the HTML, JavaScript, CSS, Images, etc that make up the user experience

Slide 35

Slide 35 text

User Interface is also the controller that serves the request.

Slide 36

Slide 36 text

User Interface M VC Data UI

Slide 37

Slide 37 text

Infrastructure are the mechanisms that give your Domain Model meaning

Slide 38

Slide 38 text

Infrastructure is responsible for hydrating and persisting the Data Model

Slide 39

Slide 39 text

Infrastructure connects with external resources to hydrate and persist the data

Slide 40

Slide 40 text

Infrastructure vs UI are different layers, but on the same level of the Onion

Slide 41

Slide 41 text

Infrastructure vs UI UI (Controllers) CANNOT talk to infrastructure

Slide 42

Slide 42 text

Infrastructure vs UI Controllers utilizing Infrastructure directly is like supergluing ourselves to an implementation

Slide 43

Slide 43 text

External Data Sources old school thought: Databases are central to an application. Apps revolve around the DB.

Slide 44

Slide 44 text

External Data Sources old school thought: Databases are central to an application. Apps revolve around the DB.

Slide 45

Slide 45 text

External Data Sources Databases simply provide a means of hydrating your Domain Model

Slide 46

Slide 46 text

External Data Sources Your Domain Model is central to your application. It is the core of the Onion.

Slide 47

Slide 47 text

External Data Sources Our application does not depend on the database.

Slide 48

Slide 48 text

External Data Sources Our application depends on data.

Slide 49

Slide 49 text

External Data Sources Where does that data come from? We don't care!

Slide 50

Slide 50 text

External Data Sources API Files Relational Databases NoSQL Variants

Slide 51

Slide 51 text

External Data Sources Whatever.

Slide 52

Slide 52 text

So how does this work?

Slide 53

Slide 53 text

Dependency moves inward

Slide 54

Slide 54 text

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.

Slide 55

Slide 55 text

Inversion of Control Some things can't know about other things, but we can use interfaces to inject other things into some things. LOL.

Slide 56

Slide 56 text

Dependency Injection Simply inject a dependency into an object that needs it, either view construction or using set methods.

Slide 57

Slide 57 text

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...

Slide 58

Slide 58 text

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]; } }

Slide 59

Slide 59 text

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.

Slide 60

Slide 60 text

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; } }

Slide 61

Slide 61 text

Domain Services ■ Repositories ■ Factories ■ Services

Slide 62

Slide 62 text

Repositories ■ Retrieve things from data sources ■ Store things in data sources

Slide 63

Slide 63 text

Repositories But Domain Services are too deep in the Onion to know about data sources...

Slide 64

Slide 64 text

Repositories Arm yourself with interfaces!

Slide 65

Slide 65 text

Repositories Domain Services should provide a contract for the Infrastructure Layer to adhere

Slide 66

Slide 66 text

Repositories interface CustomerRepositoryInterface extends RepositoryInterface { /** * @param int $id * @return AbstractEntity */ public function getById($id); /** * @return array */ public function getAll(); }

Slide 67

Slide 67 text

Repositories Use Dependency Injection for components that cannot depend on the Infrastructure Layer directly

Slide 68

Slide 68 text

Factories Factories create things. They know the business rules around construction and adhere to them.

Slide 69

Slide 69 text

Factories As part of the Domain Services layer, they can only depend on other Domain Services and the Domain Model

Slide 70

Slide 70 text

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; } }

Slide 71

Slide 71 text

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.

Slide 72

Slide 72 text

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.

Slide 73

Slide 73 text

Services Services are things like billing runs, usage statistic calculators, permission checks, etc.

Slide 74

Slide 74 text

Services Works with data, but doesn't create (Factory), retrieve or persist data (Repository).

Slide 75

Slide 75 text

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(); } }

Slide 76

Slide 76 text

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() ] ); ); } } }

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

UI: Controllers Do Not ■ Contain business rules ■ Access the database ■ Do things...

Slide 79

Slide 79 text

UI: Controllers Should ■ Have a specific purpose ■ Not have many actions ■ Have very short actions (methods)

Slide 80

Slide 80 text

UI: Controllers Should SOLID S = Single Responsibility Principle Every class should have one, and only one, responsibility

Slide 81

Slide 81 text

UI: Controllers A PartnerController should only be concerned with managing a Partner (View, Edit, Delete)

Slide 82

Slide 82 text

UI: Controllers If Partners have Contacts, Documents, and Deals, those should all each be their own controllers. Single responsibility.

Slide 83

Slide 83 text

Small Controllers. Why?

Slide 84

Slide 84 text

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; } }

Slide 85

Slide 85 text

Have fun testing that.

Slide 86

Slide 86 text

Have fun refactoring that.

Slide 87

Slide 87 text

UI: Controllers Controllers are stupid. They should contain no logic besides retrieving data and knowing which Domain Services to call.

Slide 88

Slide 88 text

UI: Controllers All business logic should go within the Domain Services layer. All processing logic should go in the controller.

Slide 89

Slide 89 text

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.

Slide 90

Slide 90 text

Why are we doing all of this?

Slide 91

Slide 91 text

FRAMEWORK INDEPENDENCE

Slide 92

Slide 92 text

DATABASE INDEPENDENCE

Slide 93

Slide 93 text

EXTERNAL AGENCY INDEPENDENCE

Slide 94

Slide 94 text

USER INTERFACE INDEPENDENCE

Slide 95

Slide 95 text

TESTABLE