modernizando a arquitertura
de sua aplicação
Antonio Spinelli @tonicospinelli
tornando o seu código mais robusto e escalável
problema simples
vs
soluções complexas
soluções simples
o objetivo não muda:
resolver problemas para o negócio!
entenda o problema
agregue valor ao negócio
agilidade não é "sobrinhagem"
desenvolvedor
desenvolvedor
automatize tarefas repetidas
peça e faça code review
leia código
Lista de Desejos para Produtos
Eu como um usuário convidado, quero adicionar um produto
indisponível dentro da minha lista de desejos, de modo que eu
receba uma notificação quando este estiver disponível.
Objetivo
reposição de estoque inteligente e
estreitar relacionamento com cliente
user story
php puro
adicionando um item na lista de desejos
php puro
// public/wishlist.php
// ...
if (isset($_POST['submit']) && isValid($_POST['wish_item'])) {
}
// ...
php puro
// public/wishlist.php
// ...
if (isset($_POST['submit']) && isValid($_POST['wish_item'])) {
$wishItem = createWishListItem($_POST['wish_item']);
}
// ...
php puro
// public/wishlist.php
// ...
if (isset($_POST['submit']) && isValid($_POST['wish_item'])) {
$wishItem = createWishListItem($_POST['wish_item']);
$stm = $db->prepare('insert new entry (email, product_id)');
$stm->execute([$wishItem['email'], $wishItem['product_id']]);
$successmsg = 'Product was added at wish list successfully!';
}
// ...
php puro
// public/wishlist.php
// ...
if (isset($_POST['submit']) && isValid($_POST['wish_item'])) {
$wishItem = createWishListItem($_POST['wish_item']);
$db->beginTransaction();
try {
$stm = $db->prepare('insert new entry (email, product_id)');
$stm->execute([$wishItem['email'], $wishItem['product_id']]);
$successmsg = 'Product was added at wish list successfully!';
$db->commit();
} catch (Exception $e) {
$db->rollBack();
$errormsg = 'Product could not be added at wishlist! :(';
}
}
// ..
php puro
// public/wishlist.php
// ...
<table>
<?php foreach ($wishlist as $item): ?>
<tr>
<td><?php echo $item['id']; ?> </td>
<td><?php echo $item['product_name']; ?> </td>
<?php if ($item['product_stock'] == 0): ?>
<td>Not Available</td>
<?php else: ?>
<td>Available</td>
<?php endif; ?>
<td>
<?php echo removeUrl($item['id'], ['email' => $email]); ?>
</td>
</tr>
<?php endforeach; ?>
</table>
// ...
php puro
notificar usuário quando
o item estiver disponível
php puro
// cli/wishlist_notify.php
// ...
$query = "find all available products from wishlist";
$stm = $db->prepare($query);
$stm->execute();
$wishlists = $stm->fetchAll(PDO::FETCH_ASSOC);
php puro
// cli/wishlist_notify.php
// ...
$query = "find all available products from wishlist";
$stm = $db->prepare($query);
$stm->execute();
$wishlists = $stm->fetchAll(PDO::FETCH_ASSOC);
foreach ($wishlists as $item) {
mail(
$item['email'],
"{$item['product_name']} available",
$mailBody
);
$stm = $db->prepare("define new status for id = ?");
$stm->execute([$item['id']]);
}
php puro
pronto!
funciona!
php puro
quais problemas temos com este código?
atenção
organização pobre
sem verificação de erros
dificuldade para reutilizar o código
php puro
php estruturado
isolando a apresentação
php estruturado
// templates/wishlist/list.php
<table>
<?php foreach ($wishlist as $item): ?>
<tr>
<td><?php echo $item['id']; ?> </td>
<td><?php echo $item['product_name']; ?> </td>
<?php if ($item['product_stock'] == 0): ?>
<td>Not Available</td>
<?php else: ?>
<td>Available</td>
<?php endif; ?>
<td>
<?php echo removeUrl($item['id'], ['email' => $email]); ?>
</td>
</tr>
<?php endforeach; ?>
</table>
php estruturado
isolando regra de negócio
php estruturado
// lib/models/wishlist.php
function addItemIntoWishlist(array $data);
function createWishListItem(array $data);
function findAllWishlistsToNotify();
function findAllWishProducts($email);
function isValidWishList(array $data);
function removeWishItem($id);
function wishlistNotified($id);
php estruturado
interagindo
regras de negócio com a apresentação
php estruturado
// public/wishlist.php
require_once __DIR__ . '/../lib/dbconn.php';
require_once __DIR__ . '/../lib/functions.php';
require_once __DIR__ . '/../lib/wishlist.php';
if (isset($_POST['submit']) && isValidWishList($_POST['wish_item'])) {
$wishItem = createWishListItem($_POST['wish_item']);
$db->beginTransaction();
try {
$stm = $db->prepare('insert new entry (email, product_id)');
$stm->execute([$wishItem['email'], $wishItem['product_id']]);
$db->commit();
$successmsg = 'Product was added at wish list successfully!';
} catch (Exception $e) {
$db->rollBack();
$errormsg = 'Product could not be added at wishlist! :(';
}
}
// ...
php estruturado
// public/wishlist.php
require_once __DIR__ . '/../lib/dbconn.php';
require_once __DIR__ . '/../lib/functions.php';
require_once __DIR__ . '/../lib/wishlist.php';
if (isset($_POST['submit']) && isValidWishList($_POST['wish_item'])) {
$wishItem = createWishListItem($_POST['wish_item']);
$db->beginTransaction();
try {
$stm = $db->prepare('insert new entry (email, product_id)');
$stm->execute([$wishItem['email'], $wishItem['product_id']]);
$db->commit();
$successmsg = 'Product was added at wish list successfully!';
} catch (Exception $e) {
$db->rollBack();
$errormsg = 'Product could not be added at wishlist! :(';
}
}
// ...
structured php
// public/wishlist.php
require_once __DIR__ . '/../lib/dbconn.php';
require_once __DIR__ . '/../lib/functions.php';
require_once __DIR__ . '/../lib/wishlist.php';
if (isset($_POST['submit']) && isValidWishList($_POST['wish_item'])) {
$wishItem = createWishListItem($_POST['wish_item']);
if (addItemIntoWishlist($wishItem)) {
$successmsg = 'Product was added at wish list successfully!';
} else {
$errormsg = 'Product could not be added at wishlist! :(';
}
}
// ...
php estruturado
// public/wishlist.php
require_once __DIR__ . '/../lib/dbconn.php';
require_once __DIR__ . '/../lib/functions.php';
require_once __DIR__ . '/../lib/wishlist.php';
if (isset($_POST['submit']) && isValidWishList($_POST['wish_item'])) {
$wishItem = createWishListItem($_POST['wish_item']);
if (addItemIntoWishlist($wishItem)) {
$successmsg = 'Product was added at wish list successfully!';
} else {
$errormsg = 'Product could not be added at wishlist! :(';
}
}
$wishlist = findAllWishProducts($_GET['email']);
include __DIR__ . '/../templates/wishlists/list.php';
php estruturado
isolando layout
php estruturado
// template/layout.php
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title ?></title>
</head>
<body>
<?php echo $content ?>
</body>
</html>
layout padrão
php estruturado
// template/wishlist/list.php
<?php $title = 'My Wish List' ?>
define o título da página
php estruturado
// template/wishlist/list.php
<?php $title = 'My Wish List' ?>
<?php ob_start() ?>
<table>
<tbody>
<?php foreach ($wishlist as $wish): ?>
<tr><!-- print all columns --></tr>
<?php endforeach; ?>
</tbody>
</table>
<?php $content = ob_get_clean() ?>
define o conteúdo da página
php estruturado
// template/wishlist/list.php
<?php $title = 'My Wish List' ?>
<?php ob_start() ?>
<table>
<tbody>
<?php foreach ($wishlist as $wish): ?>
<tr><!-- print all columns --></tr>
<?php endforeach; ?>
</tbody>
</table>
<?php $content = ob_get_clean() ?>
<?php include __DIR__ . '/../layout.php' ?>
extendendo layout padrão
php estruturado
front controller
php estruturado
sem um front controller
GET /wishlist.php?email=email@test.com
com index.php como um front controller
GET /index.php/wishlist/email@test.com
com regras de sobrescrita no servidor web
GET /wishlist/email@test.com
php estruturado
// public/index.php
// route the request internally
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
if (preg_match('/wishlist/(.*)$', $uri, $matches) && $method == 'GET') {
wishlistListAction($matches[1]);
} elseif ('/index.php/wishlist/add' === $uri && $method == 'POST') {
wishlistAddAction($_GET['email'], $_POST);
} else {
header('HTTP/1.1 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
php estruturado
// public/index.php
// route the request internally
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
if (preg_match('/wishlist/(.*)$', $uri, $matches) && $method == 'GET') {
wishlistListAction($matches[1]);
} elseif ('/index.php/wishlist/add' === $uri && $method == 'POST') {
wishlistAddAction($_GET['email'], $_POST);
} else {
header('HTTP/1.1 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
php estruturado
// public/index.php
// route the request internally
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
if (preg_match('/wishlist/(.*)$', $uri, $matches) && $method == 'GET') {
wishlistListAction($matches[1]);
} elseif ('/index.php/wishlist/add' === $uri && $method == 'POST') {
wishlistAddAction($_GET['email'], $_POST);
} else {
header('HTTP/1.1 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
php estruturado
// public/index.php
// route the request internally
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
if (preg_match('/wishlist/(.*)$', $uri, $matches) && $method == 'GET') {
wishlistListAction($matches[1]);
} elseif ('/index.php/wishlist/add' === $uri && $method == 'POST') {
wishlistAddAction($_GET['email'], $_POST);
} else {
header('HTTP/1.1 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
php estruturado
autoload
php estruturado
// public/index.php
require_once __DIR__ . '/../config/app.php';
require_once LIBRARY_DIR . '/functions.php';
require_once LIBRARY_DIR . '/wishlist.php';
// include_path is other alternative
ini_set('include_path', __DIR__ . '/../lib');
php estruturado
autoload
php estruturado
{
"name": "tonicospinelli/developing-for-business",
"description": "A Quick Project for Talk",
"license": "MIT",
"autoload": {
"files": [
"./lib/dbconn.php",
"./lib/functions.php",
"./lib/controllers/wishlist.php",
"./lib/models/wishlist.php"
]
}
}
composer
php estruturado
$ composer dump-autoload
// public/index.php
require_once __DIR__ . '/../vendor/autoload.php';
php estruturado
autoload
developing-for-business
├── cli
│ └── wishlist_notification.php
├── composer.json
├── config
│ └── app.php
├── lib
│ ├── controllers
│ │ └── wishlist.php
│ ├── functions.php
│ └── models
│ └── wishlist.php
├── public
│ └── index.php
├── templates
│ └── wishlists
└── vendor
estrutura do projeto
php estruturado
php orientado a objetos
código auto explicativo
facilidade no crescimento
testes automatizados
php orientado a objetos
testes automatizados
php orientado a objetos
garantem regras conhecidas
auxiliam na refatoração
não evitam bugs
testes automatizados
php orientado a objetos
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.
................................................... 59 / 59 (100%)
Time: 349 ms, Memory: 6.00MB
OK (59 tests, 126 assertions)
testes automatizados
php orientado a objetos
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.
..................................F................ 59 / 59 (100%)
Time: 374 ms, Memory: 6.00MB
There was 1 failure:
1) ProductTest::testUpdateProductFailed
Failed asserting that exception of type "ProductException" is
thrown.
FAILURES!
Tests: 59, Assertions: 125, Failures: 1.
testes automatizados
php orientado a objetos
não confie em arrays
evite abreviações
php orientado a objetos
/**
* Creates a new wish list data.
* @param array $data
* @return array
*/
function newWishList(array $data)
{
return array(
'email' => isset($data['email']) ? $data['email'] : null),
'product_id' => $data['product_id'],
'status' => 'P',
);
}
php orientado a objetos
objetos de valor
php orientado a objetos
objetos simples
encapsulam tipos primitivos
representam o valor
objetos de valor
php orientado a objetos
class Status
{
const PENDING = 'P';
const SENT = 'S';
private $status;
public function __construct($status)
{
$this->validate($status);
$this->status = $status;
}
// … other methods
public function equalsTo(Status $status)
{
return $status === $this;
}
}
objetos de valor
php orientado a objetos
entidades
php orientado a objetos
são mutáveis
tem identificador
entidades
php orientado a objetos
class Wishlist
{
public function __construct(
Id $id,
Email email,
WishlistItem $item,
Status $status
) {
$this->id = $id;
$this->email = $email;
$this->item = $item;
$this->status = $status;
}
}
entidades
php orientado a objetos
class Wishlist
{
public function __construct(
Id $id,
Email email,
WishlistItem $item,
Status $status
) {
$this->id = $id;
$this->email = $email;
$this->item = $item;
$this->status = $status;
}
}
por que um
objeto Item?
entidades
php orientado a objetos
class Wishlist
{
public function __construct(
Id $id,
Email email,
WishlistItem $item,
Status $status
) {
$this->id = $id;
$this->email = $email;
$this->item = $item;
$this->status = $status;
}
}
por que um
objeto Item?
Produtos
Presente
entidades
php orientado a objetos
interface WishlistItem
{
public function getId();
}
class WishlistProduct implements WishlistItem
{
public function __construct(
Id $id,
$name,
Available $availability
) {
$this->id = $id;
$this->name = $name;
$this->available = $availability;
}
}
entidades
php orientado a objetos
repositórios
php orientado a objetos
camada de persistência
estratégia de armazenamento
repositórios
php orientado a objetos
interface WishlistRepository
{
public function add(Wishlist $wishlist);
public function delete(Wishlist $wishlist);
public function find($id);
public function findAllByEmail($email);
public function findAllToNotify();
}
repositórios
php orientado a objetos
namespace DevelopBusinessApplicationProductWishlistRepositories;
class PdoRepository implements WishlistRepository
{
public function __construct(PDO $driver, Factory $factory);
public function findAllByEmail($email)
{
$query = "find all items by email";
$stm = $this->driver->prepare($query);
$stm->execute([$email]);
return $stm->fetchAll(
PDO::FETCH_FUNC, [$this->factory, 'create']
);
}
}
repositórios
php orientado a objetos
namespace DevelopBusinessApplicationProductWishlistRepositories;
class RedisRepository implements WishlistRepository
{
public function __construct(Predis $driver, Factory $factory);
public function find($id)
{
$wishlist = $this->driver->get($this->getKey($id));
if (!$wishlist) {
throw WishlistNotFoundException::byIdentifier($id);
}
return $this->factory->fromJson($wishlist);
}
}
repositórios
php orientado a objetos
class PdoRepositoryTest extends PHPUnit_Framework_TestCase
{
public function testAddNewRowSuccessful()
{
$wishlist = $this->factory->fromQueryResult(/* args */);
$pdoSpy = new PDOSpy();
(new Repository($pdoSpy))->add($wishlist);
$this->assertTrue($pdoSpy->beginTransactionCalled);
$this->assertTrue($pdoSpy->commitCalled);
$this->assertEquals(2, $wishlist->getId());
}
}
como testar repositórios?
php orientado a objetos
class PDOSpy extends PDO
{
public function beginTransaction()
{
$this->beginTransactionCalled = true;
}
public function prepare($statement, $options = null)
{
if ($statement == INSERT INTO wishlists VALUES (?, ?, ?)') {
return new WriteStatementStub($this->failureOnWrite);
}
throw new InvalidArgumentException(
"None Stub Statement was found for query: {$statement}"
);
}
}
como testar repositórios?
php orientado a objetos
function wishlistListAction($email)
{
$db = dbConnect();
$factory = new WishlistFactory();
$repository = new WishlistPdoRepository($db, $factory);
$wishlist = $repository->findAllByEmail($email);
include TEMPLATE_DIR . '/wishlists/list.php';
}
repositórios
php orientado a objetos
serviços
php orientado a objetos
camada de operação
orquestra os objetos
define o que fazer
serviços
php orientado a objetos
adicionando um item na lista de desejos
serviços
php orientado a objetos
$db = dbConnect();
$factory = new WishlistFactory();
$repository = new WishlistRepository($db, $factory);
$resolver = new ItemResolver(new ProductRepository($db));
try {
$intention = createIntention($request['wish_item']);
$useCase = new AddItemWishlistUseCase(
$repository, $factory, $resolver
);
$wishlist = $useCase->execute($intention);
$successmsg = "{$wishlist->getItemName()} added!";
} catch (DomainException $e) {
$errormsg = $e->getMessage();
}
resolvendo
dependências
adicionando um item na lista de desejos
php orientado a objetos
$db = dbConnect();
$factory = new WishlistFactory();
$repository = new WishlistRepository($db, $factory);
$resolver = new ItemResolver(new ProductRepository($db));
try {
$intention = createIntention($request['wish_item']);
$useCase = new AddItemWishlistUseCase(
$repository, $factory, $resolver
);
$wishlist = $useCase->execute($intention);
$successmsg = "{$wishlist->getItemName()} added!";
} catch (DomainException $e) {
$errormsg = $e->getMessage();
}
parsing da
requisição
adicionando um item na lista de desejos
php orientado a objetos
$db = dbConnect();
$factory = new WishlistFactory();
$repository = new WishlistRepository($db, $factory);
$resolver = new ItemResolver(new ProductRepository($db));
try {
$intention = createIntention($request['wish_item']);
$useCase = new AddItemWishlistUseCase(
$repository, $factory, $resolver
);
$wishlist = $useCase->execute($intention);
$successmsg = "{$wishlist->getItemName()} added!";
} catch (DomainException $e) {
$errormsg = $e->getMessage();
}
criando
objeto de serviço
adicionando um item na lista de desejos
php orientado a objetos
$db = dbConnect();
$factory = new WishlistFactory();
$repository = new WishlistRepository($db, $factory);
$resolver = new ItemResolver(new ProductRepository($db));
try {
$intention = createIntention($request['wish_item']);
$useCase = new AddItemWishlistUseCase(
$repository, $factory, $resolver
);
$wishlist = $useCase->execute($intention);
$successmsg = "{$wishlist->getItemName()} added!";
} catch (DomainException $e) {
$errormsg = $e->getMessage();
}
executando
o serviço
adicionando um item na lista de desejos
php orientado a objetos
$db = dbConnect();
$factory = new WishlistFactory();
$repository = new WishlistRepository($db, $factory);
$resolver = new ItemResolver(new ProductRepository($db));
try {
$intention = createIntention($request['wish_item']);
$useCase = new AddItemWishlistUseCase(
$repository, $factory, $resolver
);
$wishlist = $useCase->execute($intention);
$successmsg = "{$wishlist->getItemName()} added!";
} catch (DomainException $e) {
$errormsg = $e->getMessage();
}
mensagem de
negócio
adicionando um item na lista de desejos
php orientado a objetos
class AddItemWishlistUseCase
{
public function __construct(
WishlistRepository $repository,
WishlistFactory $factory,
ItemResolver $resolver
) {
// do something
}
}
como adicionar um item na lista de desejos
php orientado a objetos
class AddItemWishlistUseCase
{
public function execute(AddItemIntention $intention)
{
$item = $this->resolver->resolve($intention->itemId);
try {
$wishlist = $this->repository->findOneByEmailAndItem(
$email, $item
);
throw WishlistException::itemAlreadyExists($wishlist);
} catch (WishlistNotFoundException $e) {
// do nothing
}
$wishlist = $this->factory->new($email, $item);
return $this->repository->add($wishlist);
}
}
php orientado a objetos
como adicionar um item na lista de desejos
class AddItemWishlistUseCase
{
public function execute(AddItemIntention $intention)
{
$item = $this->resolver->resolve($intention->itemId);
try {
$wishlist = $this->repository->findOneByEmailAndItem(
$email, $item
);
throw WishlistException::itemAlreadyExists($wishlist);
} catch (WishlistNotFoundException $e) {
// do nothing
}
$wishlist = $this->factory->new($email, $item);
return $this->repository->add($wishlist);
}
}
php orientado a objetos
como adicionar um item na lista de desejos
class AddItemWishlistUseCase
{
public function execute(AddItemIntention $intention)
{
$item = $this->resolver->resolve($intention->itemId);
try {
$wishlist = $this->repository->findOneByEmailAndItem(
$email, $item
);
throw WishlistException::itemAlreadyExists($wishlist);
} catch (WishlistNotFoundException $e) {
// do nothing
}
$wishlist = $this->factory->new($email, $item);
return $this->repository->add($wishlist);
}
}
php orientado a objetos
como adicionar um item na lista de desejos
serviços
notificar usuário quando
um item estiver disponível
php orientado a objetos
// cli/wishlist_notify.php
require_once __DIR__ . '/../config/app.php';
require_once __DIR__ . '/../vendor/autoload.php';
$factory = new WishlistFactory();
$repository = new WishlistRepository(dbConnect(), $factory);
$notifier = new MailNotifier();
$useCase = (new NotifyItemsUseCase($repository, $notifier);
$useCase->execute(new NotifyItemsIntention(Status::PENDING));
notificar quando um item disponível
resolving
dependencies
php orientado a objetos
// cli/wishlist_notify.php
require_once __DIR__ . '/../config/app.php';
require_once __DIR__ . '/../vendor/autoload.php';
$factory = new WishlistFactory();
$repository = new WishlistRepository(dbConnect(), $factory);
$notifier = new MailNotifier();
$useCase = (new NotifyItemsUseCase($repository, $notifier);
$useCase->execute(new NotifyItemsIntention(Status::PENDING));
call service
notificar quando um item disponível
php orientado a objetos
class NotifyItemsUseCase
{
public function execute(NotifyItemsIntention $intention)
{
$status = $intention->getStatus();
$wishlists = $this->repository->findAllByStatus($status);
foreach ($wishlists as $wishlist) {
$this->notifier()->send($wishlist);
$wishlist->changeStatusTo(Status::sent());
$this->repository->update($wishlist);
}
}
}
como notificar quando um item disponível?
php orientado a objetos
class NotifyItemsUseCase
{
public function execute(NotifyItemsIntention $intention)
{
$status = $intention->getStatus();
$wishlists = $this->repository->findAllByStatus($status);
foreach ($wishlists as $wishlist) {
$this->notifier()->send($wishlist);
$wishlist->changeStatusTo(Status::sent());
$this->repository->update($wishlist);
}
}
}
como notificar quando um item disponível?
php orientado a objetos
class NotifyItemsUseCase
{
public function execute(NotifyItemsIntention $intention)
{
$status = $intention->getStatus();
$wishlists = $this->repository->findAllByStatus($status);
foreach ($wishlists as $wishlist) {
$this->notifier()->send($wishlist);
$wishlist->changeStatusTo(Status::sent());
$this->repository->update($wishlist);
}
}
}
como notificar quando um item disponível?
php orientado a objetos
injeção de dependência
php orientado a objetos
princípio da inversão de dependência
dependências configuráveis
injeção de dependência
php orientado a objetos
Respect/Config
injeção de dependência
php orientado a objetos
// config/app.ini
[databaseConnection PDO]
__construct = ["sqlite:../data/business.db", null, null]
[factory WishlistFactory]
[repository WishlistRepository]
__construct = [[databaseConnection], [factory]]
[productRepository ProductRepository]
driver = [databaseConnection]
[itemResolver WishlistItemResolver]
product = [productRepository]
injeção de dependência
php orientado a objetos
// public/index.php
require_once __DIR__ . '/../config/app.php';
require_once __DIR__ . '/../vendor/autoload.php';
$configFile = __DIR__ . '/../config/app.ini';
$container = new RespectConfigContainer($configFile);
if ('/index.php/wishlist/add' === $uri /* other validations */) {
wishlistAddAction($container, $_GET['email'], $_POST);
}
injeção de dependência
php orientado a objetos
// public/index.php
require_once __DIR__ . '/../config/app.php';
require_once __DIR__ . '/../vendor/autoload.php';
$configFile = __DIR__ . '/../config/app.ini';
$container = new RespectConfigContainer($configFile);
if ('/index.php/wishlist/add' === $uri /* other validations */) {
wishlistAddAction($container, $_GET['email'], $_POST);
}
injeção de dependência
php orientado a objetos
function wishlistAddAction(Container $container, $email, $request)
{
try {
$intention = createIntention($request['wish_item']);
$useCase = $container->wishlistAddItemWishlistUseCase;
$wishlist = $useCase->execute($intention);
$successmsg = "{$wishlist->getItemName()} added!";
} catch (Exception $e) {
$errormsg = $e->getMessage();
}
}
injeção de dependência
php orientado a objetos
eventos
php orientado a objetos
mecanismo de inversão de controle
comunicação assíncrona
baixo acoplamento
eventos
php orientado a objetos
symfony/event-dispatcher
eventos
php orientado a objetos
class UpdateProductUseCase
{
public function __construct(/* args */, $eventDispatcher);
public function execute(Intention $intention)
{
$this->repository->update($updatedProduct);
$event = new ProductWasUpdated($updatedProduct);
$this->dispatch('product.updated', $event);
return $updatedProduct;
}
}
eventos
php orientado a objetos
class UpdateProductUseCase
{
public function __construct(/* args */, $eventDispatcher);
public function execute(Intention $intention)
{
$this->repository->update($updatedProduct);
$event = new ProductWasUpdated($updatedProduct);
$this->dispatch('product.updated', $event);
if ($this->isStockIncreased($product, $updatedProduct)) {
$this->dispatch('product.stock.increased', $event);
}
return $updatedProduct;
}
}
eventos
php orientado a objetos
class StockHasIncreasedListener
{
public function __construct(NotifyProductAvailable $useCase);
public function __invoke(ProductStockIncreasedEvent $event)
{
$productId = $event->getProduct()->getId();
$intention = new NotifyProductIntention($productId);
$this->useCase->execute($intention);
}
}
events
object oriented php
config/app.ini
[wishlistStockListener StockHasIncreasedListener]
useCase = [notifyProductAvailableUseCase]
[eventDispatcher EventDispatcher]
addListener[] = ["product.stock.increased",[wishlistStockListener]]
[productUpdateUseCase UpdateProductUseCase]
__construct = [[repository], [factory], [eventDispatcher]]
eventos
php orientado a objetos
estrutura
php orientado a objetos
business
services
application
web
devices
useinterface
database
ExternalInterfaces
camadas
php orientado a objetos
// public/wishlist.php
// ...
if (isset($_POST['submit']) && isValid($_POST['wish_item'])) {
$wishItem = createWishListItem($_POST['wish_item']);
$db->beginTransaction();
try {
$stm = $db->prepare('insert new entry (email, product_id)');
$stm->execute([$wishItem['email'], $wishItem['product_id']]);
$db->commit();
$successmsg = 'Product was added at wish list successfully!';
} catch (Exception $e) {
$db->rollBack();
$errormsg = 'Product could not be added at wishlist! :(';
}
}
// ..
php puro
// lib/controllers/wishList.php
function wishlistAddAction(Container $container, $email, $request)
{
try {
$intention = createIntention($request['wish_item']);
$useCase = $container->wishlistAddItemWishlistUseCase;
$wishlist = $useCase->execute($intention);
$successmsg = "{$wishlist->getItemName()} added!";
} catch (Exception $e) {
$errormsg = $e->getMessage();
}
}
php orientado a objetos
developing-for-business
├── cli
│ └── wishlist_notification.php
├── data
│ └── business.db
├── lib
│ ├── dbconn.php
│ └── functions.php
└── public
└── wishlist.php
estrutura php puro
DevelopBusiness
└── Application
├── Product
│ ├── Controllers
│ └── Repositories
└── ProductWishlist
├── Controllers
├── Resolver
├── Listeners
└── Repositories
DevelopBusiness
├── Product
│ ├── Events
│ ├── Exceptions
│ ├── Intentions
│ ├── Repositories
│ └── UseCases
└── Wishlist
├── Exceptions
├── Intentions
├── ItemResolver.php
├── Repositories
└── UseCases
estrutura php orientado a objetos
conclusão
saia da zona de conforto
interaja com a comunidade
conclusão
automatize tarefas repetitivas
conclusão
domain driven design
conclusão
domain specific language
conclusão
princípio S.O.L.I.D.
conclusão
Implementing Domain-Driven Design
por Vaughn Vernon
Patterns of Enterprise Application Architecture
por Martin Fowler
Domain-Driven Design
por Eric Evans
DDD in PHP
por Carlos Buenosvinos, Christian Soronellas e Keyvan Akbary
referências
Life's too short for bad software
Lew Cirne, New Relic

modernizando a arquitertura de sua aplicação

  • 1.
    modernizando a arquitertura desua aplicação Antonio Spinelli @tonicospinelli tornando o seu código mais robusto e escalável
  • 2.
  • 3.
  • 4.
    o objetivo nãomuda: resolver problemas para o negócio!
  • 5.
    entenda o problema agreguevalor ao negócio agilidade não é "sobrinhagem" desenvolvedor
  • 6.
    desenvolvedor automatize tarefas repetidas peçae faça code review leia código
  • 7.
    Lista de Desejospara Produtos Eu como um usuário convidado, quero adicionar um produto indisponível dentro da minha lista de desejos, de modo que eu receba uma notificação quando este estiver disponível. Objetivo reposição de estoque inteligente e estreitar relacionamento com cliente user story
  • 8.
  • 9.
    adicionando um itemna lista de desejos php puro
  • 10.
    // public/wishlist.php // ... if(isset($_POST['submit']) && isValid($_POST['wish_item'])) { } // ... php puro
  • 11.
    // public/wishlist.php // ... if(isset($_POST['submit']) && isValid($_POST['wish_item'])) { $wishItem = createWishListItem($_POST['wish_item']); } // ... php puro
  • 12.
    // public/wishlist.php // ... if(isset($_POST['submit']) && isValid($_POST['wish_item'])) { $wishItem = createWishListItem($_POST['wish_item']); $stm = $db->prepare('insert new entry (email, product_id)'); $stm->execute([$wishItem['email'], $wishItem['product_id']]); $successmsg = 'Product was added at wish list successfully!'; } // ... php puro
  • 13.
    // public/wishlist.php // ... if(isset($_POST['submit']) && isValid($_POST['wish_item'])) { $wishItem = createWishListItem($_POST['wish_item']); $db->beginTransaction(); try { $stm = $db->prepare('insert new entry (email, product_id)'); $stm->execute([$wishItem['email'], $wishItem['product_id']]); $successmsg = 'Product was added at wish list successfully!'; $db->commit(); } catch (Exception $e) { $db->rollBack(); $errormsg = 'Product could not be added at wishlist! :('; } } // .. php puro
  • 14.
    // public/wishlist.php // ... <table> <?phpforeach ($wishlist as $item): ?> <tr> <td><?php echo $item['id']; ?> </td> <td><?php echo $item['product_name']; ?> </td> <?php if ($item['product_stock'] == 0): ?> <td>Not Available</td> <?php else: ?> <td>Available</td> <?php endif; ?> <td> <?php echo removeUrl($item['id'], ['email' => $email]); ?> </td> </tr> <?php endforeach; ?> </table> // ... php puro
  • 15.
    notificar usuário quando oitem estiver disponível php puro
  • 16.
    // cli/wishlist_notify.php // ... $query= "find all available products from wishlist"; $stm = $db->prepare($query); $stm->execute(); $wishlists = $stm->fetchAll(PDO::FETCH_ASSOC); php puro
  • 17.
    // cli/wishlist_notify.php // ... $query= "find all available products from wishlist"; $stm = $db->prepare($query); $stm->execute(); $wishlists = $stm->fetchAll(PDO::FETCH_ASSOC); foreach ($wishlists as $item) { mail( $item['email'], "{$item['product_name']} available", $mailBody ); $stm = $db->prepare("define new status for id = ?"); $stm->execute([$item['id']]); } php puro
  • 18.
  • 19.
    quais problemas temoscom este código? atenção
  • 20.
    organização pobre sem verificaçãode erros dificuldade para reutilizar o código php puro
  • 21.
  • 22.
  • 23.
    // templates/wishlist/list.php <table> <?php foreach($wishlist as $item): ?> <tr> <td><?php echo $item['id']; ?> </td> <td><?php echo $item['product_name']; ?> </td> <?php if ($item['product_stock'] == 0): ?> <td>Not Available</td> <?php else: ?> <td>Available</td> <?php endif; ?> <td> <?php echo removeUrl($item['id'], ['email' => $email]); ?> </td> </tr> <?php endforeach; ?> </table> php estruturado
  • 24.
    isolando regra denegócio php estruturado
  • 25.
    // lib/models/wishlist.php function addItemIntoWishlist(array$data); function createWishListItem(array $data); function findAllWishlistsToNotify(); function findAllWishProducts($email); function isValidWishList(array $data); function removeWishItem($id); function wishlistNotified($id); php estruturado
  • 26.
    interagindo regras de negóciocom a apresentação php estruturado
  • 27.
    // public/wishlist.php require_once __DIR__. '/../lib/dbconn.php'; require_once __DIR__ . '/../lib/functions.php'; require_once __DIR__ . '/../lib/wishlist.php'; if (isset($_POST['submit']) && isValidWishList($_POST['wish_item'])) { $wishItem = createWishListItem($_POST['wish_item']); $db->beginTransaction(); try { $stm = $db->prepare('insert new entry (email, product_id)'); $stm->execute([$wishItem['email'], $wishItem['product_id']]); $db->commit(); $successmsg = 'Product was added at wish list successfully!'; } catch (Exception $e) { $db->rollBack(); $errormsg = 'Product could not be added at wishlist! :('; } } // ... php estruturado
  • 28.
    // public/wishlist.php require_once __DIR__. '/../lib/dbconn.php'; require_once __DIR__ . '/../lib/functions.php'; require_once __DIR__ . '/../lib/wishlist.php'; if (isset($_POST['submit']) && isValidWishList($_POST['wish_item'])) { $wishItem = createWishListItem($_POST['wish_item']); $db->beginTransaction(); try { $stm = $db->prepare('insert new entry (email, product_id)'); $stm->execute([$wishItem['email'], $wishItem['product_id']]); $db->commit(); $successmsg = 'Product was added at wish list successfully!'; } catch (Exception $e) { $db->rollBack(); $errormsg = 'Product could not be added at wishlist! :('; } } // ... structured php
  • 29.
    // public/wishlist.php require_once __DIR__. '/../lib/dbconn.php'; require_once __DIR__ . '/../lib/functions.php'; require_once __DIR__ . '/../lib/wishlist.php'; if (isset($_POST['submit']) && isValidWishList($_POST['wish_item'])) { $wishItem = createWishListItem($_POST['wish_item']); if (addItemIntoWishlist($wishItem)) { $successmsg = 'Product was added at wish list successfully!'; } else { $errormsg = 'Product could not be added at wishlist! :('; } } // ... php estruturado
  • 30.
    // public/wishlist.php require_once __DIR__. '/../lib/dbconn.php'; require_once __DIR__ . '/../lib/functions.php'; require_once __DIR__ . '/../lib/wishlist.php'; if (isset($_POST['submit']) && isValidWishList($_POST['wish_item'])) { $wishItem = createWishListItem($_POST['wish_item']); if (addItemIntoWishlist($wishItem)) { $successmsg = 'Product was added at wish list successfully!'; } else { $errormsg = 'Product could not be added at wishlist! :('; } } $wishlist = findAllWishProducts($_GET['email']); include __DIR__ . '/../templates/wishlists/list.php'; php estruturado
  • 31.
  • 32.
    // template/layout.php <!DOCTYPE html> <html> <head> <title><?phpecho $title ?></title> </head> <body> <?php echo $content ?> </body> </html> layout padrão php estruturado
  • 33.
    // template/wishlist/list.php <?php $title= 'My Wish List' ?> define o título da página php estruturado
  • 34.
    // template/wishlist/list.php <?php $title= 'My Wish List' ?> <?php ob_start() ?> <table> <tbody> <?php foreach ($wishlist as $wish): ?> <tr><!-- print all columns --></tr> <?php endforeach; ?> </tbody> </table> <?php $content = ob_get_clean() ?> define o conteúdo da página php estruturado
  • 35.
    // template/wishlist/list.php <?php $title= 'My Wish List' ?> <?php ob_start() ?> <table> <tbody> <?php foreach ($wishlist as $wish): ?> <tr><!-- print all columns --></tr> <?php endforeach; ?> </tbody> </table> <?php $content = ob_get_clean() ?> <?php include __DIR__ . '/../layout.php' ?> extendendo layout padrão php estruturado
  • 36.
  • 37.
    sem um frontcontroller GET /wishlist.php?email=email@test.com com index.php como um front controller GET /index.php/wishlist/email@test.com com regras de sobrescrita no servidor web GET /wishlist/email@test.com php estruturado
  • 38.
    // public/index.php // routethe request internally $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $method = $_SERVER['REQUEST_METHOD']; if (preg_match('/wishlist/(.*)$', $uri, $matches) && $method == 'GET') { wishlistListAction($matches[1]); } elseif ('/index.php/wishlist/add' === $uri && $method == 'POST') { wishlistAddAction($_GET['email'], $_POST); } else { header('HTTP/1.1 404 Not Found'); echo '<html><body><h1>Page Not Found</h1></body></html>'; } php estruturado
  • 39.
    // public/index.php // routethe request internally $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $method = $_SERVER['REQUEST_METHOD']; if (preg_match('/wishlist/(.*)$', $uri, $matches) && $method == 'GET') { wishlistListAction($matches[1]); } elseif ('/index.php/wishlist/add' === $uri && $method == 'POST') { wishlistAddAction($_GET['email'], $_POST); } else { header('HTTP/1.1 404 Not Found'); echo '<html><body><h1>Page Not Found</h1></body></html>'; } php estruturado
  • 40.
    // public/index.php // routethe request internally $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $method = $_SERVER['REQUEST_METHOD']; if (preg_match('/wishlist/(.*)$', $uri, $matches) && $method == 'GET') { wishlistListAction($matches[1]); } elseif ('/index.php/wishlist/add' === $uri && $method == 'POST') { wishlistAddAction($_GET['email'], $_POST); } else { header('HTTP/1.1 404 Not Found'); echo '<html><body><h1>Page Not Found</h1></body></html>'; } php estruturado
  • 41.
    // public/index.php // routethe request internally $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $method = $_SERVER['REQUEST_METHOD']; if (preg_match('/wishlist/(.*)$', $uri, $matches) && $method == 'GET') { wishlistListAction($matches[1]); } elseif ('/index.php/wishlist/add' === $uri && $method == 'POST') { wishlistAddAction($_GET['email'], $_POST); } else { header('HTTP/1.1 404 Not Found'); echo '<html><body><h1>Page Not Found</h1></body></html>'; } php estruturado
  • 42.
  • 43.
    // public/index.php require_once __DIR__. '/../config/app.php'; require_once LIBRARY_DIR . '/functions.php'; require_once LIBRARY_DIR . '/wishlist.php'; // include_path is other alternative ini_set('include_path', __DIR__ . '/../lib'); php estruturado autoload
  • 44.
  • 45.
    { "name": "tonicospinelli/developing-for-business", "description": "AQuick Project for Talk", "license": "MIT", "autoload": { "files": [ "./lib/dbconn.php", "./lib/functions.php", "./lib/controllers/wishlist.php", "./lib/models/wishlist.php" ] } } composer php estruturado
  • 46.
    $ composer dump-autoload //public/index.php require_once __DIR__ . '/../vendor/autoload.php'; php estruturado autoload
  • 47.
    developing-for-business ├── cli │ └──wishlist_notification.php ├── composer.json ├── config │ └── app.php ├── lib │ ├── controllers │ │ └── wishlist.php │ ├── functions.php │ └── models │ └── wishlist.php ├── public │ └── index.php ├── templates │ └── wishlists └── vendor estrutura do projeto php estruturado
  • 48.
  • 49.
    código auto explicativo facilidadeno crescimento testes automatizados php orientado a objetos
  • 50.
  • 51.
    garantem regras conhecidas auxiliamna refatoração não evitam bugs testes automatizados php orientado a objetos
  • 52.
    PHPUnit 5.4.6 bySebastian Bergmann and contributors. ................................................... 59 / 59 (100%) Time: 349 ms, Memory: 6.00MB OK (59 tests, 126 assertions) testes automatizados php orientado a objetos
  • 53.
    PHPUnit 5.4.6 bySebastian Bergmann and contributors. ..................................F................ 59 / 59 (100%) Time: 374 ms, Memory: 6.00MB There was 1 failure: 1) ProductTest::testUpdateProductFailed Failed asserting that exception of type "ProductException" is thrown. FAILURES! Tests: 59, Assertions: 125, Failures: 1. testes automatizados php orientado a objetos
  • 54.
    não confie emarrays evite abreviações php orientado a objetos
  • 55.
    /** * Creates anew wish list data. * @param array $data * @return array */ function newWishList(array $data) { return array( 'email' => isset($data['email']) ? $data['email'] : null), 'product_id' => $data['product_id'], 'status' => 'P', ); } php orientado a objetos
  • 56.
    objetos de valor phporientado a objetos
  • 57.
    objetos simples encapsulam tiposprimitivos representam o valor objetos de valor php orientado a objetos
  • 58.
    class Status { const PENDING= 'P'; const SENT = 'S'; private $status; public function __construct($status) { $this->validate($status); $this->status = $status; } // … other methods public function equalsTo(Status $status) { return $status === $this; } } objetos de valor php orientado a objetos
  • 59.
  • 60.
  • 61.
    class Wishlist { public function__construct( Id $id, Email email, WishlistItem $item, Status $status ) { $this->id = $id; $this->email = $email; $this->item = $item; $this->status = $status; } } entidades php orientado a objetos
  • 62.
    class Wishlist { public function__construct( Id $id, Email email, WishlistItem $item, Status $status ) { $this->id = $id; $this->email = $email; $this->item = $item; $this->status = $status; } } por que um objeto Item? entidades php orientado a objetos
  • 63.
    class Wishlist { public function__construct( Id $id, Email email, WishlistItem $item, Status $status ) { $this->id = $id; $this->email = $email; $this->item = $item; $this->status = $status; } } por que um objeto Item? Produtos Presente entidades php orientado a objetos
  • 64.
    interface WishlistItem { public functiongetId(); } class WishlistProduct implements WishlistItem { public function __construct( Id $id, $name, Available $availability ) { $this->id = $id; $this->name = $name; $this->available = $availability; } } entidades php orientado a objetos
  • 65.
  • 66.
    camada de persistência estratégiade armazenamento repositórios php orientado a objetos
  • 67.
    interface WishlistRepository { public functionadd(Wishlist $wishlist); public function delete(Wishlist $wishlist); public function find($id); public function findAllByEmail($email); public function findAllToNotify(); } repositórios php orientado a objetos
  • 68.
    namespace DevelopBusinessApplicationProductWishlistRepositories; class PdoRepositoryimplements WishlistRepository { public function __construct(PDO $driver, Factory $factory); public function findAllByEmail($email) { $query = "find all items by email"; $stm = $this->driver->prepare($query); $stm->execute([$email]); return $stm->fetchAll( PDO::FETCH_FUNC, [$this->factory, 'create'] ); } } repositórios php orientado a objetos
  • 69.
    namespace DevelopBusinessApplicationProductWishlistRepositories; class RedisRepositoryimplements WishlistRepository { public function __construct(Predis $driver, Factory $factory); public function find($id) { $wishlist = $this->driver->get($this->getKey($id)); if (!$wishlist) { throw WishlistNotFoundException::byIdentifier($id); } return $this->factory->fromJson($wishlist); } } repositórios php orientado a objetos
  • 70.
    class PdoRepositoryTest extendsPHPUnit_Framework_TestCase { public function testAddNewRowSuccessful() { $wishlist = $this->factory->fromQueryResult(/* args */); $pdoSpy = new PDOSpy(); (new Repository($pdoSpy))->add($wishlist); $this->assertTrue($pdoSpy->beginTransactionCalled); $this->assertTrue($pdoSpy->commitCalled); $this->assertEquals(2, $wishlist->getId()); } } como testar repositórios? php orientado a objetos
  • 71.
    class PDOSpy extendsPDO { public function beginTransaction() { $this->beginTransactionCalled = true; } public function prepare($statement, $options = null) { if ($statement == INSERT INTO wishlists VALUES (?, ?, ?)') { return new WriteStatementStub($this->failureOnWrite); } throw new InvalidArgumentException( "None Stub Statement was found for query: {$statement}" ); } } como testar repositórios? php orientado a objetos
  • 72.
    function wishlistListAction($email) { $db =dbConnect(); $factory = new WishlistFactory(); $repository = new WishlistPdoRepository($db, $factory); $wishlist = $repository->findAllByEmail($email); include TEMPLATE_DIR . '/wishlists/list.php'; } repositórios php orientado a objetos
  • 73.
  • 74.
    camada de operação orquestraos objetos define o que fazer serviços php orientado a objetos
  • 75.
    adicionando um itemna lista de desejos serviços php orientado a objetos
  • 76.
    $db = dbConnect(); $factory= new WishlistFactory(); $repository = new WishlistRepository($db, $factory); $resolver = new ItemResolver(new ProductRepository($db)); try { $intention = createIntention($request['wish_item']); $useCase = new AddItemWishlistUseCase( $repository, $factory, $resolver ); $wishlist = $useCase->execute($intention); $successmsg = "{$wishlist->getItemName()} added!"; } catch (DomainException $e) { $errormsg = $e->getMessage(); } resolvendo dependências adicionando um item na lista de desejos php orientado a objetos
  • 77.
    $db = dbConnect(); $factory= new WishlistFactory(); $repository = new WishlistRepository($db, $factory); $resolver = new ItemResolver(new ProductRepository($db)); try { $intention = createIntention($request['wish_item']); $useCase = new AddItemWishlistUseCase( $repository, $factory, $resolver ); $wishlist = $useCase->execute($intention); $successmsg = "{$wishlist->getItemName()} added!"; } catch (DomainException $e) { $errormsg = $e->getMessage(); } parsing da requisição adicionando um item na lista de desejos php orientado a objetos
  • 78.
    $db = dbConnect(); $factory= new WishlistFactory(); $repository = new WishlistRepository($db, $factory); $resolver = new ItemResolver(new ProductRepository($db)); try { $intention = createIntention($request['wish_item']); $useCase = new AddItemWishlistUseCase( $repository, $factory, $resolver ); $wishlist = $useCase->execute($intention); $successmsg = "{$wishlist->getItemName()} added!"; } catch (DomainException $e) { $errormsg = $e->getMessage(); } criando objeto de serviço adicionando um item na lista de desejos php orientado a objetos
  • 79.
    $db = dbConnect(); $factory= new WishlistFactory(); $repository = new WishlistRepository($db, $factory); $resolver = new ItemResolver(new ProductRepository($db)); try { $intention = createIntention($request['wish_item']); $useCase = new AddItemWishlistUseCase( $repository, $factory, $resolver ); $wishlist = $useCase->execute($intention); $successmsg = "{$wishlist->getItemName()} added!"; } catch (DomainException $e) { $errormsg = $e->getMessage(); } executando o serviço adicionando um item na lista de desejos php orientado a objetos
  • 80.
    $db = dbConnect(); $factory= new WishlistFactory(); $repository = new WishlistRepository($db, $factory); $resolver = new ItemResolver(new ProductRepository($db)); try { $intention = createIntention($request['wish_item']); $useCase = new AddItemWishlistUseCase( $repository, $factory, $resolver ); $wishlist = $useCase->execute($intention); $successmsg = "{$wishlist->getItemName()} added!"; } catch (DomainException $e) { $errormsg = $e->getMessage(); } mensagem de negócio adicionando um item na lista de desejos php orientado a objetos
  • 81.
    class AddItemWishlistUseCase { public function__construct( WishlistRepository $repository, WishlistFactory $factory, ItemResolver $resolver ) { // do something } } como adicionar um item na lista de desejos php orientado a objetos
  • 82.
    class AddItemWishlistUseCase { public functionexecute(AddItemIntention $intention) { $item = $this->resolver->resolve($intention->itemId); try { $wishlist = $this->repository->findOneByEmailAndItem( $email, $item ); throw WishlistException::itemAlreadyExists($wishlist); } catch (WishlistNotFoundException $e) { // do nothing } $wishlist = $this->factory->new($email, $item); return $this->repository->add($wishlist); } } php orientado a objetos como adicionar um item na lista de desejos
  • 83.
    class AddItemWishlistUseCase { public functionexecute(AddItemIntention $intention) { $item = $this->resolver->resolve($intention->itemId); try { $wishlist = $this->repository->findOneByEmailAndItem( $email, $item ); throw WishlistException::itemAlreadyExists($wishlist); } catch (WishlistNotFoundException $e) { // do nothing } $wishlist = $this->factory->new($email, $item); return $this->repository->add($wishlist); } } php orientado a objetos como adicionar um item na lista de desejos
  • 84.
    class AddItemWishlistUseCase { public functionexecute(AddItemIntention $intention) { $item = $this->resolver->resolve($intention->itemId); try { $wishlist = $this->repository->findOneByEmailAndItem( $email, $item ); throw WishlistException::itemAlreadyExists($wishlist); } catch (WishlistNotFoundException $e) { // do nothing } $wishlist = $this->factory->new($email, $item); return $this->repository->add($wishlist); } } php orientado a objetos como adicionar um item na lista de desejos
  • 85.
    serviços notificar usuário quando umitem estiver disponível php orientado a objetos
  • 86.
    // cli/wishlist_notify.php require_once __DIR__. '/../config/app.php'; require_once __DIR__ . '/../vendor/autoload.php'; $factory = new WishlistFactory(); $repository = new WishlistRepository(dbConnect(), $factory); $notifier = new MailNotifier(); $useCase = (new NotifyItemsUseCase($repository, $notifier); $useCase->execute(new NotifyItemsIntention(Status::PENDING)); notificar quando um item disponível resolving dependencies php orientado a objetos
  • 87.
    // cli/wishlist_notify.php require_once __DIR__. '/../config/app.php'; require_once __DIR__ . '/../vendor/autoload.php'; $factory = new WishlistFactory(); $repository = new WishlistRepository(dbConnect(), $factory); $notifier = new MailNotifier(); $useCase = (new NotifyItemsUseCase($repository, $notifier); $useCase->execute(new NotifyItemsIntention(Status::PENDING)); call service notificar quando um item disponível php orientado a objetos
  • 88.
    class NotifyItemsUseCase { public functionexecute(NotifyItemsIntention $intention) { $status = $intention->getStatus(); $wishlists = $this->repository->findAllByStatus($status); foreach ($wishlists as $wishlist) { $this->notifier()->send($wishlist); $wishlist->changeStatusTo(Status::sent()); $this->repository->update($wishlist); } } } como notificar quando um item disponível? php orientado a objetos
  • 89.
    class NotifyItemsUseCase { public functionexecute(NotifyItemsIntention $intention) { $status = $intention->getStatus(); $wishlists = $this->repository->findAllByStatus($status); foreach ($wishlists as $wishlist) { $this->notifier()->send($wishlist); $wishlist->changeStatusTo(Status::sent()); $this->repository->update($wishlist); } } } como notificar quando um item disponível? php orientado a objetos
  • 90.
    class NotifyItemsUseCase { public functionexecute(NotifyItemsIntention $intention) { $status = $intention->getStatus(); $wishlists = $this->repository->findAllByStatus($status); foreach ($wishlists as $wishlist) { $this->notifier()->send($wishlist); $wishlist->changeStatusTo(Status::sent()); $this->repository->update($wishlist); } } } como notificar quando um item disponível? php orientado a objetos
  • 91.
    injeção de dependência phporientado a objetos
  • 92.
    princípio da inversãode dependência dependências configuráveis injeção de dependência php orientado a objetos
  • 93.
  • 94.
    // config/app.ini [databaseConnection PDO] __construct= ["sqlite:../data/business.db", null, null] [factory WishlistFactory] [repository WishlistRepository] __construct = [[databaseConnection], [factory]] [productRepository ProductRepository] driver = [databaseConnection] [itemResolver WishlistItemResolver] product = [productRepository] injeção de dependência php orientado a objetos
  • 95.
    // public/index.php require_once __DIR__. '/../config/app.php'; require_once __DIR__ . '/../vendor/autoload.php'; $configFile = __DIR__ . '/../config/app.ini'; $container = new RespectConfigContainer($configFile); if ('/index.php/wishlist/add' === $uri /* other validations */) { wishlistAddAction($container, $_GET['email'], $_POST); } injeção de dependência php orientado a objetos
  • 96.
    // public/index.php require_once __DIR__. '/../config/app.php'; require_once __DIR__ . '/../vendor/autoload.php'; $configFile = __DIR__ . '/../config/app.ini'; $container = new RespectConfigContainer($configFile); if ('/index.php/wishlist/add' === $uri /* other validations */) { wishlistAddAction($container, $_GET['email'], $_POST); } injeção de dependência php orientado a objetos
  • 97.
    function wishlistAddAction(Container $container,$email, $request) { try { $intention = createIntention($request['wish_item']); $useCase = $container->wishlistAddItemWishlistUseCase; $wishlist = $useCase->execute($intention); $successmsg = "{$wishlist->getItemName()} added!"; } catch (Exception $e) { $errormsg = $e->getMessage(); } } injeção de dependência php orientado a objetos
  • 98.
  • 99.
    mecanismo de inversãode controle comunicação assíncrona baixo acoplamento eventos php orientado a objetos
  • 100.
  • 101.
    class UpdateProductUseCase { public function__construct(/* args */, $eventDispatcher); public function execute(Intention $intention) { $this->repository->update($updatedProduct); $event = new ProductWasUpdated($updatedProduct); $this->dispatch('product.updated', $event); return $updatedProduct; } } eventos php orientado a objetos
  • 102.
    class UpdateProductUseCase { public function__construct(/* args */, $eventDispatcher); public function execute(Intention $intention) { $this->repository->update($updatedProduct); $event = new ProductWasUpdated($updatedProduct); $this->dispatch('product.updated', $event); if ($this->isStockIncreased($product, $updatedProduct)) { $this->dispatch('product.stock.increased', $event); } return $updatedProduct; } } eventos php orientado a objetos
  • 103.
    class StockHasIncreasedListener { public function__construct(NotifyProductAvailable $useCase); public function __invoke(ProductStockIncreasedEvent $event) { $productId = $event->getProduct()->getId(); $intention = new NotifyProductIntention($productId); $this->useCase->execute($intention); } } events object oriented php
  • 104.
    config/app.ini [wishlistStockListener StockHasIncreasedListener] useCase =[notifyProductAvailableUseCase] [eventDispatcher EventDispatcher] addListener[] = ["product.stock.increased",[wishlistStockListener]] [productUpdateUseCase UpdateProductUseCase] __construct = [[repository], [factory], [eventDispatcher]] eventos php orientado a objetos
  • 105.
  • 106.
  • 107.
    // public/wishlist.php // ... if(isset($_POST['submit']) && isValid($_POST['wish_item'])) { $wishItem = createWishListItem($_POST['wish_item']); $db->beginTransaction(); try { $stm = $db->prepare('insert new entry (email, product_id)'); $stm->execute([$wishItem['email'], $wishItem['product_id']]); $db->commit(); $successmsg = 'Product was added at wish list successfully!'; } catch (Exception $e) { $db->rollBack(); $errormsg = 'Product could not be added at wishlist! :('; } } // .. php puro
  • 108.
    // lib/controllers/wishList.php function wishlistAddAction(Container$container, $email, $request) { try { $intention = createIntention($request['wish_item']); $useCase = $container->wishlistAddItemWishlistUseCase; $wishlist = $useCase->execute($intention); $successmsg = "{$wishlist->getItemName()} added!"; } catch (Exception $e) { $errormsg = $e->getMessage(); } } php orientado a objetos
  • 109.
    developing-for-business ├── cli │ └──wishlist_notification.php ├── data │ └── business.db ├── lib │ ├── dbconn.php │ └── functions.php └── public └── wishlist.php estrutura php puro
  • 110.
    DevelopBusiness └── Application ├── Product │├── Controllers │ └── Repositories └── ProductWishlist ├── Controllers ├── Resolver ├── Listeners └── Repositories DevelopBusiness ├── Product │ ├── Events │ ├── Exceptions │ ├── Intentions │ ├── Repositories │ └── UseCases └── Wishlist ├── Exceptions ├── Intentions ├── ItemResolver.php ├── Repositories └── UseCases estrutura php orientado a objetos
  • 111.
  • 112.
    interaja com acomunidade conclusão
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
    Implementing Domain-Driven Design porVaughn Vernon Patterns of Enterprise Application Architecture por Martin Fowler Domain-Driven Design por Eric Evans DDD in PHP por Carlos Buenosvinos, Christian Soronellas e Keyvan Akbary referências
  • 118.
    Life's too shortfor bad software Lew Cirne, New Relic