Skip to content

Instantly share code, notes, and snippets.

@fwfurtado
Last active July 26, 2019 17:44
Show Gist options
  • Save fwfurtado/1dd68c71b79f9e979a8ee0bd29e5aa20 to your computer and use it in GitHub Desktop.
Save fwfurtado/1dd68c71b79f9e979a8ee0bd29e5aa20 to your computer and use it in GitHub Desktop.
Siglas

Command Query Responsibility Segregation (CQRS)

Data Driven Design

Event Driven

Event Sourcing

Data Centric

Domain Centric


Onion Architecture

Hexagonal Architecture

Scream Architecture

Clean Architecture

Micro Kernel Achitecture

DCI (Data, Context and Interaction)

BCE (Boundary Control Entity)

Primitive Obsession

Usar tipos primitivos (inclusive os tipos pré-definidos da linguagem como String, Map/Dictonary por exemplo) para representar ideias do dominio (ou usar esses tipos indiscriminadamente). Por exemplo double para representar um valor monetário ou usar Map/Dictonary para representar um objeto.

A ideia aqui é criar novas classes para representar essas ideias do dominio, dessa forma podemos encapsular os comportamentos especializados.

Exemplo: Em nossos softwares é comum termos uma representação de nosso usuário, que contém dados alguns pessoais (nome, e-mail, foto) e dados de autenticação (login, senha)

Poderiamos ter uma classe assim:

class Usuario {
  private String nome;
  private String email;
  private URL foto;
 
  private String login;
  private String senha;

  public Usuario(String nome, String email, URL foto, String login, String senhaEmTexoPuro){
   
    if (ValidadorDeEmail.emailInvalido(email)){
      thow new IllegalArgumentException("Email: " +email+ " é inválido");
    }

    this.nome = nome;
    this.email = email;
    this.foto = foto;
    this.login = login;
    this.senha = AlgunHash.aplicarHash(senhaEmTexoPuro);
  }

  public String getEmail(){
    return email;
  }

  public String getNome(){
    return nome;
  }

  public URL foto(){
    return foto;
  }

  public String getLogin(){
    return login;
  }

  public boolean senhaCompativel(String senhaTextoPuro){
    return AlgunHash.comparaHashComSenhaEmTextoPuro(senha, senhaEmTexoPuro);
  }
}

Perceba que temos várias regras sobre objetos que são relevantes (validar senha, verificar e-mail), que estão espalhadas na classe Usuario.

Podemos melhorar nossa classe usuário criando alguns Value Objects para extrair as responsabilidades para as classes específicas.

class Usuario {
  private String nome;
  private Email email;
  private URL foto;
  private Credenciais  credenciais;
  
  public Usuario(String nome, Email email, URL foto, Credenciais credenciais){
    this.nome = nome;
    this.email = email;
    this.foto = foto;
    this.credenciais = credenciais;
  }
  
  
  public String getNome(){
    return nome;
  }
  
  
  public String getEmail(){
    return email.getEndereco();
  }
  
  public URL getFoto(){
    return foto;
  }  

  public String getLogin(){
    return credenciais.getLogin();
  }
  
  public boolean senhaCompativel(String senha){
    return credenciais.senhaCompativel(senha);
  }

}

class Email {

  private String email;

  public Email(String email){
    if (ValidadorDeEmail.emailInvalido(email)){
      thow new IllegalArgumentException("Email: " +email+ " é inválido");
    }
    
    this.email = email;
  }
  
  public String getEndereco(){
    return email;
  }
  
  /* Se fizer sentido */
  
  public String getRadical(){
    return email.substring(0, email.indexOf("@");
  }
  
}


class Credenciais {
  private String login;
  private Senha senha;
  
  public Credenciais(String login, Senha senha){
    this.login = login;
    this.senha = senha;
  }
  
  public String getLogin(){
    return login;
  }
  
  public boolean senhaCompativel(String senhaTextoPuro){
    return senha.combinaCom(senhaTextoPuto);
  }
  
}


class Senha {
  private String senha;
  
  public Senha(String senhaEmTexoPuro){
    this.senha = AlgunHash.aplicarHash(senhaEmTexoPuro);
  }
  
  public boolean combinaCom(String senhaTextoPuro){
    return AlgunHash.comparaHashComSenhaEmTextoPuro(senha, senhaEmTexoPuro);
  }
}

Dessa forma temos mais semântica nos objetos e temos objetos mais especializados.


Long Method

Métodos longos, são indícios de que o método faz mais do que deveria fazer. Tente quebrar as ações dentro do método em métodos privados, veja se aquelas ações não podem ser extraidas para classes especializadas.


Large Class

Classes que contém muitos atributos/metodos, são indícios de que ela faz mais do que deveria fazer. Existe uma falha na modelagem da classe, então repense nas responsabilidades, tente extrai-las para novas classes, métodos e etc.

Para exemplificar vamos usar uma classe Pessoa com nome e dados referente ao endereço.

class Pessoa {
   private String nome;
   private String rua;
   private Integer numero;    
   private String complemento;    
   private String bairro;
   private String cidade;
   private String estado;
   private String cep;
   
