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.

7bb3a66a199daec275d5ad339724c754?s=128

Junior Grossi

June 07, 2019
Tweet

Transcript

  1. WORKSHOP DE QUALIDADE Qualidade de Código: Um resumo prático do

    que todo dev precisa saber
  2. AVISOS Não dá pra cobrir tudo "Resumo prático" Qualidade envolve

    bastante teoria Experiência vem com o tempo
  3. AVISOS Não existe apenas um jeito certo A prática vai

    te mostrar o seu jeito! Diferente é bom! Faz pensar!
  4. AGENDA MANHÃ 1. Resumão do Clean Code 2. Object Calisthenics

    3. PHP Defensivo
  5. AGENDA TARDE 1. Panorama sobre Arquitetura de So ware 2.

    TDD raiz na prática 3. SOLID com outros olhos
  6. FORMATO Interrompa quando quiser Contribua se puder Pequena pausa de

    10 a 15 min entre temas
  7. CONSIDERAÇÕES Workshop em guias/temas auto-explicativos Temas que mudaram a minha

    visão Garantia Vitalícia da Camaradagem
  8. PARTIU!

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

    livro!
  10. LIVRO Clean Code A Handbook of Agile So ware Cra

    smanship Robert C. Martin (Uncle Bob)
  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
  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."
  13. (bootstrap time!)

  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
  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!
  16. ALGUNS PONTOS Passamos mais tempo lendo código Sucesso relacionado com

    o entendimento (o entendimento diminui ao longo do tempo)
  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."
  18. EXPRESSE INTENÇÃO AO DAR NOMES Use nomes pronunciáveis / Evite

    abreviações
  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"; }
  20. NOMES "BUSCÁVEIS" / CONSTANTES for ($i = 0; $i <

    5; $i++) { // ... } for ($i = 0; $i < WORK_DAYS_PER_WEEK; $i++) { // ... }
  21. EVITE PREFIXOS/SUFIXOS DESNECESSÁRIOS (As pessoas geralmente "aprendem" a ignorar prefixos

    e sufixos muito rapidamente.) $txtFirstName = $controller­>request()­>get('txt_FirstName');
  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
  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!
  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)
  25. NOMES DE MÉTODOS Devem ser verbos Utilize Named Constructors (mais

    significado) Accessors (get), Mutators (set) e predicates (is)
  26. UMA PALAVRA POR CONCEITO Nunca misture palavras de conceitos similares

    fetch, retrieve, get DeviceController, DeviceManager
  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
  28. NÃO ADICIONE CONTEXTO DESNECESSÁRIO Contexto deve ficar no namespace Nome

    de classe deve resumir o que ela faz GSDAMailingAddress, GSDAAccountAddress
  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."
  30. FUNÇÕES/MÉTODOS PEQUENOS Máximo 20 linhas. Se possível menos. Máximo 120

    caracteres por linha. Se possível 80.
  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
  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
  33. "STEPDOWN RULE" Leitura fluente como texto if (!$user = $this­>findUser())

    { // ... } $this­>save($user); $user = $this­>findUser(); if (!$user) { // ... } $this­>save($user);
  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; }
  35. switch Tolerados se aparecerem apenas uma vez Pensar em AbstractFactory

    se mais de uma vez Cuidado para não ferir SRP
  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
  37. ARGUMENTOS Nenhum é sempre melhor 1 ou 2 aceitáveis /

    3 deve ser evitado (aumento do número de argumentos dificulta escrever testes)
  38. "Flag Arguments" nunca (boolean) Deixa claro que faz mais de

    uma coisa Refatore para um método para cada coisa
  39. Encapsule argumentos em objetos (dá mais significado ao argumento) private

    function makeCircle(float $x, float $y): Circle { // ... } private function makeCircle(Point $point): Circle { // ... }
  40. Verbos e palavras-chave (dá mais significado ao argumento) public function

    find(Email $email): User { // ... } public function findUserByEmail(Email $email): User { // ... }
  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; } }
  42. CAPÍTULO 4 Comentários "Don't comment bad code - rewrite it."

    Brian W. Kernighan e P. J. Plaugher
  43. "Truth can only be found in one place: the code.

    Only the code can truly tell you what it does."
  44. None
  45. COMENTÁRIOS ACEITÁVEIS Legal Comments Clarification (formato de datas) Comentários TODO

    Referência a issues (ticket Jira)
  46. CONSIDERAÇÕES Remova comentários que não adicionam nada (docblock) Referências internas

    Críticas ou sugestões
  47. /** * Returns the day of the month. * *

    @return int the day of the month. */ public function getDayOfMonth(): int { return $this­>dayOfMonth; }
  48. None
  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);
  50. CAPÍTULO 5 Formatação Visual importa sim!

  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
  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; }
  53. Indentação. Nem precisa falar, né?

  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."
  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
  56. LEI DE DEMETER "Fale com amigos, não com estranhos." Encapsule

    o máximo possível. Exponha apenas o necessário.
  57. DTO (DATA TRANSFER OBJECTS) Classes puras somente para transferir dados

    Imutável. Somente getters.
  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 }
  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."
  60. Exceptions sempre. Código de erro nunca! Escreva o bloco try/catch

    primeiro (facilita pensar em todas as possíveis exceções)
  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(/**/); }
  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());
  63. NULL RETURN Evite retornar null Dispare exception e trate na

    origem Use Null Objects
  64. NÃO PASSE NULL COMO PARÂMETRO Perde consistência Adiciona complexidade (mais

    ifs)
  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."
  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.
  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!
  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) ); } }
  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)
  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
  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)
  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()); }
  73. CAPÍTULO 10 Classes "Let's talk about Clean Classes"

  74. ENCAPSULAMENTO private por padrão protected quando necessário (herança ou testes)

    Foco sempre em encapsular primeiro! (use abstrações)
  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)
  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!)
  77. COESÃO ENTRE CLASSES Use e abuse de Dependency Injection (favorece

    SRP!) Favorece DRY (Don't Repeat Yourself)
  78. "COMPOSITION OVER INHERITANCE" Mais fácil com classes menores Nova funcionalidade?

    Nova classe + Dependency Injection da anterior!
  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; } }
  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?"
  81. 4 REGRAS DO SIMPLE DESIGN RULE

  82. REGRA 1 RODE TODOS OS TESTES Continuamente (Loop 1, 2,

    3) Incrementalmente (Continuous Integration)
  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!)
  84. REGRA 2: ELIMINE DUPLICIDADE "Duplication is the primary enemy of

    a well-designed system!" Use e abuse de SRP!
  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ê!
  86. REGRA 4: MINIMIZE CLASSES E MÉTODOS Regra menos importante! Não

    exagere na SRP! Não tenha classes ou métodos demais
  87. OBRIGADO!

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

    programar
  89. QUALIDADE DE CÓDIGO

  90. CLEAN CODE / SOLID

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

  92. SOBRE SOLID: É um objetivo muito ousado Exige muitos conceitos

    de OOP Envolve muita experiência em abstração Exige um time muito "Maduro "
  93. SOLID não é um processo SOLID é o objetivo final...

    ...de um processo muito longo
  94. COMO COMEÇAR O PROCESSO?

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

  96. None
  97. Sua classe precisa de uma dieta

  98. OBJECT CALISTHENICS

  99. Jeff Bay, 2008 The Thoughtworks Anthology

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

  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
  102. REGRA 1 Apenas um nível de indentação por método

  103. MOTIVOS Evitar efeito hadouken Facilitar leitura do código Promover SRP

    (Single Responsibility Principle)
  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
  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
  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
  107. REGRA 2 Não use ELSE

  108. MOTIVOS Porque else não serve para nada Porque você não

    precisa do else Na verdade você nunca precisou do else
  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() }
  110. private function response(Request $request): Response { if ($request­>expectsJson()) { $response

    = $this­>createJsonResponse(); } else { $response = $this­>createHtmlResponse(); } return $this­>formatResponse($response); }
  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(); }
  112. REGRA 3 Envolva seus tipos primitivos

  113. MOTIVOS Tipos primitivos são muito permissivos Não possuem nenhum significado/comportamento

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

    $amount, $currency ); return $this­>paymentProvider ­>confirmationNumber(); }
  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(); } }
  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 }
  117. public function charge(Money $price): string { $this­>paymentProvider­>chargeUser( $this­>user­>id(), $price­>amount(), $price­>currencyAbbreviation(),

    ); return $this­>paymentProvider ­>confirmationNumber(); }
  118. REGRA 4 Envolva suas collections em classes

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

    mesma classe
  120. CONCEITO Manipular objectos similares em um só lugar Somente uma

    propriedade por classe collection Métodos realizam ações no conjunto de objectos
  121. class ActiveUsersCollection implement Iterator { private array $users = [];

    public function __construct(array $users) { $this­>users = $users; } public function filterByActivationProcess(): self { // Code } }
  122. REGRA 5 Use apenas um ponto (­>) por linha

  123. MOTIVOS Facilita a leitura Lei de Demeter (fale com amigos)

    Reforça o Open-close principle
  124. EXCEÇÕES Fluent Interfaces / Chaining Pattern $builder­>where(...)­>order(...)­>limit(...)­>get()

  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
  126. class Employee { public function addToCollection(CountriesCollection $countries): void { $countries­>add(

    $this ­>address ­>addToCollection($countries) ); } }
  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
  128. REGRA 6 Não abrevie

  129. MOTIVOS Aumentar entendimento do contexto Porque abreviação é uma merda

    Porque eu não sou obrigado a entender
  130. None
  131. EXEMPLOS $cfClient quer dizer $cloudflareClient $rsp quer dizer $requestServiceProvider $mgSrv

    quer dizer $mongoService
  132. DICA Nomes estão ficando muito grandes? Reveja SRP! Sua classe/método

    pode estar fazendo coisa demais
  133. REGRA 7 Mantenha todas as classes pequenas

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

    uma merda
  135. REGRINHAS Classes com até 50 linhas 150 linhas (PHP) Pacotes

    com no máximo 10 arquivos
  136. REGRA 8 Não tenha mais que duas variáveis de instância

    em sua classe
  137. None
  138. MOTIVOS SRP Porque classe grande é uma merda

  139. None
  140. class Customer { private Name $name; private int $customerId; }

    class Name { private string $firstName; private string $lastName; }
  141. Não seja um extremista! para lógica de domínio para representação

    de dados (Entity)
  142. REGRA 9 Não use getters ou setters A galera de

    Java pira
  143. None
  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)
  145. class Company { private EmployeesCollection $employees; public function setEmployees(EmployeesCollection $employees):

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

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

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

    void { $this­>total += $question­>total(); } }
  149. Getters/Setters. Evil. Period. Yegor Bugayenko https://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html

  150. ALGUMAS VERDADES Lemos mais código que escrevemos Escrever bem é

    difícil Exige prática Requer tempo
  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
  152. PRÓXIMOS PASSOS Hexagonal Architecture / DDD (abstrações) SOLID / Clean

    Code
  153. Domain-Driven Design in PHP Carlos Buenosvinos, Christian Soronellas, and Keyvan

    Akbary https://leanpub.com/ddd-in-php
  154. OBRIGADO!

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

  156. REFERÊNCIA Extremely Defensive PHP Marco Pivetta PHPSW: Coding Practices, June

    2015 @Ocramius https://www.youtube.com/watch?v=8d2AtAGJPno
  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
  158. Direção Defensiva Programação Defensiva

  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)
  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)
  161. CONSIDERAÇÕES Essas práticas são excelentes: a longo prazo trabalhando em

    equipe para projetos open-source
  162. REFLEXÃO "Código não é reutilizável. Abstrações são reutilizáveis." Marco Pivetta

    (@Ocramius)
  163. #1 - INTERFACIFICATION Abstraia tudo que possa ser compartilhado

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

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

  166. #2 - IMUTABILIDADE Evite alterar o estado de um objeto

    após sua inicialização
  167. Value Objects em DDD Command Handler pattern

  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 }
  169. #3 - NO SETTERS Setters mudam o comportamento de uma

    classe em tempo de execução (+1 ponto de falha / testes)
  170. OOP se resume à comunicação entre objetos (envio de mensagens

    / comportamentos) (substantivo / verbo)
  171. Dependências somente no construtor! (seu DI Container agradece!)

  172. #4 - NAMED CONSTRUCTORS Dê significado quando construir um objeto

    new Foo()?
  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');
  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 {} }
  175. #5 - DEPENDÊNCIA OPCIONAL JAMAIS Conheça e use Null/Fake Objects

  176. class UserLogin { private Logger $logger; public function setLogger(Logger $logger

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

    { // } } $userLogin = new UserLogin(new FakeLogger());
  178. #6 - EVITE RETORNAR NULL Use Null Objects (evita vários

    ifs + favorece exceptions)
  179. class NullLogger implements LoggerInterface { public function log(string $str): void

    { // do nothing } }
  180. #7 - PUBLIC METHODS Evite métodos públicos desnecessários (melhor caso

    é sempre 1 por classe!) regra do substantivo / verbo
  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
  182. #8 - BOOLEAN COMO PARÂMETRO Evite utilizar variáveis boolean como

    parâmetro de método (+1 caso de teste! SEMPRE!)
  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 }
  184. nº parâmetros boolean * 2 = # de test cases

    para o método
  185. #9 - ENCAPSULE ESTADOS Tome cuidado com objetos que representam

    estados
  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; } }
  187. #10 - FINAL CLASSES POR PADRÃO "Composition over inheritance"

  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 { /* ... */
  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
  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)
  191. LEMBRETE! Você pode extender interfaces interface Foo extends Bar {}

  192. #11 - PRIVATE POR PADRÃO Só exponha o que é

    realmente necessário! (private + public)
  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!
  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);
  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);
  196. #12 - NÃO ASSUMA "IDEMPOTÊNCIA" Chamadas consecutivas podem se alterar!

  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');
  198. #13 - NÃO USE TIPOS mixed declare(strict_types=1) por padrão

  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 { // ... }
  200. public function login(UserIdentifier $identifier, string $password): bool { // ...

    }
  201. Mas ainda pode melhorar!

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

  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; } }
  204. Value Objects encapsulam validação! (garantia de consistência centralizada)

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

    pode se encrencar!
  206. Em PHP Trait é Ctrl + C / Ctrl +

    V * Aumenta muito o acoplamento! As vezes DRY não é o melhor caso!
  207. Aumenta a dependência da Trait (nem tudo deve ser "compartilhável")

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

    testou tudo?
  209. public function foo(): Identifier { if (/**/) { $this­>bar(); //

    1 } if (/**/) { return $this­baz(); // 2 } return $this­>bar(); // 3 }
  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?
  211. Não seja um extremista #ficaadica

  212. OBRIGADO!

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

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

  215. ARQUITETURA & DESIGN

  216. ARQUITETURA Visão em alto nível "Arquitetura de uma casa" Conexões

    e Comunicação de modo geral
  217. DESIGN Estruturas e decisões em baixo nível "Estilo dos móveis

    de uma casa" Design Patterns
  218. Robert C. Martin "O objetivo da arquitetura de so ware

    é minimizar os recursos humanos necessários para construir e manter um sistema."
  219. DESENVOLVIMENTO So ware difícil de manter tem vida curta Arquitetura

    facilita desenvolvimento para o time que o mantém
  220. Uma boa arquitetura começa com bons acordos Decisões em conjunto

    (time!) Comunicação é fundamental Mudar arquitetura é muito caro!
  221. MATURIDADE Às vezes grandes projetos começam pequenos Zero (ou pouca)

    arquitetura Problemas de arquitetura aparecem bem depois
  222. OBJETIVOS Fácil desenvolvimento / baixo custo Publicação rápida e fácil

    Diminuir custos com manutenção (custo mais caro)
  223. ARQUITETURA EM CAMADAS "Deixe opções em aberto" Camadas independentes Casos

    de uso independentes Comandos independentes
  224. SCREAMING ARCHITECTURE

  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?
  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."
  227. QUAL O FOCO? "Uma boa arquitetura enfatiza os casos de

    uso e os dissocia de preocupações periférias."
  228. A Web é uma arquitetura?

  229. Não! Web é o mecanismo de entrega Sua arquitetura não

    deve se preocupar com entrega A Web é apenas um detalhe
  230. SOBRE TESTES

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

    baseada em casos de uso.
  232. PARA TESTAR: Você não precisa de framework Você não precisa

    de servidor web Você não precisa de banco de dados
  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."
  234. "Sua arquitetura deve dizer aos leitores sobre o sistema, não

    sobre o framework usado no sistema."
  235. CLEAN ARCHITECTURE

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

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

  238. HEXAGONAL ARCHITECTURE "Hexagonal Architecture - Message-Oriented So ware Design" (Matthias

    Noback) https://youtu.be/K1EJBmwg9EQ
  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
  240. "INDEPENDÊNCIA"

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

  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.
  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
  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.
  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)
  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.
  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!"
  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)
  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(), ]); } }
  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)
  251. POR ONDE COMEÇAR? Command & CommandHandler Pattern Command seria um

    "DTO" Handler seria o "Use Case"
  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.
  253. Command para Handler

  254. USE CASE: CREATE USER CreateUserCommand / CreateUserHandler

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

  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); } }
  257. 2. COMMAND Classe simples que guarda os dados necessários para

    execução da ação. Classe imutável!
  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() }
  259. 3. HANDLER Caso de uso propriamente dito Recebe o Command

    com todos os dados necessários
  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()), ]); } }
  261. PRÓXIMO PASSO Adicionar um CommandBus (responsável por executar os Handlers)

    composer require league/tactician
  262. INDICAÇÃO DE LIVRO Clean Architecture (Robert C. Martin)

  263. Grossi, Junior Estudar arquitetura de so ware vai te mostrar

    que mágicas não existem!
  264. OBRIGADO!

  265. SOLID PRINCIPLES entenda antes de tentar fazer

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

  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
  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)
  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
  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
  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
  272. SOLID DDD (Domain-Driven Design) SOLID Hexagonal Architecture SOLID CommandHandler Pattern

    SOLID CQRS / Event Sourcing
  273. OBJETIVOS Criação de estruturas de so ware que: Toleram mudanças

    facilmente São fáceis de entender São reutilizáveis
  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.
  275. SOLID é o objetivo final a ser alcançado Exige prática,

    paciência e perseverança
  276. OS PRINCÍPIOS SOLID (foco na arquitetura)

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

  278. "Uma classe deve ter somente uma razão para mudar." Robert

    C. Martin, 2005
  279. ATENÇÃO É um dos princípios mais conhecidos É um dos

    princípios menos aplicados É talvez o princípio menos entendido
  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)
  281. Historicamente, o SRP tem sido descrito como: "A module should

    have one, and only one, reason to change."
  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."
  283. Eles possuem visões diferentes da mesma coisa: "A module should

    be responsible to one, and only one, actor."
  284. SINTOMAS DE VIOLAÇÃO

  285. SINTOMA 1 Duplicação Acidental

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

    {} public function save() {} }
  287. Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

  288. PROBLEMAS Os autores estão totalmente acoplados Time do CFO pode

    afetar algo do time do COO
  289. Fonte: Clean Architecture, Robert C. Martin (Uncle Bob)

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

    é usado pelo COO em reportHours()
  291. "O SRP diz para separar os códigos que são dependentes

    de diferentes atores."
  292. SINTOMA 2 Merges

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

    Employee.
  294. Diferentes comportamentos na mesma classe Resultado: merge Muitas pessoas mudando

    o mesmo código "separar códigos que suportam diferentes atores"
  295. SOLUÇÃO? Separar ações compartilhadas em classes Regra do "substantivo +

    verbo" + final classes Composition over inheritance (ou composite reuse principle)
  296. Quando declarar uma classe como final? Quando ela tiver apenas

    um método público Provavelmente ela vai implementar alguma interface
  297. SOLUÇÃO?

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

  299. Agrupar as responsabilidades em Facade pattern Fonte: Clean Architecture, Robert

    C. Martin (Uncle Bob)
  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)
  301. OCP The Open-Close Principle (O de SOLID)

  302. "Um artefato de so ware deve ser aberto para extensão

    mas fechado para modificação." Bertrand Meyer, 1988
  303. Adicionar algo não implica em mudar algo Adicionar comportamentos Separação

    de acordo com SRP
  304. EXTRATO BANCÁRIO Home-banking: página HTML do extrato (números negativos em

    vermelho)
  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 = '<span class="red">' . $number . '</span>'; }
  306. Você alterou a classe para adicionar um comportamento

  307. "Uma boa arquitetura de so ware reduziria a quantidade de

    código alterado para próximo do mínimo. O ideal seria zero."
  308. OCP (funções diferentes para diferentes ações) (SRP em conjunto) StatementDataGenerator

    (dados) StatementHtmlDecorator (cor vermelha) StatementReportDecorator (parênteses)
  309. Dependency Inversion

  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
  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
  312. Como mudar o "formatter" em runtime? Dependency Inversion

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

    RedColorFormatter implements FormatterInterface {} class ParenthesesFormatter implements FormatterInterface {} class NegativeSignFormatter implements FormatterInterface {}
  314. // HTML (Web) $generator = new StatementGenerator( new RedColorFormatter() );

    // Report $generator = new StatementGenerator( new ParenthesesFormatter() );
  315. SRP OCP DIP

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

  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
  318. None
  319. "Uma classe base deve poder ser substituída pela sua classe

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

  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 {}
  322. O PROBLEMA DO QUADRADO/RETÂNGULO (exemplo de violação do LSP)

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

  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)
  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());
  326. LSP + ARQUITETURA O conceito de "base" pode ser estendido

  327. EXEMPLO Comparador de preços de taxis

  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
  329. Você sempre envia dados adicionais pickupAddress | pickupTime | destinationAddress

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

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

  333. Talvez o princípio mais simples de entender

  334. "Nenhum cliente deve ser forçado a depender de métodos que

    ele não usa" Robert C. Martin, 2002
  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
  336. None
  337. interface Animal { public function walk(); public function fly(); public

    function swim(); }
  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() {} // ? }
  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!"
  340. segregation / separação interface WalkInterface { public function walk(); }

    interface FlyInterface { public function fly(); } interface SwimInterface { public function swim(); }
  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() {} }
  342. Interface? Menor possível! (interfaces grandes podem ferir ISP)

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

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

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

  347. class ListAllUsersAction { private MysqlUserRepository $userRepository; public function __construct(MysqlUserRepository $userRepository)

    { $this­>userRepository = $userRepository; } public function __invoke(): JsonResponse {} }
  348. Dependências somente no construtor! Constructor Injection

  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'); }
  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; }
  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 {} }
  352. Mudar interfaces implica em mudar as implementações Mudar implementações nem

    sempre implica em mudar as interfaces
  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)
  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)
  355. PROBLEMA Criar instâncias de implementações com base em abstrações (implementações

    possuem dependências)
  356. SOLUÇÕES Dependency Injection Container Abstract Factory Pattern

  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'] ) ); } );
  358. class ListAllUsersAction { private UserRepositoryInterface $userRepository; public function __construct( UserRepositoryInterface

    $userRepository ) { $this­>userRepository = $userRepository; } public function __invoke(): JsonResponse {} }
  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
  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); } }
  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
  362. SUGESTÃO Hexagonal Architecture Domain-Driven Design

  363. OBRIGADO!