Is your code highly coupled to delivery mechanisms?

About me

Luciano Queiroz

  • Recife / PE
     
  • PHP Software Engineer ~7 years
     
  • Work remotely ~4 years
     
  • Motivations
     
  • Community

Delivery Mechanisms

  • Web application
     
  • Console application
     
  • Database
     
  • ORM
     
  • SoA
     
  • Messages Queue

Delivery Mechanisms

Starting a new project

Some common steps

  • Web Framework
     
  • Set a database
     
  • Create Schema / Migration
     
  • Create Model
     
  • Create Controller
     
  • Create View

success! :)

"Businesses regularly put too much effort into developing glorified database table editors."

Vaughn Vernon, Implementing Domain Driven Design (Perason Education, Inc: 2013), 172.

View

Controller

Model

Database

Data

Data

Data

Anemic Models

Memory Loss

<?php
App::uses('AppController', 'Controller');

class CustomersController extends AppController {

	public function add() {
		if ($this->request->is('post')) {
			$this->Customer->create();
			if ($this->Customer->save($this->request->data)) {
				$this->Flash->success(__('The customer has been saved.'));
				return $this->redirect(array('action' => 'index'));
			} else {
				$this->Flash->error(__('The customer could not be saved. Please, try again.'));
			}
		}
	}

	public function edit($id = null) {
		if (!$this->Customer->exists($id)) {
			throw new NotFoundException(__('Invalid customer'));
		}
		if ($this->request->is(array('post', 'put'))) {
			if ($this->Customer->save($this->request->data)) {
				$this->Flash->success(__('The customer has been saved.'));
				return $this->redirect(array('action' => 'index'));
			} else {
				$this->Flash->error(__('The customer could not be saved. Please, try again.'));
			}
		} else {
			$options = array('conditions' => array('Customer.' . $this->Customer->primaryKey => $id));
			$this->request->data = $this->Customer->find('first', $options);
		}
	}
}

Coupling to Controller and ORM 

<?php
App::uses('AppController', 'Controller');

class CustomersController extends AppController {

	public function add() {
		if ($this->request->is('post')) {
			$this->Customer->create();
			if ($this->Customer->save($this->request->data)) {
				$this->Flash->success(__('The customer has been saved.'));
				return $this->redirect(array('action' => 'index'));
			} else {
				$this->Flash->error(__('The customer could not be saved. Please, try again.'));
			}
		}
	}

	public function edit($id = null) {
		if (!$this->Customer->exists($id)) {
			throw new NotFoundException(__('Invalid customer'));
		}
		if ($this->request->is(array('post', 'put'))) {
			if ($this->Customer->save($this->request->data)) {
				$this->Flash->success(__('The customer has been saved.'));
				return $this->redirect(array('action' => 'index'));
			} else {
				$this->Flash->error(__('The customer could not be saved. Please, try again.'));
			}
		} else {
			$options = array('conditions' => array('Customer.' . $this->Customer->primaryKey => $id));
			$this->request->data = $this->Customer->find('first', $options);
		}
	}
}

Problems found

  • Difficult/slow to test
     
  • Reusability almost impossible
     
  • It only works in web environment
     
  • Difficult to run multiple times
     
  • Dependency on concretions and not abstractions

Screaming Architecture

"So what does the architecture of your application scream? When you look at the top level directory structure, and the source files in the highest level package; do they scream: Health Care System, or Accounting System, or Inventory Management System? Or do they scream: Rails, or Spring/Hibernate, or ASP?"

Uncle Bob

Layered Architecture

"Develop a design within each layer that is cohesive and that depends only on the layers below."

[Evans, Ref, p.16]

User Interface

Application Layer

Layered Architecture

Infrastructure

Domain Layer

Reorganizing

Application Layer

Domain Layer

Layered Architecture

Infrastructure

Dependency Inversion Principle

Depend on abstractions, not on concretions.

Application Layer

Domain Layer

Layered Architecture

Infrastructure

Ain't no layers anymore!!!

Layered Architecture

Application

Domain

Infrastructure

Layered Architecture with DIP

Domain

Entity

Value Object

Application

Infrastructure

Why frameworks can't help?

Hexagonal

"Create your application to work without either a UI or a database so you can run automated regression-tests against the application, work when the database becomes unavailable, and link applications together without any user involvement"

Alistair Cockburn.

Hexagonal ):
Octogonal

Domain

Application

Infrastructure

Http

Persistence

AMPQ

Console

Http Port / Adapter

Http

Request

Command

Handler

ServiceBus

Entities

Value Objects

Http Land

Domain Land

Command

<?php

class RegisterCustomer {
    
    private $name;
    private $email;
    private $username;
    private $customer;
    
    public function __construct(
        $name, $email, $username
    )
    {
            $customer = Customer::fromPersonAndUser(
            Person::fromName($name),
            User::fromUsernameAndEmail(
                Username::fromString($username), 
                Email::fromString($email)
            )
            
            $this->$customer = $customer;
        )
    }

    public function customer()
    {
        return $this->customer;
    }
}

CommandHandler

<?php

class RegisterCustomerHandler {

    private $customerRepository;
    
    public function __construct(
        ICustomerRepository $repository
    ) 
    {
        $this->customerRepository = $repository;
    }
    
    public function handle(
        RegisterCustomer $registerCustomer
    )
    {   
        $customer = $registerCustomer->customer();
        return $this->repository->store($customer);
    }
}

Adapter / Persistence Port 

MySqlRepository

MongoDB

MySQL

Domain Land

Persistence Land

MongoDBRepository

InMemoryRepository

CacheRepository

Redis

IRepository

Handler

ICustomerRepository

<?php

interface ICostumerRepository {
    public function store();
    public function ofId($id);
}

MySqlCustomerRepository

<?php

class MySqlCustomerRepository implements ICustomerRepository {
    private $entityRepository;
    public function __construct(EntityRepository $entityRepository)
    {
        $this->entityRepository = $entityRepository;
    }
    
    public function store(Customer $customer)
    {
        $this->entityRepository->em->persist($customer);
        $this->entityRepository->em->flush();
    }
    public function ofId(CustomerId $customerId)
    {
        return $this->entityRepository->findBy(['id' => $customerId]);
    }
}

Why Hexagonal?

Advantages using Hexagonal

  • Faster tests
     
  • Code first
     
  • Focus on Use-Cases
     
  • Decoupled Domain Model
     
  • Bounded Context

References

  • Implementing Domain Driven Design - Vaughn Vernon.
     

  • Domain-Driven Design: Tackling Complexity in the Heart of Software - Eric Evans.
     

  • Principles of Package Design - Mathias Noback.
     

  • Clean Code Episode VII - Architecture, Use Cases, and High Level Design - Uncle Bob.
     

  • http://alistair.cockburn.us/Hexagonal+architecture

Contacts

  • @luciianoqueiroz
     

  • luciiano.queiroz@gmail.com

Questions?

Thanks! :)

Is your code highly coupled to delivery mechanisms?

By Luciano Queiroz

Is your code highly coupled to delivery mechanisms?

Slide para o MeetUp do PHP Pernambuco

  • 854