   Pessoa(String nome, String rua, Integer numero, String complemento, String bairro, String cidade,  String estado, String cep){
     this.nome = nome;
     this.rua = rua;
     this.numero = numero;
     this.complemento = complemento;
     this.bairro = bairro;
     this.cidade = cidade;
     this.estado = estado;
     this.cep = cep;
   }
   
   public String getNome(){
     return nome;
   }
   
   public String getEnderecoCompleto(){
     return String.format("%s, %d %s - %s %s/%s %s", rua, numero, complemento, bairro, cidade, estado, cep);
   }
}

Podemos extrar as informações do endereço para classes especializadas

class Pessoa {
  private String nome;
  private Endereco endereco;
 
  Pessoa(String nome, Endereco endereco){
      this.nome = nome;
      this.endereco = endereco;
  }
  
  public String getNome(){
    return nome;
  }
  
  public String getEnderecoCompleto(){
    return endereco.getEnderecoCompleto();
  }
}

class Endereco {
  private String rua;
  private Integer numero;    
  private String complemento;    
  private Bairro bairro;
  private String cep; 

  Endereco (String rua, Interger numero, String complemento, String bairro, String cep){
     this.rua = rua;
     this.numero = numero;
     this.complemento = complemento;
     this.cidade = cidade;
     this.cep = cep;
  }

   public String getEnderecoCompleto(){
     return String.format("%s, %d %s - %s %s", rua, numero, complemento, bairro.getBairroCompleto(), cep);
   }
}

classs Bairro {
  private String nome;
  private Cidade cidade;

  Bairro(String nome, Cidade cidade){
    this.nome = nome;
    this.cidade = cidade;
  }
  
  public String getBairroCompleto(){
    return nome + " " + cidade.getCidadeCompleta();
  }
}

class Cidade {
  private String nome;
  private Estado estado;

  Cidade(String nome, Estado estado){
    this.nome = nome;
    this.estado = estado;
  }

  public String getCidadeCompleta(){
    return nome + "/" + estado;
  }
}

enum Estado {
  SP;
}

Long parameter list

Um exemplo disso é um método que envia um e-email. Para esse caso precisamos receber o remenetente, destinatário, assunto, corpo do e-mail e talvez anexos.

public void enviarEmail(String remetente, String destinatario, String assunto, String corpoDaMensagem, File anexo){
  //Envia o email
}

Ao invés de receber todas essas informações "soltas", podemos criar um objeto que englobe todas essas informações e receber esse objeto.

class Mensagem {
  private final String remetente;
  private final String destinatario;
  private final String assunto;
  private final String corpoDaMensagem;
  private final File anexo;

  Mensagem(String remenetente, String destinatario, String assunto, String corpoDaMensagem, File anexo){
    this.remetente = remetente;
    this.destinatario = destinatario;
    this.assunto = assunto;
    this.corpoDaMensagem = corpoDaMensagem;
    this.anexo = anexo;
  }

  // Getters;
}

public void enviarEmail(Mensagem mensagem){
  //Envia o email
}

Data clumps

Quando algumas partes do código contém os mesmos grupos de váriaveis. Esse grupo de váriaveis que ser repete deveria ter uma classe para representa-los.


Feature Envy (Inveja de funcionalidades)

Um método acessa dados de outro objeto mais do que dele mesmo


Inappropriate Intimacy

Quando uma classe usa atributos e/ou métodos internos de outra classe


Message chains

Quando temos um encadeamento de métodos someObject.getX().getY().doSomething()


Middle Man

Quando uma classe faz somente uma ação e delega o restante das ações para outras classes. Isso pode acontecer quando refatoramos nossa classe e removemos alguns smells. Com isso, nossa classe acaba ficando "inútil" (ta bom exagerei ao chama-la de inútil, dado que ela ainda faz uma ação). Nesse caso por que manter essa classe que só delega ações? O ideal seria ela também delegar sua própria ação, assim podemos eleimna-la. ;)


Don't repeat yourself (DRY) -

CADA BLOCO DE INFORMAÇÕES DEVE TER UMA REPRESENTAÇÃO OFICIAL, EXCLUSIVA E SEM AMBIGUIDADES DENTRO DE UM SISTEMA

Um mesmo item não pode ser representado em dois ou mais lugares. Pois se um mudar, teremos dois ou mais pontos para alterar


Law of Demeter (LoD) - Don't talk to your neighbor's neighbor! (principle of least knowledge)

Na classe C, para todos os métodos M definidos em C, todos os objetos com o qual M se comunica devem ser: Argumento de M ou Um membro de C

Um objeto deve depender somente de atributos da própria classe ou valores que ele receba por argumento em seus métodos.

