Boas Práticas no Processo de Desenvolvimento de Software


Jefferson Mariano de Souza

Objetivo

  • Alinhamento arquitetura do sistema
  • Situações de exemplo
  • Conceitos SOLID

Agenda

  • Alinhamento sobre a solução atual
  • Situações de exemplo
  • Boas práticas

Alinhamento sobre a solução atual

Qual o objetivo do desenvolvimento do software?

Resolver situações e problemas de negócio

Organização da Solução

  • Monolito central (syn4tdf)
  • Frontend único
  • Módulos satélites (motor de complementos, gerador de relatórios, automação, contab4tdf)
  • Banco de dados único
Visão Geral
Utilização

Arquitetura do módulo central

  • Arquitetura modular
  • Módulos se baseiam na onion architecture
  • Código dividido em camadas de responsabilidade
Onion Architecture

Arquitetura módulo central

Módulos

Manipulação de dados

  • Aplicação é totalmente dependente de dados
  • Transformação de dados é de responsabilidade da camada de banco de dados

Camada de Apresentação

  • Papel do frontend é apenas apresentação
  • Lógica de negócio não pode ficar no frontend

Situações de exemplo

  • Modelagem de dados
  • Nomenclatura
  • Compatibilidade com situação preexistente
  • Restrição em tabelas com dados existentes

Modelagem de dados

Situação: Persistir dados para envio de e-mail de uma obrigação

  • configurações de servidor
  • mensagem do e-mail
Tabela com modelagem inconsistente
Tabela com dados repetidos

Nomenclatura

Situação: Código possui classes em português e em inglês

  • Ao criar estrutura de classes para um novo cenário, qual nomenclatura usar?
  • DDD

Compatibilidade

Situação: Para resolver determinada situação, a solução pensada é apagar uma coluna da tabela

  • Apagar coluna? Já existe no ambiente do cliente?
  • E se o cliente depende da coluna em um complemento?
  • E se o cliente possui dado importante nesta coluna?

Situação: Alteração em tabela existente

  • Evitar restringir tabelas existentes
  • Constraints que afetam dados
  • Se tabela existe no ambiente do cliente com determinada config, já há cenários diversos
  • Se necessário, validar no código de negócio (Java)

Copy & Paste

Situação: Já estava assim, me baseei em outro lugar

  • eu me baseei em outro lugar e já estava assim
  • copiar código é sempre sinal de algo errado
  • impacta refactor, evolução etc. Código nunca deve ser copiado, mas sim ajustado para manter a lógica em um lugar só
  • favorecer o reuso

Boas práticas

Responsabilidade de cada camada

  • resource/controller (recebe e redireciona requisição)
  • service (lógica de negócio)
  • repository (apenas executar query, quem orquestra é o service)

Cultura do Questionamento

  • quem eu vou impactar se fizer de determinada forma?
  • outros módulos?
  • outros clientes?
  • conversar com o dev responsável antes de iniciar o desenvolvimento
  • todos os requisitos são apenas um convite para uma conversa
  • é necessário alinhar com algum dev da equipe e validar a ideia da solução
  • em muitos casos, as demandas precisam ser amadurecidas

SOLID

SOLID é um acrônimo que contempla princípios da programação orientada a objetos e design de código identificados por Robert C. Martin (Uncle Bob)

## Single Responsibility Principle |Letra|Princípio|Definição| |-|-|-| |S|Princípio da Responsabilidade Única|Uma classe deve ter um, e somente um, motivo para mudar| Note: Single Responsibility Principle

Single Responsibility Principle

              
class Order {
  public calculateTotalSum(){/*...*/}
  public getItems(){/*...*/}
  public getItemCount(){/*...*/}
  public addItem(item: Item){/*...*/}
  public deleteItem(itemId: number){/*...*/}

  public printOrder(order: Order){/*...*/}
  public showOrder(order: Order){/*...*/}

  public load(orderID: number){/*...*/}
  public save(order: Order){/*...*/}
  public update(order: Order){/*...*/}
  public delete(order: Order){/*...*/}
}
              
            
              
class Order {
  public calculateTotalSum(){/*...*/}
  public getItems(){/*...*/}
  public getItemCount(){/*...*/}
  public addItem(item: Item){/*...*/}
  public deleteItem(itemId: number){/*...*/}
}

class OrderRepository {
  public load(orderID: number){/*...*/}
  public save(order: Order){/*...*/}
  public update(order: Order){/*...*/}
  public delete(order: Order){/*...*/}
}

class OrderViewer {
  public printOrder(order: Order){/*...*/}
  public showOrder(order: Order){/*...*/}
}
              
            
Single Responsibility Principle
## Open-Closed Principle |Letra|Princípio|Definição| |-|-|-| |O|Princípio Aberto-Fechado|Você deve ser capaz de estender um comportamento de uma classe, sem modificá-lo| Note: Open-Closed Principle Objetos ou entidades devem estar abertos para extensão, mas fechados para modificação

