Do legado ao DDD
Leonn Leite
engesoftware
PHPDF
@leonnleite
Duck talk
● Nada mal
● Anda mal
● Voa mal
Agenda
● Motivação
● Entendendendo sua aplicação
● O que é DDD?
● Um caminho
Motivação
Brasília é o PAÍS do legado
CI em legado, é perigoso
Replicando .
Difícil de manter
Aplicações estruturadas
Quando tem OO
muitas vezes é OO estruturado
Métodos com 1k de linhas
SIMEC, ZF1
Testes
Existe um ser humano
Testes unitários, não existem
Se existem, é quando sobra tempo
Mas nunca sobra tempo
TDD?
Contratos com governo não exigem
Se não paga, não faço
Reutilização
Primeira resposta do stackoverflow
Ctrl + C, Ctrl + V
Satis ou Toran Proxy
Culpado?
Ninguém
e todo mundo
Tem culpa eu?
Falta de tempo
Experiência dos profissionais
Comunicação falha
Falta de tempo
Não reinvente a roda
Foca no problema real
"perfumaria" não deve tomar
mais tempo que seu problema real
Tá bonito, mas não funciona
Experiência dos
profissionais
Pair programming
Fale (literalmente) seus problemas
Leia mais código
Faça code review
Leia do github
Falha na
comunicação
Medo de perguntar
Cada um fala de uma forma
As vezes, quando tentamos
programar em inglês, fica mais
complexo
Encontro de comunidades do PHPx2018
"Vocês ainda não perceberam, que o poder da empresa está na mão do
programador?!" - Baeta, Tiago (sócio-fundador do iMasters)
Entendendo sua aplicação
"Um povo que não conhece a sua história está condenado a repeti-la" Edmund Burke
<?php
if (isset($_POST['submit'])) {
$stm = $db->prepare('insert into ... ');
$stm->execute($_POST['user_name'], $_POST['user_password']);
$sucess = 'Deu bom!';
}
?>
<?php
if (isset($_POST['submit'])) {
if (strlen($_POST['user_password']) >= 6) {
$stm = $db->prepare('insert into ... ');
$stm->execute($_POST['user_name'], $_POST['user_password']);
$sucess = 'Deu bom!';
}
}
?>
<?php
if (isset($_POST['submit'])) {
if (strlen($_POST['user_password']) >= 6) {
$stm = $db->prepare('insert into ... ');
$stm->execute($_POST['user_name'], $_POST['user_password']);
$sucess = 'Deu bom!';
} else {
$sucess = 'Senha invalida';
}
}
?>
<?php
if (isset($_POST['submit'])) {
if (strlen($_POST['user_password']) >= 6) {
$stm = $db->prepare('insert into ... ');
$stm->execute($_POST['user_name'], $_POST['user_password']);
$sucess = 'Deu bom!';
} else {
$sucess = 'Senha invalida';
}
}
?>
<table>
<tr>
<td><?php echo $sucess ?></td>
</tr>
</table>
Funciona?
Sim
mas...
Verificação de erros, quase nula
Desorganizado
Reutilização é só possível se copiar
Manutenção difícil
Dificuldade nos testes
Jogar fora e criar do zero
vs
Refatorar
View
View
Camada
Não tem inteligência
Só imprime
json é view
xml é view
html é view
Front Controller
Front Controller
Meio de campo
Controla as requisições
Inicia todas as jogadas
Service
Service
Chamadas externas
Faz a transição do controller para o
dominio
Orquestra as operações do dominio
Regra de negócio
(Não deveriam estar aqui)
VO
(value object)
VO
(value object)
É um objeto...
Encapsulam tipos primitivos
Money (exemplo clássico)
Email
Representam valor
Imutáveis
https://github.com/moneyphp/money
use MoneyMoney;
$fiveEur = Money::EUR(500);
$tenEur = $fiveEur->add($fiveEur);
final class Money //..
{
//..
public function add(Money ...$addends)
{
$amount = $this->amount;
//..
return new self($amount, $this->currency);
}
//..
}
Entity
Entity
Também são objetos…
Possui um identificador
Que não deve mudar
São mutáveis
Pode possuir VO's
class User
{
private $id;
private $name;
private $email;
public function __construct(string $name, string $email)
{
$this->id = Uuid::uuid4();
$this->name = $name;
$this->email = $email;
}
}
$validUser = new User('Marcel', 'marcel@santos.com'); //ok
$maybeValidUser = new User('Marcel', 'phpsp'); //ok
class Email
{
private $email;
public function __construct(string $email)
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('invalid email format');
}
$this->email = $email;
}
}
$validUser = new User('Marcel', new Email('marcel@santos.com')); //ok
$invalidUser = new User('Marcel', new Email('phpsp')); // not ok
Repository
Repository
Coleção
Camada de persistência
In/Out
Inversão de dependência (SOLID)
interface UserRepository
{
public function add(User $user): bool;
public function delete(User $user): bool;
public function find(Uuid $uuid): User;
public function findAllByEmail(Email $email): UserCollection;
public function findAll(): UserCollection;
}
O que é DDD?
Eric Evans
Tackling complexity in the heart of
software
(atacando as complexidades no coração do software)
Core Domain
Core Domain
Domain =
Core Domain + Subdomain
Core Domain
Subdomain
Subdomain
Subdomain
What makes the system worth writing?
(Por que escrever esse software vale a pena?)
Why not buy it off the shelf?
(Por que não comprar uma solução pronta?)
Why not outsource it?
(Por que não terceirizar o desenvolvimento)
Cuidado
Nem sempre o core domain é o que
parece
Subdomain =
Generic + Supporting
Generic Subdomain
Generic
Subdomain
Open source
Pago
Terceirizado
Supporting
Módulo de pagamento no ecommerce
Autenticação em sistemas corporativos
Eu não consigo viver sem o supporting
Eu consigo adaptar o generic
Nelson Sena
Core Domain
Generic
Subdomain
Supporting
Supporting
IVSF
“Irresistível Vontade de Sair Fazendo”
http://www.eduardopires.net.br/2016/03/ddd-bounded-context/
Comunicação
Bad Communication
Bad code
Exemplo:
Planta
Domain expert
Ubiquitous Language
Ubiquitous
Language
Código
Fala
Todos envolvidos tem que falar
nesses termos
Todo mundo deve saber o que é
"planta", o programador e o
"cliente"
É mais fácil você adaptar ou o
cliente? Alguém vai ter que dar o
braço a torcer
(dentro de um contexto limitado)
Bounded Context
(pra mim a parte mais difícil)
https://martinfowler.com/bliki/BoundedContext.html
UpdateUser
===
MoveToNewAddress
ChangeEmail
ChangePassword
IntroduceNewContactPerson
https://www.slideshare.net/andrzejkrzywda/from-legacy-to-ddd-slides-for-the-screencast
Named Constructor
new User('Leonn', 5);
new User('LInacio', 4);
User::fromNameAndFingersByHand('Leonn', 5);
O caminho
(que não é a solução definitiva)
tem muita opinião minha
e de outros
Não desenvolva baseado em framework
Independente de storage
(banco de dados, cache, search engine)
Usem "final class"
ActiveRecord
ActiveRecord
Use Eventos
Modelo anêmico vs Rico
Modelo
anêmico
getter
setter
Martin Fowler desaprova
https://www.martinfowler.com/bliki/AnemicDomainModel.html
Modelo rico
geralmente não tem setter
é construtor mermo
regra de negócio é na entidade
segue mundo real
https://andrebvitoria.wordpress.com/2017/04/11/modelo-anemico-vs-modelo-rico/
Delivery Mechanism
public function saveUser(ParameterBag $parameterBag)
{
$user = new User();
if ($id = $parameterBag->get('id')) {
$user = $this->userRepository>find($id);
if (!$user) {
throw new UserNotFoundException();
}
}
$user>setName($parameterBag->get('name'));
$user>setEmail($parameterBag->get('email'));
$user>setIsActive($parameterBag->get('isActive'));
$this->entityManager->persist($user);
$this->entityManager->flush();
$this->getLogger()->info(Usuario adicinado', $user);
$this->getMailer()->send('Usuario adicinado', 'Foi adicionada um categorio ' . $user->getName());
$this->getSms()->send('Olá, foi adicinado um usuario');
return $category;
}
CommandBus
Middleware
final class AddUserCommand
{
private $name;
private $email;
public function __construct(string $name, Email $email)
{
// ..
}
public function getName(): string
{
return $this->name;
}
//...
}
final class AddUserHandler
{
private $repository;
private $eventDispatcher;
public function __construct(UserRepository $repository, EventDispatcher $dispatcher)
{
//..
}
public function handle(AddUserCommand $command)
{
$user = User::createByNameAndEmail(
$command->getName(),
$command->getEmail()
);
$this->emailRepository->add($user);
$this->eventDispatcher->dispatcher(AddUserEvent::class, $user);
}
}
final class EditUserCommand
{
private $id;
private $name;
private $email;
public function __construct(Uuid $id, string $name, Email $email)
{
// ..
}
public function getName(): string
{
return $this->name;
}
//...
}
Saia da zona de conforto
Agradecimento
Marcel dos Santos
Bruno Neves
Marcelo MX
Ciro Vargas
Keywords
DDD
Eric Evans
Vaughn Vernon
Arquitetura Hexagonal
CQRS
Event Source
Domain Events
CommandBus
big ball of mud
Referências
Antonio Spinelli https://www.youtube.com/watch?v=8Oc22yq8O_I
Jefersson Nathan https://www.youtube.com/watch?v=JPkhrVkXKhE
Marcel Santos https://speakerdeck.com/marcelgsantos/melhore-a-qualidade-do-seu-codigo-com-object-calisthenics
Bruno Neves https://www.slideshare.net/brunonm/projetando-uma-arquitetura-expressiva
Nelson Sena https://www.youtube.com/watch?v=TOtA40H2MUs
Eric Evans https://domainlanguage.com/ddd/surrounded-by-legacy-software/
Andrzej Krzywda https://www.youtube.com/watch?v=g0Eoe1DfFPE
Carlos Buenovinos https://www.youtube.com/watch?v=aKcmbOZV9mA&list=PLfgj7DYkKH3Cd8bdu5SIHGYXh_bPV2idP
Duvidas?

Do legado ao DDD