$30 off During Our Annual Pro Sale. View Details »

Workshop de Qualidade: Um resumo prático do que todo dev precisa saber

Workshop de Qualidade: Um resumo prático do que todo dev precisa saber

Workshop de Qualidade de Software apresentado no Darkmira Tour PHP 2019, em Fortaleza, CE.

Assuntos abordados: Clean Code (resumo do livro), Object Calisthenics, PHP Defensivo, Arquitetura de Software, Testes (https://github.com/jgrossi/darkmira-2019-workshop) e SOLID.

Total: 8 horas de duração.

Junior Grossi

June 07, 2019
Tweet

More Decks by Junior Grossi

Other Decks in Programming

Transcript

  1. WORKSHOP DE QUALIDADE
    Qualidade de Código:
    Um resumo prático do que todo dev precisa saber

    View Slide

  2. AVISOS
    Não dá pra cobrir tudo
    "Resumo prático"
    Qualidade envolve bastante teoria
    Experiência vem com o tempo

    View Slide

  3. AVISOS
    Não existe apenas um jeito certo
    A prática vai te mostrar o seu jeito!
    Diferente é bom! Faz pensar!

    View Slide

  4. AGENDA
    MANHÃ
    1. Resumão do Clean Code
    2. Object Calisthenics
    3. PHP Defensivo

    View Slide

  5. AGENDA
    TARDE
    1. Panorama sobre Arquitetura de So ware
    2. TDD raiz na prática
    3. SOLID com outros olhos

    View Slide

  6. FORMATO
    Interrompa quando quiser
    Contribua se puder
    Pequena pausa de 10 a 15 min entre temas

    View Slide

  7. CONSIDERAÇÕES
    Workshop em guias/temas auto-explicativos
    Temas que mudaram a minha visão
    Garantia Vitalícia da Camaradagem

    View Slide

  8. PARTIU!

    View Slide

  9. CLEAN CODE
    resumão pra quem comprou mas não leu o livro!

    View Slide

  10. LIVRO
    Clean Code
    A Handbook of Agile So ware Cra smanship
    Robert C. Martin (Uncle Bob)

    View Slide

  11. AGENDA
    Resumo dos principais tópicos
    Não dá pra cobrir tudo
    Focado em PHP quando possível!
    Alguns detalhes vão ser explicados mais à frente

    View Slide

  12. CAPÍTULO 1
    Clean Code
    "You are reading this book for two reasons. First, you are a programmer. Second,
    you want to be a better programmer. Good. We need better programmers."

    View Slide

  13. (bootstrap time!)

    View Slide

  14. "CODE-SENSE"
    "A programmer without "code-sense" can look at a messy module and
    recognize the mess but will have no idea what to do about it. A
    programmer with "code-sense" will look at a messy module and see
    options and variations." Uncle Bob

    View Slide

  15. SEGUNDO UNCLE BOB:
    Escrever código limpo requer disciplina
    O "code-sense" é a chave!
    Alguns de nós nascem com o "code-sense"
    Outros só com a prática!

    View Slide

  16. ALGUNS PONTOS
    Passamos mais tempo lendo código
    Sucesso relacionado com o entendimento
    (o entendimento diminui ao longo do tempo)

    View Slide

  17. CAPÍTULO 2
    Meaningful Names
    "Names are everywhere in so ware. We name our variables, our functions, our
    arguments, classes, and packages. We name our source files and the directories
    that contain them. We name and name and name."

    View Slide

  18. EXPRESSE INTENÇÃO AO DAR NOMES
    Use nomes pronunciáveis / Evite abreviações

    View Slide

  19. class DtaRcrd102
    {
    private DateTime $genymdhsm;
    private DateTime $modymdhms;
    private string $pszqint = "102";
    }
    class Customer
    {
    private DateTime $generationTimestamp;
    private DateTime $modificarionTimestamp;
    private string $recordId = "102";
    }

    View Slide

  20. NOMES "BUSCÁVEIS" / CONSTANTES
    for ($i = 0; $i < 5; $i++) {
    // ...
    }
    for ($i = 0; $i < WORK_DAYS_PER_WEEK; $i++) {
    // ...
    }

    View Slide

  21. EVITE PREFIXOS/SUFIXOS DESNECESSÁRIOS
    (As pessoas geralmente "aprendem" a ignorar prefixos e sufixos muito rapidamente.)
    $txtFirstName = $controller­>request()­>get('txt_FirstName');

    View Slide

  22. INTERFACES E IMPLEMENTAÇÕES
    Evitar informação demais pro usuário (dev)
    (estou passando uma interface ou classe concreta?)
    PSR Naming Conventions | Symfony
    UserInterface, AbstractResolver, ReusableTrait

    View Slide

  23. EVITE O "MAPA MENTAL"
    Não obrigue o usuário a tentar entender o que está
    escrito
    Buscar entender pelo contexto é muito mais
    demorado!

    View Slide

  24. NOMES DE CLASSES
    "Classes and objects should have noun or noun phrase names like Customer, WikiPage,
    Account, and AddressParser. Avoid words like Manager, Processor, Data, or
    Info in the name of a class. A class name should not be a verb."
    (resumindo: nome de classe = substantivo)

    View Slide

  25. NOMES DE MÉTODOS
    Devem ser verbos
    Utilize Named Constructors (mais significado)
    Accessors (get), Mutators (set) e predicates (is)

    View Slide

  26. UMA PALAVRA POR CONCEITO
    Nunca misture palavras de conceitos similares
    fetch, retrieve, get
    DeviceController, DeviceManager

    View Slide

  27. USE NOMES PRESENTES EM PADRÕES
    Todo dev sabe (ou deveria) saber Design Patterns
    Facilita o entendimento sobre a solução
    implementada
    AccountVisitor, ConsoleDecorator, UserFactory

    View Slide

  28. NÃO ADICIONE CONTEXTO DESNECESSÁRIO
    Contexto deve ficar no namespace
    Nome de classe deve resumir o que ela faz
    GSDAMailingAddress, GSDAAccountAddress

    View Slide

  29. CAPÍTULO 3
    Functions
    "In the early days of programming we composed our systems of routines and
    subroutines. Nowadays only the functions survives from those early days.
    Functions are the first line of organization in a any program."

    View Slide

  30. FUNÇÕES/MÉTODOS PEQUENOS
    Máximo 20 linhas. Se possível menos.
    Máximo 120 caracteres por linha. Se possível 80.

    View Slide

  31. BLOCOS E INDENTAÇÃO
    Cada bloco de uma linha (chamada de método)
    Métodos com 1 ou 2 níveis de indentação
    Muito mais fáceis de entender

    View Slide

  32. FAÇA UMA COISA
    Devem fazer apenas uma coisa (e fazer bem)
    Nomes muito grande podem necessitar ser
    refatorados
    Palavras de alerta: And, Or, If

    View Slide

  33. "STEPDOWN RULE"
    Leitura fluente como texto
    if (!$user = $this­>findUser()) {
    // ...
    }
    $this­>save($user);
    $user = $this­>findUser();
    if (!$user) {
    // ...
    }
    $this­>save($user);

    View Slide

  34. private function something(): bool
    {
    return !$this­>foo() || $this­>bar();
    }
    private function something(): bool
    {
    if (!$this­>foo()) {
    return false;
    }
    if ($this­>bar()) {
    return true;
    }
    return false;
    }

    View Slide

  35. switch
    Tolerados se aparecerem apenas uma vez
    Pensar em AbstractFactory se mais de uma vez
    Cuidado para não ferir SRP

    View Slide

  36. USE NOMES DESCRITIVOS
    Word's principle: "You know you are working on clean code when each routine
    turns out to be pretty much what you expected."
    Não tenha medo de escrever nomes longos
    Escreva exatamente o que está sendo feito

    View Slide

  37. ARGUMENTOS
    Nenhum é sempre melhor
    1 ou 2 aceitáveis / 3 deve ser evitado
    (aumento do número de argumentos dificulta escrever testes)

    View Slide

  38. "Flag Arguments" nunca (boolean)
    Deixa claro que faz mais de uma coisa
    Refatore para um método para cada coisa

    View Slide

  39. Encapsule argumentos em objetos
    (dá mais significado ao argumento)
    private function makeCircle(float $x, float $y): Circle
    {
    // ...
    }
    private function makeCircle(Point $point): Circle
    {
    // ...
    }

    View Slide

  40. Verbos e palavras-chave
    (dá mais significado ao argumento)
    public function find(Email $email): User
    {
    // ...
    }
    public function findUserByEmail(Email $email): User
    {
    // ...
    }

    View Slide

  41. PREFIRA EXCEPTIONS
    São melhores que códigos de erros
    try/catch em método separado
    $user = $this­>getLoggedUser();
    public function getLoggedUser(): ?User
    {
    try {
    return $this­>userResolver­>user();
    } catch (EmptyTokenException $e) {
    return null;
    }
    }

    View Slide

  42. CAPÍTULO 4
    Comentários
    "Don't comment bad code - rewrite it."
    Brian W. Kernighan e P. J. Plaugher

    View Slide

  43. "Truth can only be found in one place: the code.
    Only the code can truly tell you what it does."

    View Slide

  44. View Slide

  45. COMENTÁRIOS ACEITÁVEIS
    Legal Comments
    Clarification (formato de datas)
    Comentários TODO
    Referência a issues (ticket Jira)

    View Slide

  46. CONSIDERAÇÕES
    Remova comentários que não adicionam nada
    (docblock)
    Referências internas
    Críticas ou sugestões

    View Slide

  47. /**
    * Returns the day of the month.
    *
    * @return int the day of the month.
    */
    public function getDayOfMonth(): int
    {
    return $this­>dayOfMonth;
    }

    View Slide

  48. View Slide

  49. /** The first name */
    private string $firstName;
    /** The last name */
    private string $lastName;
    /* Added by Junior */
    $response = new InputStreamResponse();
    $response­>setBody($formatter­>getResultStream());
    // $resultStream = $formatter­>getResultStream();
    // $reader = new StreamReader($resultStram);

    View Slide

  50. CAPÍTULO 5
    Formatação
    Visual importa sim!

    View Slide

  51. Escolhe um code style e o siga
    Agrupe blocos de linhas no mesmo contexto
    Declare variáveis próximo da utilização
    Variáveis de instância sempre no topo da classe

    View Slide

  52. Funções dependentes próximas
    Não alinhe horizontalmente
    class FitNesseExpediter implements ResponseSender
    {
    private Socket $socket;
    private InputStream $input;
    private OutputStream $output;
    private Request $request;
    private Response $response;
    private FitNesseContext $context;
    private float $requestParsingTimeLimit;
    protected float $requestProgress;
    }

    View Slide

  53. Indentação. Nem precisa falar, né?

    View Slide

  54. CAPÍTULO 6
    Objetos e Estrutura de Dados
    "There is a reason that we keep our variables private. We don't want anyone else
    to depende on them. We want to keep the freedom to change their type or
    implementation on a whim or an impulse."

    View Slide

  55. Esconder implementação expões abstrações
    Manipular a essência dos dados por abstrações
    OOP permite adicionar novas classes sem mudar as
    existentes

    View Slide

  56. LEI DE DEMETER
    "Fale com amigos, não com estranhos."
    Encapsule o máximo possível. Exponha apenas o necessário.

    View Slide

  57. DTO (DATA TRANSFER OBJECTS)
    Classes puras somente para transferir dados
    Imutável. Somente getters.

    View Slide

  58. class Address implements ResponseSender
    {
    private string $street;
    private string $city;
    private Country $country;
    public function __construct(
    string $street,
    string $city,
    Country $country
    ) {
    $this­>street = $street;
    $this­>city = $city;
    $this­>country = $country;
    }
    // Only getters
    }

    View Slide

  59. CAPÍTULO 7
    Error Handling
    "...things can go wrong, and when they do, we as programmers are responsible
    for making sure that our code does what it needs to do."

    View Slide

  60. Exceptions sempre. Código de erro nunca!
    Escreva o bloco try/catch primeiro
    (facilita pensar em todas as possíveis exceções)

    View Slide

  61. Exceptions como primeira opção
    Trate a exceção na origem
    public function resolveUser(): User
    {
    $user = $this­>findInRequest();
    if (!$user) {
    throw new UserNotFoundException();
    }
    return $user;
    }
    try {
    $user = $resolver­>resolveUser();
    } catch (UserNotFoundException $e) {
    return new Response(/**/);
    }

    View Slide

  62. DEFINA CONTEXTO
    public static function byEmail(string $email): self
    {
    return new self(
    sprintf('User not found with the email %s', $email)

    }
    throw UserNotFoundException::byEmail($user­>getEmail());

    View Slide

  63. NULL RETURN
    Evite retornar null
    Dispare exception e trate na origem
    Use Null Objects

    View Slide

  64. NÃO PASSE NULL COMO PARÂMETRO
    Perde consistência
    Adiciona complexidade (mais ifs)

    View Slide

  65. CAPÍTULO 9
    Unit Tests
    "The Agile and TDD movements have encouraged many programmers to write
    automated unit tests, and more are joining their ranks every day. But in the mad
    rush to add testing to our discipline, many programmers have missed one of the
    more subtle, and important, points of writing good tests."

    View Slide

  66. AS 3 LEIS DO TDD
    1. Você não deve escrever código antes de ter escrito um teste pra
    ele que irá falhar.
    2. Você não deve escrever mais de um teste unitário que seja
    suficiente para passar.
    3. Você não deve escrever mais código do que o necessário para
    fazer o teste passar.

    View Slide

  67. TESTES ANTES OU DEPOIS?
    Antes! Ciclo 1, 2, 3 que irá cobrir todos os casos
    possíveis, incluindo de falhas.
    Escreva um teste e o faça falhar!
    Adicione aquele if maroto e faça o teste passar!

    View Slide

  68. CLEAN TESTS?
    Clean Code também para testes
    Crie "helpers" em forma de trait para adicionar
    funcionalidades extras
    trait JwtAuthenticationTrait
    {
    use HttpRequestsTrait;
    public function loginUser(User $user): void
    {
    $serializedToken = $this­>generateTokenForUser($user);
    $this­>request­>withHeader(
    'HTTP_AUTHORIZATION', sprintf('Bearer %s', $serializedToken)

    }
    }

    View Slide

  69. DICAS PARA CLEAN TESTS
    Apenas um assert por teste (customize!)
    (nomes de testes mais claros)
    Apenas um conceito por teste
    (cada caso possível é um novo teste)

    View Slide

  70. Teste com usuário admin + teste com não admin
    public function test_only_admins_can_change_user_type(): void {}
    public function test_admin_users_can_change_user_type(): void {}
    public function test_non_admin_users_cannot_change_user_type(): void

    View Slide

  71. "FIRST" RULE
    Fast: testes devem ser rápidos
    Independent: não devem compartilhar detalhes
    Repeatable: independentes de ambiente
    Self-Validating: ou passou ou não passou (boolean)
    Timely: escreva testes sempre antes (depois + difícil de testar)

    View Slide

  72. BÔNUS!
    Regra dos 3As (Arrange, Act, Assert)
    public function test_admin_users_can_change_user_type(): void
    {
    // Arrange
    $admin = $this­>makeAdminUser();
    $user = $this­>makeNormalUser(['type' => User::TYPE_MEMBER]);
    $this­>loginAsUser($admin);
    // Act
    $formatter = new Formatter();
    $user = $formatter­>format($user);
    // Assert
    $this­>assertEquals(User::TYPE_MEMBER, $user­>getType());
    }

    View Slide

  73. CAPÍTULO 10
    Classes
    "Let's talk about Clean Classes"

    View Slide

  74. ENCAPSULAMENTO
    private por padrão
    protected quando necessário (herança ou testes)
    Foco sempre em encapsular primeiro! (use abstrações)

    View Slide

  75. CLASSES PEQUENAS
    Evite "God Classes" (SRP)
    Tamanho é determinado pelo nome!
    (evite Manager ou Processor)
    Caso feliz: ter um único método público (SRP)
    (facilita muito abstração)

    View Slide

  76. COESÃO
    Busque sempre alta coesão
    (alto relacionamento entre métodos e propriedades)
    Poucas variáveis de instância por classe
    (favorece SRP!)

    View Slide

  77. COESÃO ENTRE CLASSES
    Use e abuse de Dependency Injection
    (favorece SRP!)
    Favorece DRY
    (Don't Repeat Yourself)

    View Slide

  78. "COMPOSITION OVER INHERITANCE"
    Mais fácil com classes menores
    Nova funcionalidade? Nova classe + Dependency
    Injection da anterior!

    View Slide

  79. Nada de TokenManager ou coisas do tipo!
    OCP - Open-close Principle
    final class JwtTokenGenerator implements TokenGeneratorInterface {}
    final class IntegrationTokenGenerator implements TokenGeneratorInterface
    {
    private TokenGeneratorInterface $tokenGenerator;
    public function __construct(TokenGeneratorInterface $tokenGenerator)
    {
    $this­>tokenGenerator = $tokenGenerator;
    }
    }

    View Slide

  80. CAPÍTULO 12
    Emergent Design
    "What if there were four simple rules that you could follow that would help you
    create good designs as you worked?"

    View Slide

  81. 4 REGRAS DO SIMPLE DESIGN RULE

    View Slide

  82. REGRA 1
    RODE TODOS OS TESTES
    Continuamente (Loop 1, 2, 3)
    Incrementalmente (Continuous Integration)

    View Slide

  83. REGRA 2 A 4
    REFATORE
    Remova código, não só adicione!
    "The fact that we have these tests eliminates the fear that cleaning up
    the code will break it!"
    TDD (testes ANTES! coverage!)

    View Slide

  84. REGRA 2: ELIMINE DUPLICIDADE
    "Duplication is the primary enemy of a well-designed system!"
    Use e abuse de SRP!

    View Slide

  85. REGRA 3: GARANTA EXPRESSIVIDADE
    Escolha de bons nomes
    Utilização de Design Patterns
    Facilite a vida do próximo dev! Ele pode ser você!

    View Slide

  86. REGRA 4: MINIMIZE CLASSES E MÉTODOS
    Regra menos importante!
    Não exagere na SRP!
    Não tenha classes ou métodos demais

    View Slide

  87. OBRIGADO!

    View Slide

  88. OBJECT CALISTHENICS
    9 regras que vão mudar sua
    forma de programar

    View Slide

  89. QUALIDADE DE CÓDIGO

    View Slide

  90. CLEAN CODE / SOLID

    View Slide

  91. Você realmente escreve códigos 100% SOLID?
    #sinceridade

    View Slide

  92. SOBRE SOLID:
    É um objetivo muito ousado
    Exige muitos conceitos de OOP
    Envolve muita experiência em abstração
    Exige um time muito "Maduro "

    View Slide

  93. SOLID não é um processo
    SOLID é o objetivo final...
    ...de um processo muito longo

    View Slide

  94. COMO COMEÇAR O PROCESSO?

    View Slide

  95. É impossível aplicar SOLID em uma classe "gorda"

    View Slide

  96. View Slide

  97. Sua classe precisa de uma dieta

    View Slide

  98. OBJECT CALISTHENICS

    View Slide

  99. Jeff Bay, 2008
    The Thoughtworks Anthology

    View Slide

  100. 9 REGRAS/EXERCÍCIOS
    para deixar seus objetos em forma

    View Slide

  101. ATENÇÃO
    Não se engane
    Algumas regras são bem difíceis
    Aplique com cautela
    Não seja um extremista
    Invista tempo em cada regra
    Algumas regras são dependentes

    View Slide

  102. REGRA 1
    Apenas um nível de indentação por método

    View Slide

  103. MOTIVOS
    Evitar efeito hadouken
    Facilitar leitura do código
    Promover SRP (Single Responsibility Principle)

    View Slide

  104. public function notify(DateInterval $dateInterval): void
    {
    $posts = $this­>postsRepository
    ­>findByDateInterval($dateInterval);
    foreach ($posts as $post) {
    $totals = $this­>report­>calculateTotals($post);
    foreach ($post­>authors() as $author) {
    $this­>message­>sendTo($author, $totals);
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    View Slide

  105. $totals = $this­>report­>calculateTotals($post);
    foreach ($post­>authors() as $author) {
    $this­>message­>sendTo($author, $totals);
    }
    public function notify(DateInterval $dateInterval): void
    1
    {
    2
    $posts = $this­>postsRepository
    3
    ­>findByDateInterval($dateInterval);
    4
    5
    foreach ($posts as $post) {
    6
    7
    8
    9
    10
    }
    11
    }
    12

    View Slide

  106. $this­>notifyAuthors($post);
    private function notifyAuthors(Post $post): void
    {
    $totals = $this­>report
    ­>calculateTotals($post);
    foreach ($post­>authors() as $author) {
    $this­>message
    ­>sendTo($author, $totals);
    }
    }
    public function notify(DateInterval $dateInterval): void
    1
    {
    2
    $posts = $this­>postsRepository
    3
    ­>findByDateInterval($dateInterval);
    4
    5
    foreach ($posts as $post) {
    6
    7
    }
    8
    }
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    View Slide

  107. REGRA 2
    Não use ELSE

    View Slide

  108. MOTIVOS
    Porque else não serve para nada
    Porque você não precisa do else
    Na verdade você nunca precisou do else

    View Slide

  109. private function authorizeUser(User $user): bool
    {
    if ($user­>isAdmin()) {
    $result = true;
    } else {
    $result = false;
    }
    return $result;
    }
    private function authorizeUser(User $user): bool
    {
    if ($user­>isAdmin()) {
    return true;
    }
    return false; // Ou só return $user­>isAdmin()
    }

    View Slide

  110. private function response(Request $request): Response
    {
    if ($request­>expectsJson()) {
    $response = $this­>createJsonResponse();
    } else {
    $response = $this­>createHtmlResponse();
    }
    return $this­>formatResponse($response);
    }

    View Slide

  111. private function response(Request $request): Response
    {
    $response = $this­>buildFromRequest($request);
    return $this­>formatResponse($response);
    }
    private function buildFromRequest(Request $request): Response
    {
    if ($request­>expectsJson()) {
    return $this­>createJsonResponse();
    }
    return $this­>createHtmlResponse();
    }

    View Slide

  112. REGRA 3
    Envolva seus tipos primitivos

    View Slide

  113. MOTIVOS
    Tipos primitivos são muito permissivos
    Não possuem nenhum significado/comportamento
    Encapsulamento
    Value Objects em DDD

    View Slide

  114. public function charge(int $amount, string $currency): string
    {
    $this­>paymentProvider­>chargeUser(
    $this­>user­>id(), $amount, $currency

    return $this­>paymentProvider
    ­>confirmationNumber();
    }

    View Slide

  115. class Money
    {
    private int $amount;
    private Currency $currency;
    public function __construct(int $amount, string $currency)
    {
    $this­>amount = $amount;
    $this­>currency = new Currency($currency);
    }
    public function amount(): int
    {
    return $this­>amount;
    }
    public function currencyAbbreviation(): string
    {
    return $this­>currency
    ­>abbreviation();
    }
    }

    View Slide

  116. class Currency
    {
    private string $abbreviation;
    public function __construct(string $abbreviation)
    {
    if (!$this­>abbreviationIsValid($abbreviation)) {
    throw new InvalidCurrencyAbbreviationException;
    }
    $this­>abbreviation = strtoupper($abbreviation);
    }
    public function abbreviation(): string
    {
    return $this­>abbreviation;
    }
    // Code
    }

    View Slide

  117. public function charge(Money $price): string
    {
    $this­>paymentProvider­>chargeUser(
    $this­>user­>id(),
    $price­>amount(),
    $price­>currencyAbbreviation(),

    return $this­>paymentProvider
    ­>confirmationNumber();
    }

    View Slide

  118. REGRA 4
    Envolva suas collections em classes

    View Slide

  119. MOTIVOS
    Te força a usar SRP
    Agrupa ações semelhantes na mesma classe

    View Slide

  120. CONCEITO
    Manipular objectos similares em um só lugar
    Somente uma propriedade por classe collection
    Métodos realizam ações no conjunto de objectos

    View Slide

  121. class ActiveUsersCollection implement Iterator
    {
    private array $users = [];
    public function __construct(array $users)
    {
    $this­>users = $users;
    }
    public function filterByActivationProcess(): self
    {
    // Code
    }
    }

    View Slide

  122. REGRA 5
    Use apenas um ponto (­>) por linha

    View Slide

  123. MOTIVOS
    Facilita a leitura
    Lei de Demeter (fale com amigos)
    Reforça o Open-close principle

    View Slide

  124. EXCEÇÕES
    Fluent Interfaces / Chaining Pattern
    $builder­>where(...)­>order(...)­>limit(...)­>get()

    View Slide

  125. class RemoteCompany
    {
    private CountriesCollection $countries;
    public function addCountries(EmployeesCollection $employees): void
    {
    foreach ($employees as $employee) {
    $this­>countries­>add(
    $employee­>address­>country­>code();
    )
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    View Slide

  126. class Employee
    {
    public function addToCollection(CountriesCollection $countries): void
    {
    $countries­>add(
    $this
    ­>address
    ­>addToCollection($countries)

    }
    }

    View Slide

  127. class RemoteCompany
    {
    private CountriesCollection $countries;
    public function addCountries(EmployeesCollection $employees): void
    {
    foreach ($employees as $employee) {
    $employee­>addToCollection(
    $this­>countries

    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    View Slide

  128. REGRA 6
    Não abrevie

    View Slide

  129. MOTIVOS
    Aumentar entendimento do contexto
    Porque abreviação é uma merda
    Porque eu não sou obrigado a entender

    View Slide

  130. View Slide

  131. EXEMPLOS
    $cfClient quer dizer $cloudflareClient
    $rsp quer dizer $requestServiceProvider
    $mgSrv quer dizer $mongoService

    View Slide

  132. DICA
    Nomes estão ficando muito grandes?
    Reveja SRP!
    Sua classe/método pode estar fazendo coisa demais

    View Slide

  133. REGRA 7
    Mantenha todas as classes pequenas

    View Slide

  134. MOTIVOS
    Mais fáceis de manter
    SRP
    Porque classe grande é uma merda

    View Slide

  135. REGRINHAS
    Classes com até 50 linhas 150 linhas (PHP)
    Pacotes com no máximo 10 arquivos

    View Slide

  136. REGRA 8
    Não tenha mais que duas variáveis de instância em
    sua classe

    View Slide

  137. View Slide

  138. MOTIVOS
    SRP
    Porque classe grande é uma merda

    View Slide

  139. View Slide

  140. class Customer
    {
    private Name $name;
    private int $customerId;
    }
    class Name
    {
    private string $firstName;
    private string $lastName;
    }

    View Slide

  141. Não seja um extremista!
    para lógica de domínio
    para representação de dados (Entity)

    View Slide

  142. REGRA 9
    Não use getters ou setters
    A galera de Java pira

    View Slide

  143. View Slide

  144. MOTIVOS
    Podem ferir SRP / Encapsulamento
    Regra: Tell, don't ask!
    Não possuem significado algum
    "Immutable classes"
    pra accessors (sem tomada de decisão)

    View Slide

  145. class Company
    {
    private EmployeesCollection $employees;
    public function setEmployees(EmployeesCollection $employees): void
    {
    $this­>employees = $employees;
    }
    }

    View Slide

  146. class Company
    {
    private EmployeesCollection $employees;
    public function hireEmployee(Employee $employee): void
    {
    $this­>employees­>add($employee)
    }
    }

    View Slide

  147. class Exam
    {
    private int $total;
    public function setTotal(int $total): void
    {
    $this­>total = $total;
    }
    }

    View Slide

  148. class Exam
    {
    private int $total;
    public function addCorrectQuestion(Question $question): void
    {
    $this­>total += $question­>total();
    }
    }

    View Slide

  149. Getters/Setters. Evil. Period.
    Yegor Bugayenko
    https://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html

    View Slide

  150. ALGUMAS VERDADES
    Lemos mais código que escrevemos
    Escrever bem é difícil
    Exige prática
    Requer tempo

    View Slide

  151. Você escreve para outro dev ler
    Você pode ser esse outro dev
    Devs são burros preguiçosos
    Incluindo eu e você
    Previna problemas com devs

    View Slide

  152. PRÓXIMOS PASSOS
    Hexagonal Architecture / DDD (abstrações)
    SOLID / Clean Code

    View Slide

  153. Domain-Driven Design in PHP
    Carlos Buenosvinos, Christian Soronellas, and Keyvan Akbary
    https://leanpub.com/ddd-in-php

    View Slide

  154. OBRIGADO!

    View Slide

  155. PHP DEFENSIVO
    16 dicas para prevenir problemas futuros
    (internamente)

    View Slide

  156. REFERÊNCIA
    Extremely Defensive PHP
    Marco Pivetta
    PHPSW: Coding Practices, June 2015
    @Ocramius
    https://www.youtube.com/watch?v=8d2AtAGJPno

    View Slide

  157. OBJETIVOS
    Evitar problemas a nível de código
    Assume que devs irão errar em algum momento
    Previnir o impacto destes erros
    Amenizar impacto direto na Arquitetura

    View Slide

  158. Direção Defensiva
    Programação Defensiva

    View Slide

  159. REGRAS
    Nunca confie no código de outra pessoa
    Nunca confie no seu código
    (sua forma de programar muda ao longo do tempo)

    View Slide

  160. POKA-YOKE
    "...evitar a ocorrência de defeitos em processos de
    fabricação e/ou na utilização de produtos."
    (exemplo do carro automático)

    View Slide

  161. CONSIDERAÇÕES
    Essas práticas são excelentes:
    a longo prazo
    trabalhando em equipe
    para projetos open-source

    View Slide

  162. REFLEXÃO
    "Código não é reutilizável. Abstrações são reutilizáveis."
    Marco Pivetta (@Ocramius)

    View Slide

  163. #1 - INTERFACIFICATION
    Abstraia tudo que possa ser compartilhado

    View Slide

  164. Abstrações / Interfaces
    Contratos
    Illuminate\Contracts\Foo

    View Slide

  165. PSR-7: HTTP MESSAGE INTERFACES
    Psr\Http\Message\RequestInterface
    Psr\Http\Message\ResponseInterface

    View Slide

  166. #2 - IMUTABILIDADE
    Evite alterar o estado de um objeto após sua
    inicialização

    View Slide

  167. Value Objects em DDD
    Command Handler pattern

    View Slide

  168. final class ListAllBookingsCommand
    {
    private string $branchId;
    private Carbon $startDate;
    private Carbon $endDate;
    public function __construct(
    string $branchId,
    Carbon $startDate,
    Carbon $endDate
    ) {
    $this­>branchId = $branchId;
    $this­>startDate = $startDate;
    $this­>endDate = $endDate;
    }
    // Getters
    }

    View Slide

  169. #3 - NO SETTERS
    Setters mudam o comportamento de uma classe em
    tempo de execução
    (+1 ponto de falha / testes)

    View Slide

  170. OOP se resume à comunicação entre objetos
    (envio de mensagens / comportamentos)
    (substantivo / verbo)

    View Slide

  171. Dependências somente no construtor!
    (seu DI Container agradece!)

    View Slide

  172. #4 - NAMED CONSTRUCTORS
    Dê significado quando construir um objeto
    new Foo()?

    View Slide

  173. CARBON
    (muito útil quando se possui muitos parâmetros no construtor)
    mocks / dependências
    $date = new Carbon(/* what? */);
    $date = Carbon::createFromTimestamp(1559763294);
    $date = Carbon::createFromDatetime($dateTime);
    $date = Carbon::parse('2019­06­07 09:34:19');

    View Slide

  174. class ColorTransformer
    {
    private function __construct(string $hexadecimal) {}
    public static function createFromRgb(
    int $red,
    int $green,
    int $blue
    ): self {
    // convert from RGB to HEX
    return new self($hexadecimal);
    }
    public static function createFromHexadecimal() {}
    public static function createFromHsl() {}
    public static function createFromCmyk() {}
    public function toRgb(): RgbColor {}
    public function toHsl(): HslColor {}
    public function toCmyk(): CmykColor {}
    public function toHexadecimal(): HexadecimalColor {}
    }

    View Slide

  175. #5 - DEPENDÊNCIA OPCIONAL JAMAIS
    Conheça e use Null/Fake Objects

    View Slide

  176. class UserLogin
    {
    private Logger $logger;
    public function setLogger(Logger $logger = null)
    {
    $this­>logger = $logger;
    }
    }

    View Slide

  177. class UserLogin
    {
    private Logger $logger;
    public function __construct(Logger $logger)
    {
    //
    }
    }
    $userLogin = new UserLogin(new FakeLogger());

    View Slide

  178. #6 - EVITE RETORNAR NULL
    Use Null Objects
    (evita vários ifs + favorece exceptions)

    View Slide

  179. class NullLogger implements LoggerInterface
    {
    public function log(string $str): void
    {
    // do nothing
    }
    }

    View Slide

  180. #7 - PUBLIC METHODS
    Evite métodos públicos desnecessários
    (melhor caso é sempre 1 por classe!)
    regra do substantivo / verbo

    View Slide

  181. Stefan Priebsch ( ) |
    "A public method is like a child: once you've written it, you are
    going to maintain it for the rest of its life!"
    @spriebsch thePHP.cc

    View Slide

  182. #8 - BOOLEAN COMO PARÂMETRO
    Evite utilizar variáveis boolean como parâmetro de
    método
    (+1 caso de teste! SEMPRE!)

    View Slide

  183. public function delete(User $user, bool $softDelete = true): bool
    {
    // code
    if ($softDelete) {
    // code
    }
    }
    public function delete(User $user): bool
    {
    // code
    }
    public function softDelete(User $user): bool
    {
    // code
    }

    View Slide

  184. nº parâmetros boolean * 2 =
    # de test cases para o método

    View Slide

  185. #9 - ENCAPSULE ESTADOS
    Tome cuidado com objetos que representam estados

    View Slide

  186. class MoneyTransfer
    {
    public function __construct(
    Money $amount,
    DateTime $transferDate
    ) {
    $this­>amount = $amount;
    $this­>transferDate = clone $transferDate;
    }
    }
    class MoneyTransfer
    {
    public function __construct(
    Money $amount,
    DateTimeImmutable $transferDate
    ) {
    $this­>amount = $amount;
    $this­>transferDate = $transferDate;
    }
    }

    View Slide

  187. #10 - FINAL CLASSES POR PADRÃO
    "Composition over inheritance"

    View Slide

  188. COMO NÃO FAZER?
    class Db { /* ... */ }
    class Core extends Db { /* ... */ }
    class User extends Core { /* ... */ }
    class Admin extends User { /* ... */ }
    class Bot extends Admin { /* ... */ }
    class BotThatDoesSpecialThings extends Bot { /* ... */ }
    class PatchedBot extends BotThatDoesSpecialThings { /* ... */

    View Slide

  189. Não permita herança por padrão
    Se realmente necessário você vai voltar atrás
    Composition pode ser usada a qualquer momento
    Herança só é realmente necessária em poucos casos

    View Slide

  190. QUANDO REALMENTE APLICAR
    A final class implementa uma interface
    Todos os métodos públicos da final class são parte da
    interface
    Abstração sempre!
    (mocking final classes)

    View Slide

  191. LEMBRETE!
    Você pode extender interfaces
    interface Foo extends Bar {}

    View Slide

  192. #11 - PRIVATE POR PADRÃO
    Só exponha o que é realmente necessário!
    (private + public)

    View Slide

  193. protected é desnecessário com final class
    Propriedades são geralmente dependências
    Na maioria das vezes não é necessário expor dependências
    (Object Calisthenics #5: one dot per line!)
    Métodos public são ações/transações!

    View Slide

  194. COMO NÃO FAZER?
    public function addMoney(Money $money): void
    {
    $this­>money[] = $money;
    }
    public function markBillPaidWithMoney(Bill $bill, Money $money): void
    {
    $this­>bills[] = $bill­>paid($money);
    }
    $bankAccount­>addMoney($money);
    $bankAccount­>markBillPaidWithMoney($bill, $money);

    View Slide

  195. Métodos públicos são ações!
    public function payBill(Bill $bill, Money $money): void
    {
    $this­>addMoney($money);
    $this­>markBillPaidWithMoney($bill, $money);
    }
    private function addMoney(Money $money): void
    {
    $this­>money[] = $money;
    }
    private function markBillPaidWithMoney(Bill $bill, Money $money): void
    {
    $this­>bills[] = $bill­>paid($money);
    }
    $bankAccount­>payBill($bill, money);

    View Slide

  196. #12 - NÃO ASSUMA "IDEMPOTÊNCIA"
    Chamadas consecutivas podem se alterar!

    View Slide

  197. "Em matemática e ciência da computação, a idempotência é a propriedade que
    algumas operações têm de poderem ser aplicadas várias vezes sem que o valor
    do resultado se altere após a aplicação inicial." Wikipedia
    (convenhamos, isso é feio pra bosta ne!)
    $userId = $controller­>request()­>get('userId');
    $userRoles = $controller­>request()­>get('userRoles');
    $request = $controller­>request();*
    $userId = $request­>get('userId');
    $userRoles = $request­>get('userRoles');

    View Slide

  198. #13 - NÃO USE TIPOS mixed
    declare(strict_types=1) por padrão

    View Slide

  199. Se pode ser muita coisa, encapsule!
    /**
    * @param mixed $identifier ID, e­mail address or phone number
    * @param string $password The provided password
    * @return bool
    */
    public function login($identifier, $password): bool
    {
    // ...
    }

    View Slide

  200. public function login(UserIdentifier $identifier, string $password): bool
    {
    // ...
    }

    View Slide

  201. Mas ainda pode melhorar!

    View Slide

  202. #14 - USE VALUE OBJECTS
    Use e abuse de objetos!!!

    View Slide

  203. final class PasswordChanger
    {
    private UserRepository $userRepository;
    public function change(
    UserIdentifier $identifier,
    Password $password
    ): bool {
    // ...
    }
    }
    class Password
    {
    private string $password;
    public function __construct(string $password)
    {
    $this­>validate($password);
    $this­>password = $password;
    }
    }

    View Slide

  204. Value Objects encapsulam validação!
    (garantia de consistência centralizada)

    View Slide

  205. #15 - TOME CUIDADO COM Trait
    São ótimas, mas você pode se encrencar!

    View Slide

  206. Em PHP Trait é
    Ctrl + C / Ctrl + V *
    Aumenta muito o acoplamento!
    As vezes DRY não é o melhor caso!

    View Slide

  207. Aumenta a dependência da Trait
    (nem tudo deve ser "compartilhável")

    View Slide

  208. #16 - TESTE TODOS OS CENÁRIOS
    Você tem certeza que testou tudo?

    View Slide

  209. public function foo(): Identifier
    {
    if (/**/) {
    $this­>bar(); // 1
    }
    if (/**/) {
    return $this­baz(); // 2
    }
    return $this­>bar(); // 3
    }

    View Slide

  210. Dependendo do método vai ser foda!
    Tendência a diminuir a complexidade!
    (basicamente fazer menos coisas, e isso é bom!)
    Testou o caso da Exception?
    Testou o retorno true e também o false?

    View Slide

  211. Não seja um extremista
    #ficaadica

    View Slide

  212. OBRIGADO!

    View Slide

  213. ARQUITETURA DE SOFTWARE
    o básico não tão básico que você precisa saber

    View Slide

  214. AGENDA
    Arquitetura & Design
    Screaming Architecture
    Clean Architecture
    Primeiros Passos

    View Slide

  215. ARQUITETURA & DESIGN

    View Slide

  216. ARQUITETURA
    Visão em alto nível
    "Arquitetura de uma casa"
    Conexões e Comunicação de modo geral

    View Slide

  217. DESIGN
    Estruturas e decisões em baixo nível
    "Estilo dos móveis de uma casa"
    Design Patterns

    View Slide

  218. Robert C. Martin
    "O objetivo da arquitetura de so ware é
    minimizar os recursos humanos necessários para
    construir e manter um sistema."

    View Slide

  219. DESENVOLVIMENTO
    So ware difícil de manter tem vida curta
    Arquitetura facilita desenvolvimento para o time que o mantém

    View Slide

  220. Uma boa arquitetura começa com bons acordos
    Decisões em conjunto (time!)
    Comunicação é fundamental
    Mudar arquitetura é muito caro!

    View Slide

  221. MATURIDADE
    Às vezes grandes projetos começam pequenos
    Zero (ou pouca) arquitetura
    Problemas de arquitetura aparecem bem depois

    View Slide

  222. OBJETIVOS
    Fácil desenvolvimento / baixo custo
    Publicação rápida e fácil
    Diminuir custos com manutenção (custo mais caro)

    View Slide

  223. ARQUITETURA EM CAMADAS
    "Deixe opções em aberto"
    Camadas independentes
    Casos de uso independentes
    Comandos independentes

    View Slide

  224. SCREAMING ARCHITECTURE

    View Slide

  225. "Arquitetura fala por si só"
    Sobre o que é seu so ware? Olhando código!
    Leilão? Loja virtual? Reserva de hotéis?
    Ou uma aplicação Laravel? Zend? Symfony?

    View Slide

  226. "Uma boa arquitetura de so ware permite que decisões sobre frameworks,
    banco de dados, servidores web, e outros detalhes de ferramentas de
    ambiente, fiquem pra depois."
    Uncle Bob
    "Frameworks são opções a serem
    deixadas em aberto."

    View Slide

  227. QUAL O FOCO?
    "Uma boa arquitetura enfatiza os casos de uso e
    os dissocia de preocupações periférias."

    View Slide

  228. A Web é uma arquitetura?

    View Slide

  229. Não! Web é o mecanismo de entrega
    Sua arquitetura não deve se preocupar com entrega
    A Web é apenas um detalhe

    View Slide

  230. SOBRE TESTES

    View Slide

  231. Mais fácil escrever (e manter) "unit tests" em arquitetura
    totalmente baseada em casos de uso.

    View Slide

  232. PARA TESTAR:
    Você não precisa de framework
    Você não precisa de servidor web
    Você não precisa de banco de dados

    View Slide

  233. Lembrete: seus casos de uso são desacoplados!
    Seus casos de uso coordenam suas classes Entity.
    "Seus objetos Entity devem ser objetos simples que
    não possuem dependência nenhuma com frameworks
    ou bancos de dados."

    View Slide

  234. "Sua arquitetura deve dizer aos leitores sobre o
    sistema, não sobre o framework usado no
    sistema."

    View Slide

  235. CLEAN ARCHITECTURE

    View Slide

  236. ARQUITETURAS MAIS CONHECIDAS
    Domain-Driven Design (DDD)
    Hexagonal Architecture

    View Slide

  237. DOMAIN-DRIVEN DESIGN
    Implementing Domain-Driven Design
    (Vaughn Vernon)

    View Slide

  238. HEXAGONAL ARCHITECTURE
    "Hexagonal Architecture - Message-Oriented So ware Design"
    (Matthias Noback)
    https://youtu.be/K1EJBmwg9EQ

    View Slide

  239. CARACTERÍSTICAS COMUNS
    Independente de frameworks (framework-as-a-tool)
    Testável sem qualquer elemento externo
    Independente da UI (Web / Console)
    Independente de banco de dados
    Independente de qualquer agente externo

    View Slide

  240. "INDEPENDÊNCIA"

    View Slide

  241. https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

    View Slide

  242. THE DEPENDENCY RULE
    "Dependências de código devem apontar somente para dentro, na direção de
    políticas de nível mais alto."
    Nada em um círculo interno pode saber alguma coisa concreta
    sobre algo em um círculo externo.

    View Slide

  243. THE DEPENDENCY RULE
    (AS 4 CAMADAS)
    1. Entities (Enterprise Business Rules)
    2. Use Cases (Application Business Rules)
    3. Interface Adapters
    4. Frameworks and Drivers

    View Slide

  244. 1. ENTITIES
    Encapsulamento de "Regras de Negócio" críticas
    Conjunto de classes e métodos
    Estes objetos não devem ser afetados por mudanças de navegação de página
    ou segurança. Nenhuma mudança operacional deve afetar as entidades.

    View Slide

  245. 2. USE CASES
    Regras de Negócio específicas da aplicação
    Comandam o fluxo de dados
    ("de" e "para" as entidades)
    Mudanças nesta camada não devem afetar as entidades.
    (CommandHandler Pattern)

    View Slide

  246. 3. INTERFACE ADAPTERS
    Conversão de dados externos para camadas internas
    ("Use Cases" e "Entities" - e vice-versa)
    MVC (Model, View, Controller)
    Ex: códigos SQL
    Nenhum código de camadas mais internas deve saber algo
    de banco de dados, por exemplo.

    View Slide

  247. 4. FRAMEWORKS AND DRIVERS
    Geralmente não se escreve código nesta camada
    Camada onde vão todos os detalhes
    ("Web é um detalhe", "banco de dados é um detalhe")
    Do lado de fora estes detalhes não podem fazer muito estrago.
    Uncle Bob
    "Não case com o framework!"

    View Slide

  248. ATRAVESSANDO LIMITES
    Usando Dependency Inversion (SOLID)
    Exemplo: um caso de uso precisa chamar um "Presenter", mas se chamar
    diretamente violaria a "Dependency Rule" - nada de dentro deve saber sobre
    algo de fora. O caso de uso deve chamar uma interface (abstração) ("use case
    output port" - figura)

    View Slide

  249. class ListActiveUsersHandler
    {
    private ViewRendererInterface $viewRenderer;
    public function __construct(ViewRendererInterface $viewRenderer)
    {
    $this­>viewRenderer = $viewRenderer;
    }
    public function handle(ListActiveUsersCommand $command): string
    {
    return $this­>viewRenderer
    ­>render('foo', [
    'data' => $command­>getData(),
    ]);
    }
    }

    View Slide

  250. ATRAVESSANDO LIMITES
    Exemplo do que não se deve fazer
    Consulta ao ORM retorna um row object, não a representação do seu domínio.
    Você não pode passar esse row object para dentro do círculo, pois as camadas
    internas não devem saber nada sobre objetos das camadas de fora (BD).
    Doctrine ORM vs Eloquent ORM (Laravel)
    DTO (Data Transfer Object)

    View Slide

  251. POR ONDE COMEÇAR?
    Command & CommandHandler Pattern
    Command seria um "DTO"
    Handler seria o "Use Case"

    View Slide

  252. Command é um objeto simples imutável, que contem todos
    os inputs necessários para executar o "Handler"
    Handler é a execução do comando, com suas próprias
    dependências. Ele sabe que precisa do Command para
    processar.

    View Slide

  253. Command para Handler

    View Slide

  254. USE CASE: CREATE USER
    CreateUserCommand / CreateUserHandler

    View Slide

  255. 1. CONTROLLER ACTION
    POST /users
    UsersController@create()

    View Slide

  256. final class CreateUserAction
    {
    private CreateUserHandler $handler;
    public function __construct(CreateUserHandler $handler)
    {
    $this­>handler = $handler;
    }
    public function __invoke(RequestInterface $request): ResponseInterface
    {
    $user = $this­>handler­>handle(
    new CreateUserCommand(
    $request­>getAttribute('email'),
    $request­>getAttribute('firstName'),
    $request­>getAttribute('lastName'),
    $request­>getAttribute('password'),
    )

    return new JsonResponse($user, 201);
    }
    }

    View Slide

  257. 2. COMMAND
    Classe simples que guarda os dados necessários para
    execução da ação.
    Classe imutável!

    View Slide

  258. final class CreateUserCommand implements CommandInterface
    {
    private string $email;
    private string $firstName;
    private string $lastName;
    private string $password;
    public function __construct(
    string $email,
    string $firstName,
    string $lastName,
    string $password
    ) {
    $this­>email = $email;
    $this­>firstName = $firstName;
    $this­>lastName = $lastName;
    $this­>password = $password;
    }
    // Getters: getEmail(), getFirstName(), getLastName(), getPassword()
    }

    View Slide

  259. 3. HANDLER
    Caso de uso propriamente dito
    Recebe o Command com todos os dados necessários

    View Slide

  260. final class CreateUserHandler implements HandlerInterface
    {
    private UserRepositoryInterface $userRepository;
    private PasswordEncrypterInterface $passwordEncrypter;
    public function __construct(
    UserRepositoryInterface $userRepository,
    PasswordEncrypterInterface $passwordEncrypter
    ) {
    $this­>userRepository = $userRepository;
    $this­>passwordEncrypter = $passwordEncrypter;
    }
    public function handle(CreateUserCommand $command): UserEntity
    {
    return $this­>userRepository­>create([
    'email' => $command­>getEmail(),
    'firstName' => $command­>getFirstName(),
    'lastName' => $command­>getLastName(),
    'password' => $this­>passwordEncrypter($command­>getPassword()),
    ]);
    }
    }

    View Slide

  261. PRÓXIMO PASSO
    Adicionar um CommandBus
    (responsável por executar os Handlers)
    composer require league/tactician

    View Slide

  262. INDICAÇÃO DE LIVRO
    Clean Architecture
    (Robert C. Martin)

    View Slide

  263. Grossi, Junior
    Estudar arquitetura de so ware vai te
    mostrar que mágicas não existem!

    View Slide

  264. OBRIGADO!

    View Slide

  265. SOLID PRINCIPLES
    entenda antes de tentar fazer

    View Slide

  266. AGENDA
    Introdução
    Objetivos do SOLID
    Os princípios
    Conclusão

    View Slide

  267. INTRODUÇÃO
    Bom so ware começa com código limpo
    Na construção de uma casa / so ware:
    Tijolos ruins implicam em código ruim
    Código ruim pode ser escrito mesmo com tijolos bons

    View Slide

  268. SOLID
    Atua "no tijolo" e na "construção da casa"
    "Os SOLID Principles nos dizem como organizar nossas funções e
    estruturas de dados em classes, e como essas classes devem ser
    conectadas."
    -- Robert C. Martin (Uncle Bob)

    View Slide

  269. Em outras palavras...
    "Os "SOLID Principles" nos garante uma base firme a longo prazo e
    nos previne de fazer merda mesmo com essa base firme."
    -- Junior Grossi

    View Slide

  270. ASPECTOS DO SOLID
    Implicação na qualidade do código
    Maior implicação na arquitetura (nosso foco)
    Foco a longo prazo da aplicação de SOLID

    View Slide

  271. "SOLID é uma excelente forma de design, que possui
    impacto direto na qualidade da arquitetura de so ware
    adotada. Ele se encaixa perfeitamente nos padrões
    atuais de arquitetura de so ware."
    -- Junior Grossi

    View Slide

  272. SOLID DDD (Domain-Driven Design)
    SOLID Hexagonal Architecture
    SOLID CommandHandler Pattern
    SOLID CQRS / Event Sourcing

    View Slide

  273. OBJETIVOS
    Criação de estruturas de so ware que:
    Toleram mudanças facilmente
    São fáceis de entender
    São reutilizáveis

    View Slide

  274. UM POUCO DE HISTÓRIA
    SOLID foi consolidado pelo Uncle Bob
    Eles (princípios) começaram a ser "montados" na
    década de 1980 (diferentes autores)
    Foram ordenados em uma outra ordem (ser ser SOLID)
    Em 2004 Michael Feathers enviou um e-mail ao Uncle Bob dizendo que
    aqueles princípios, se reordenados, formariam a palavra SOLID.

    View Slide

  275. SOLID é o objetivo final a ser alcançado
    Exige prática, paciência e perseverança

    View Slide

  276. OS PRINCÍPIOS SOLID
    (foco na arquitetura)

    View Slide

  277. SRP
    The Single Responsibility Principle
    (S de SOLID)

    View Slide

  278. "Uma classe deve ter somente uma razão para mudar."
    Robert C. Martin, 2005

    View Slide

  279. ATENÇÃO
    É um dos princípios mais conhecidos
    É um dos princípios menos aplicados
    É talvez o princípio menos entendido

    View Slide

  280. "Of all the SOLID principles, the Single Responsibility Principle (SRP)
    might be the least well understood. That's likely because it has a
    particularly inappropriate name. It is too easy for programmers to
    hear the name and then assume that it means that every module
    should do just one thing."
    Robert C. Martin (Uncle Bob)

    View Slide

  281. Historicamente, o SRP tem sido descrito como:
    "A module should have one, and only one, reason to change."

    View Slide

  282. Os usuários e stakeholders são o motivo de mudança:
    "A module should be responsible to one, and only one, user or stakeholder."

    View Slide

  283. Eles possuem visões diferentes da mesma coisa:
    "A module should be responsible to one, and only one, actor."

    View Slide

  284. SINTOMAS DE VIOLAÇÃO

    View Slide

  285. SINTOMA 1
    Duplicação Acidental

    View Slide

  286. class Employee
    {
    public function calculatePay() {}
    public function reportHours() {}
    public function save() {}
    }

    View Slide

  287. Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

    View Slide

  288. PROBLEMAS
    Os autores estão totalmente acoplados
    Time do CFO pode afetar algo do time do COO

    View Slide

  289. Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

    View Slide

  290. CONSEQUÊNCIAS
    Time CFO altera lógica do regularHours()
    O método também é usado pelo COO em
    reportHours()

    View Slide

  291. "O SRP diz para separar os códigos que são
    dependentes de diferentes atores."

    View Slide

  292. SINTOMA 2
    Merges

    View Slide

  293. Dois devs diferentes, de diferentes times, alterando
    comportamento da classe Employee.

    View Slide

  294. Diferentes comportamentos na mesma classe
    Resultado: merge
    Muitas pessoas mudando o mesmo código
    "separar códigos que suportam diferentes atores"

    View Slide

  295. SOLUÇÃO?
    Separar ações compartilhadas em classes
    Regra do "substantivo + verbo" + final classes
    Composition over inheritance (ou composite reuse principle)

    View Slide

  296. Quando declarar uma classe como final?
    Quando ela tiver apenas um método público
    Provavelmente ela vai implementar alguma interface

    View Slide

  297. SOLUÇÃO?

    View Slide

  298. Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

    View Slide

  299. Agrupar as responsabilidades em Facade pattern
    Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

    View Slide

  300. DICA PARA SRP
    Um método público por classe
    Este método faz alguma coisa (ação/verbo)
    Abstração mais fácil (Dependency Inversion)

    View Slide

  301. OCP
    The Open-Close Principle
    (O de SOLID)

    View Slide

  302. "Um artefato de so ware deve ser aberto para extensão mas
    fechado para modificação."
    Bertrand Meyer, 1988

    View Slide

  303. Adicionar algo não implica em mudar algo
    Adicionar comportamentos
    Separação de acordo com SRP

    View Slide

  304. EXTRATO BANCÁRIO
    Home-banking: página HTML do extrato
    (números negativos em vermelho)

    View Slide

  305. Adicionar exportação em PDF para impressão P&B
    (números negativos em vermelho não vão aparecer)
    Primeiro pensamento? Adicionar um if
    if ($source === 'report') {
    $number = '(' . $number . ')';
    } else {
    $number = '' . $number . '';
    }

    View Slide

  306. Você alterou a classe para adicionar um
    comportamento

    View Slide

  307. "Uma boa arquitetura de so ware reduziria a
    quantidade de código alterado para próximo do
    mínimo. O ideal seria zero."

    View Slide

  308. OCP (funções diferentes para diferentes ações)
    (SRP em conjunto)
    StatementDataGenerator (dados)
    StatementHtmlDecorator (cor vermelha)
    StatementReportDecorator (parênteses)

    View Slide

  309. Dependency Inversion

    View Slide

  310. class StatementGenerator
    {
    private RedColorFormatter $formatter;
    public function __construct(RedColorFormatter $formatter)
    {
    $this­>formatter = $formatter;
    }
    public function generate(): string
    {
    return $this­>formatter­>format(
    $this­>getData()

    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    View Slide

  311. public function __construct(RedColorFormatter $formatter)
    {
    $this­>formatter = $formatter;
    }
    class StatementGenerator
    1
    {
    2
    private RedColorFormatter $formatter;
    3
    4
    5
    6
    7
    8
    9
    public function generate(): string
    10
    {
    11
    return $this­>formatter­>format(
    12
    $this­>getData()
    13

    14
    }
    15
    }
    16

    View Slide

  312. Como mudar o "formatter" em runtime?
    Dependency Inversion

    View Slide

  313. public function __construct(FormatterInterface $formatter)
    {
    $this­>formatter = $formatter;
    }
    class RedColorFormatter implements FormatterInterface {}
    class ParenthesesFormatter implements FormatterInterface {}
    class NegativeSignFormatter implements FormatterInterface {}

    View Slide

  314. // HTML (Web)
    $generator = new StatementGenerator(
    new RedColorFormatter()

    // Report
    $generator = new StatementGenerator(
    new ParenthesesFormatter()

    View Slide

  315. SRP OCP DIP

    View Slide

  316. LSP
    The Liskov Substitution Principle
    (L de SOLID)

    View Slide

  317. "Se q(x) é uma propriedade demonstrável dos objetos x de tipo T.
    Então q(y) deve ser verdadeiro para objetos y de tipo S onde S é um
    subtipo de T."
    Barbara Liskov, 1988

    View Slide

  318. View Slide

  319. "Uma classe base deve poder ser substituída pela sua
    classe derivada."

    View Slide

  320. Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

    View Slide

  321. class Billing
    {
    private LicenseInterface $licence;
    public function __construct(LicenseInterface $license)
    {
    $this­>license = $license;
    }
    public function execute()
    {
    return $this­>license­>calcfee(
    $this­>fooBar()

    }
    class PersonalLicense implements LicenseInterface {}
    class BusinessLicense implements LicenseInterface {}

    View Slide

  322. O PROBLEMA DO QUADRADO/RETÂNGULO
    (exemplo de violação do LSP)

    View Slide

  323. Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

    View Slide

  324. O problema é o comportamento da classe base, do ponto de
    vista que quem vai usar.
    O usuário espera o comportamento de um retângulo
    (2 lados de medidas diferentes)

    View Slide

  325. "teria de adicionar um if do lado do User pra saber como se
    comportar quando é um retângulo ou quadrado"
    $rectangle = $this­>rectangle;
    $rectangle­>setWidth(5);
    $rectangle­>setHeight(2);
    $this­>assertEquals(10, $r­>area());

    View Slide

  326. LSP + ARQUITETURA
    O conceito de "base" pode ser estendido

    View Slide

  327. EXEMPLO
    Comparador de preços de taxis

    View Slide

  328. Usuário recebe a lista de preços e serviços
    Você "escolhe" um motorista, que possui uma URL
    (no serviços externo)
    purplecab.com/driver/bob

    View Slide

  329. Você sempre envia dados adicionais
    pickupAddress | pickupTime | destinationAddress

    View Slide

  330. PUT purplecab.com/driver/bob
    pickupAddress=foo&
    pickupTime=123456789&
    destinationAddress=bar

    View Slide

  331. View Slide

  332. ISP
    The Interface Segregation Principle
    (I de SOLID)

    View Slide

  333. Talvez o princípio mais simples de entender

    View Slide

  334. "Nenhum cliente deve ser forçado a depender de métodos que
    ele não usa"
    Robert C. Martin, 2002

    View Slide

  335. HISTÓRIA
    Consultoria na Xerox (impressoras)
    Cada impressora tinha uma função (ou mais de uma)
    Classe Job com várias funções
    Dificuldade de manter pequenas mudanças em
    pequenas funções

    View Slide

  336. View Slide

  337. interface Animal {
    public function walk();
    public function fly();
    public function swim();
    }

    View Slide

  338. class Bird implements Animal {
    public function walk() {} // OK
    public function fly() {} // OK
    public function swim() {} // ?
    }
    class Frog implements Animal {
    public function walk() {} // OK
    public function fly() {} // ?
    public function swim() {} // OK
    }
    class Cat implements Animal {
    public function walk() {} // OK
    public function fly() {} // ?
    public function swim() {} // ?
    }

    View Slide

  339. Implicações a nível de linguagem
    (Java - necessidade de recompilar novamente)
    Implicações a nível de arquitetura
    (Adicionando comportamentos desnecessários)
    "gato não voa, nem nada!"

    View Slide

  340. segregation / separação
    interface WalkInterface {
    public function walk();
    }
    interface FlyInterface {
    public function fly();
    }
    interface SwimInterface {
    public function swim();
    }

    View Slide

  341. class Bird implements WalkInterface, FlyInterface {
    public function walk() {}
    public function fly() {}
    }
    class Frog implements WalkInterface, SwimInterface {
    public function walk() {}
    public function swim() {}
    }
    class Cat implements WalkInterface {
    public function walk() {}
    }

    View Slide

  342. Interface? Menor possível!
    (interfaces grandes podem ferir ISP)

    View Slide

  343. DIP
    The Dependency Inversion Principle
    (D de SOLID)

    View Slide

  344. "Os sistemas mais flexíveis são aqueles cujas dependências do
    código fonte são referências apenas à abstrações, não à
    classes concretas"
    Robert C. Martin, 1994

    View Slide

  345. CONCEITOS

    View Slide

  346. DEPENDÊNCIAS
    Classes que são necessárias para uma outra
    Dependency Injection

    View Slide

  347. class ListAllUsersAction
    {
    private MysqlUserRepository $userRepository;
    public function __construct(MysqlUserRepository $userRepository)
    {
    $this­>userRepository = $userRepository;
    }
    public function __invoke(): JsonResponse {}
    }

    View Slide

  348. Dependências somente no construtor!
    Constructor Injection

    View Slide

  349. Setter Injection
    Method Injection
    public function setLogged(Logger $logger): void
    {
    $this­>logger = $logger;
    }
    public function login(Logger $logger): bool
    {
    $logger­>log('User logged in');
    }

    View Slide

  350. ABSTRAÇÕES
    São interfaces, ou contratos, ou qualquer coisa parecida
    Dizem apenas o que as classes concretas devem fazer, não como fazer
    interface UserRepositoryInterface
    {
    public function getAll(): Collection;
    public function find(Uuid $uuid): User;
    }

    View Slide

  351. CLASSES CONCRETAS
    A implementação por si só ("como")
    Podem variar por N motivos
    class MysqlUserRepository implements UserRepositoryInterface
    {
    private $dependency1;
    private $dependency2;
    public function getAll() {}
    public function find(Uuid $uuid): User {}
    }

    View Slide

  352. Mudar interfaces implica em mudar as
    implementações
    Mudar implementações nem sempre implica em
    mudar as interfaces

    View Slide

  353. "Interface são menos voláteis do que implementações"
    "Indeed, good so ware designers and architects work had to reduce the
    volatility of interfaces. They try to find ways to add functionality to
    implementations without making changes to the interfaces."
    Robert C. Martin
    "Code is not reusable, code is crap! Abstractions are reusable."
    Marco Pivetta (@Ocramius)

    View Slide

  354. ALGUMAS REGRAS
    Não referencie classes concretas voláteis (Dependency Injection)
    Não derive de classes concretas voláteis (evite herança)
    Não sobrescreva funções concretas (crie classes abstratas)
    Nunca mencione o nome de nada concreto ou volátil (só pra reforçar)

    View Slide

  355. PROBLEMA
    Criar instâncias de implementações com base em
    abstrações
    (implementações possuem dependências)

    View Slide

  356. SOLUÇÕES
    Dependency Injection Container
    Abstract Factory Pattern

    View Slide

  357. Dependency Injection Container
    $this­>app­>bind(
    UserRepositoryInterface::class,
    function (Application $app) {
    return new MysqlUserRepository(
    new PDO(
    $dsn = sprintf(
    'mysql:dbname=%s;host=%s',
    $app­>config['dbname'],
    $app­>config['dbhost'],
    ),
    $user = $app­>config['dbuser'],
    $password = $app­>config['dbpassword']
    )

    }

    View Slide

  358. class ListAllUsersAction
    {
    private UserRepositoryInterface $userRepository;
    public function __construct(
    UserRepositoryInterface $userRepository
    ) {
    $this­>userRepository = $userRepository;
    }
    public function __invoke(): JsonResponse {}
    }

    View Slide

  359. Abstract Factory Pattern
    "Abstract Factory is a creational design pattern that lets you produce families of
    related objects without specifying their concrete classes."
    https://refactoring.guru/design-patterns/abstract-factory
    https://designpatternsphp.readthedocs.io

    View Slide

  360. class ProductFactory
    {
    private const SHIPPING_COSTS = 50;
    public function createShippableProduct(int $price): Product
    {
    return new ShippableProduct(
    $price, self::SHIPPING_COSTS

    }
    public function createDigitalProduct(int $price): Product
    {
    return new DigitalProduct($price);
    }
    }

    View Slide

  361. CONCLUSÃO
    SOLID não é fácil
    Exige maturidade do time (muito OOP)
    Quase sempre tem interfaces no meio
    Exige um entendimento muito amplo do domínio

    View Slide

  362. SUGESTÃO
    Hexagonal Architecture
    Domain-Driven Design

    View Slide

  363. OBRIGADO!

    View Slide