Open-Closed Principle

              
class ContratoClt {
  public salario(): number {/*...*/}
}

class Estagio {
  public bolsaAuxilio(): number {/*...*/}
}

class FolhaDePagamento {
  protected saldo: number;

  public calcular(funcionario: Funcionario) {
    if ( funcionario instanceof ContratoClt ) {
      this.saldo = funcionario.salario();
    } else if ( funcionario instanceof Estagio ) {
      this.saldo = funcionario.bolsaAuxilio();
    }
  }
}
              
            
Ao alterar uma classe já existente para adicionar um novo comportamento, corremos um sério risco de introduzir bugs em algo que já estava funcionando.
              
interface Remuneravel {
  public remuneracao(): number;
}

class ContratoClt implements Remuneravel {
  public remuneracao(): number { /*...*/ }
}

class Estagio implements Remuneravel {
  public remuneracao(): number { /*...*/ }
}

class FolhaDePagamento {
  protected saldo: number;
  
  public calcular(funcionario: Remuneravel) {
    this.saldo = funcionario.remuneracao();
  }
}                
              
            
Open-Closed Principle
## Liskov Substitution Principle |Letra|Princípio|Definição| |-|-|-| |L|Princípio da Substituição de Liskov|As classes base devem ser substituíveis por suas classes derivadas| Note: Liskov Substitution Principle Barbara Liskov

Liskov Substitution Principle

Se para cada objeto o1 do tipo S há um objeto o2 do tipo T de forma que, para todos os programas P definidos em termos de T, o comportamento de P é inalterado quando o1 é substituído por o2 então S é um subtipo de T
se S é um subtipo de T, então os objetos do tipo T, em um programa, podem ser substituídos pelos objetos de tipo S sem que seja necessário alterar as propriedades deste programa
              
public class Passaro {
  public voar() {};
}

public class Pato extends Passaro { /*...*/ }

public class Avestruz extends Passaro { 
  public voar() {
    throw new Error("Avestruz não voa!");
  };
}
              
            
              
public class Passaro {};

public class PassaroVoador extends Passaro {
  public voar(){};
}

public class Pato extends PassaroVoador {}

public class Avestruz extends Passaro {
  //avestruz não voa!
}
              
            
Liskov Substitution Principle
## Interface Segregation Principle |Letra|Princípio|Definição| |-|-|-| |I|Princípio da Segregação da Interface|Muitas interfaces específicas são melhores do que uma interface única| Note: Interface Segregation Principle Uma classe não deve ser forçada a implementar interfaces e métodos que não irão utilizar

Interface Segregation Principle

              
interface Ave {
  public setLocalizacao(longitude, latitude);
  public setAltitude(altitude);
  public renderizar();
}

class Papagaio implements Ave {
  public setLocalizacao(longitude, latitude) {
    //Faz alguma coisa
  }
    
  public setAltitude($altitude) {
    //Faz alguma coisa   
  }
    
  public renderizar() {
    //Faz alguma coisa
  }
}

class Pinguim implements Ave {
  public setLocalizacao(longitude, latitude) {
    //Faz alguma coisa
  }
    
  public setAltitude(altitude) {
    //Não faz nada...  Pinguins são aves que não voam!
  }
    
  public renderizar() {
    //Faz alguma coisa
  }
}
              
            
              
interface Ave {
  public setLocalizacao(longitude, latitude);
  public renderizar();
}

interface AveVoadora extends Ave {
  public setAltitude(altitude);
}

class Papagaio implements AveVoadora {
  public setLocalizacao(longitude, latitude) {
    //Faz alguma coisa
  }
    
  public setAltitude(altitude) {
    //Faz alguma coisa   
  }
    
  public renderizar() {
    //Faz alguma coisa
  }
}

class Pinguim implements Ave {
  public setLocalizacao(longitude, latitude) {
    //Faz alguma coisa
  }
  
  public renderizar() {
    //Faz alguma coisa
  }
}
              
            
Interface Segregation Principle
## Dependency Inversion Principle |Letra|Princípio|Definição| |-|-|-| |D|Princípio da inversão da dependência|Dependa de abstrações e não de implementações| Note: Dependency Inversion Principle

Dependency Inversion Principle

Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender da abstração Abstrações não devem depender de detalhes. Detalhes devem depender de abstrações
              
class PasswordReminder {
  private dbConnection: MySQLConnection;
    
  constructor() {       
    this.dbConnection = new MySQLConnection();           
  }
}
              
            
              
interface Connection {
  public connect() {}
}

class MySQLConnection implements Connection {
  public connect() {
    // ...
  }
}

class OracleConnection implements Connection {
  public connect() {
    // ...
  }
}

class PasswordReminder {
  private dbConnection: Connection;
    
  constructor(Connection dbConnection) {       
    this.dbConnection = dbConnection;           
  }
}
              
            
Dependency Inversion Principle

THE END

Apresentação disponível em: https://studiojms.github.io/sw-dev-good-practices-presentation/