(Logo ele não pode depender de coisas estáticas de outras classes, encadeamento de vários métodos para executar uma ação someObject.getX().getY().doSomething() esse caso poderia ser resumido para someObject.doSomething() por exemplo)


Tell don't ask

Em linguagens procedurais é muito comum ficarmos perguntando (ask) o estado para então fazer uma operação (pois os dados e os comportamentos estão separados). Em linguagens orientadas a objetos ao invés de ficar perguntado (ask) o estado para efetuar uma operação, podemos simplesmente dizer para/mandar (tell) o objeto fazer alguma operação.

Ou seja, quando queremos efetuar uma operação sobre um objeto, ao invés de perguntar qual o estado atual para com base nisso aplicar a operação. Devemos simplesmente mandar a operação ser feita.

Para ilustrar vamos ter uma classe que represente um jogador e uma para computar a pontuação de um jogador. Com isso podemos incrementar/decrementar essa pontuação.

class Pontuacao {
  private Integer pontos = 0;
  
  public void setPontos(Integer pontos){
    this.pontos = pontos;
  }  
  
  public Integer getPontos(){
    return pontos;
  }
  
}

class Jogador {
  private String nome;
  private Pontuacao pontuacao = new Pontuacao();
  
  Jogador( String nome ) {
    this.nome = nome;
  }
  
  public String getNome(){
    return nome;
  }
  
  public Integer getPontuacao(){
    return pontuacao.getPontos();
  }
  
  public void setPotuacao(Integer novaPontuacao){
    pontuacao.setPontos(novaPontuacao);
  }
}

class Jogo {

  public static void main(String[] args){
    Jogador fernando = new Jogador("Fernando");
    
    // Nesse ponto o jogo já está rodando e queremos incrementar a pontuação do jogador
    
    Integer novaPontuacao = fernando.getPontuacao() + 10;
    
    fernando.setPontuacao(novaPontuacao);  
    
    //Agora queremos subtrair o 5 pontos
    
    novaPontuacao = fernando.getPontuacao() - 5;
    
    fernando.setPontuacao(novaPontuacao);  
  }  
}

O problema com o código acima é que estamos perguntando qual o valor atual da pontuação e, a partir disso adicionamos o valor de 10 pontos. E somente então redefinimos ("mandamos alterar/executar") o valor da pontuação.

O ideal aqui é não precisar perguntar nada ao objeto e sim mandar ele alterar a pontuação direto.

Para isso poderíamos escrever o seguinte código.

class Pontuacao {
  private Integer pontos = 0;
  
  public void addPontos(Integer pontos){
    this.pontos += pontos;
  }
  
  public void subPontos(Integer pontos){
    this.pontos -= pontos;
  }
  
  public Integer getPontos(){
    return valor;
  }  
}

class Jogador {
  private String nome;
  private Pontuacao pontuacao = new Pontuacao();
  
  Jogador( String nome ) {
    this.nome = nome;
  }  
  
  public String getNome(){
    return nome;
  }
  
  public Integer getPontuacao(){
    return pontuacao.getPontos();
  }
  
  public void incrementarPontuacao(Integer valorAIncrementar){
    pontuacao.addPontos(valorAIncrementar);
  }
  
  public void decrementarPontuacao(Integer valorADecrementar){
    pontuacao.subPontos(valorADecrementar)
  }
}

class Jogo {

  public static void main(String[] args){
    Jogador fernando = new Jogador("Fernando");
    
    // Nesse ponto o jogo já está rodando e queremos incrementar a pontuação do jogador
    
    fernando.incrementarPontuacao(10);
    
    //Agora queremos subtrair o 5 pontos
    
    fernando.decrementarPontuacao(5);
    
  }  
}

Principle of least privilege

Só deve ser capaz de acessar informações e recursos que são necessários para um propósito legítimo.


Anemic Domain Model


Rich Domain Model


Conway's Law

Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations


Inverse Conway Maneuver

Evolving your team and organizational structure to promote your desired architecture. Ideally your technology architecture will display isomorphism with your business architecture.


Hollywood principle

Don't call us, we'll call you!


IoC


Object Calisthenics

  1. Only One Level Of Indentation Per Method

  2. Don’t Use The ELSE Keyword

  3. Wrap All Primitives And Strings

  4. First Class Collections

  5. One Dot Per Line

  6. Don’t Abbreviate

  7. Keep All Entities Small

  8. No Classes With More Than Two Instance Variables

  9. No Getters/Setters/Properties


SOLID

  1. Single Responsibility Principle (SRP)

  2. Open Close Principle (OCP)

  3. Liskov Substitution Principle (LSP)

  4. Interface Segregation Principle (ISP)

  5. Dependency Inversion Principle (DIP)

Unit Test

Integration Test

Acceptance Test (or System Test)

Smoke Test

Load Test

Stress Test

Spike Test

Endurance Test

Generative Test

Property Based Test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment