Clean Architecture sem ""Arrodeios""

Por: Kilderson Sena

REFLEXÕES

Partes das regras de negócio na View ou no controller? Impedindo assim reuso e potencializando duplicação de código?

Regras de negócio estavam diretamente "casadas" com o banco de dados? Dificultando criação de testes?

É arriscado ou seguro substituir uma biblioteca de sua aplicação sem ter que mexer em milhares de linhas de código?
(Por exemplo do moment.js)

Você já se deparou com as situações abaixo:

E para onde isso nos leva?

Dificuldade na evolução do produto
Aplicação fica engessada e com código rígido.

Se você passa ou já passou por isso, tá na hora de virar esse jogo!

Arquitetura de Software

"(...) minimizar os recursos humanos necessários para construir e manter um determinado sistema."

Robert C. Martin

Arquitetura de Software

"(...) são decisões que ao mesmo tempo são importantes e difíceis de mudar."

Martin Fowler

Arquitetura de Software

"(...) é sobre coisas importantes seja lá elas quais forem.."

Ralph Johnson (Gang of Four)

Clean architecture

É uma estratégia arquitetural orientado ao desacoplamento entre as regras de negócio da aplicação, recursos externos como frameworks e bancos de dados.

Clean architecture

Separação da aplicação em

camadas lógicas

E a tal das camadas?

CAMADAS !== PASTAS

As camadas são LÓGICAS, camadas NÃO SÃO pastas!

E a tal das camadas?

E a tal das camadas?

"(...) o centro da sua aplicação não é o banco de dados. Não é ele e nem um ou mais frameworks que você pode estar usando. O centro da sua aplicação são os use cases."

Robert C. Martin

E a tal das camadas?

"(...) devem definir como as coisas são organizadas, como elas se relacionam e que responsabilidades elas devem ter."

Rodrigo Branas

Quem é tu mermo?

Kilderson Sena

🤓 Sênior Full Stack Developer

👨‍🎓 Graduado em Análise e Desenv. Sistemas

🤘 Movido a café e Rockn'roll

👨‍💻 Programador há mais de 14 anos

👨‍👩‍👦 Esposo da Dayanny Pai do Kauan Lucas

fb.com/kilderson.sena

@derson_sena

@derson_sena

dersonsena

youtube.com/yiiacademybr

@yiiacademy

yiiacademy.com.br

Quem é tu mermo?

Quem é tu mermo?

8 Motivos para

usar o Yii2

yiiacademy.com.br/8-motivos-para-usar-o-yii-2

Aprenda Orientação a Objetos com Forma de Gelo

devtube.com.br/ebook-oo1.html

Agradecimentos

Vinícius Dias

(PHP Rio)

Junior Grossi

(PHP MG)

Willian Correa
(PHP BR)

Marcel dos Santos
(PHP SP)

Rodrigo Branas

(branas.io)

Erandir Jr.

(PHP com Rapadura)

Pré-requisitos

  • DTO (Data Transfer Object)
  • Design Pattern Adapter (Wrapper)
  • Single Responsibility Principle (S do SOLID)
  • Dependency Inversion (D do SOLID)
  • Um pouco de DDD

Minha opinião

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

São objetos que servem para transferir dados de uma camada para outra.

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Eu costumo por os dados primitivos, que não critique nada e seja imutável.

Objetivo dele é: receber os dados e levar para o(s) interessado(s).

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

São usados mais na "borda" da arquitetura para fazer a entrada de dados de forma desacoplada.

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

class CreateOrderInputData {
  constructor(
    private orderId: string,
    private clientId: number
  ){}

  getOrderId() {
    return this.orderId
  }

  getClientId() {
    return this.clientId
  }
}

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

É um padrão de projeto estrutural que permite objetos com interfaces incompatíveis colaborarem entre si.

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

(Classe de Negócio)

SalePayment

(API PagSeguro)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

// Business Class
class SalePayment {
  constructor() {
    //... connection stuff
  }

  async execute() {
    // some business logic and validations...
    const payload = await this.http.post('http://pagseguro...', {
      paymentMode: "default",
      paymentMethod: "creditCard",
      currency: "BRL",
      itemDescription1: "Notebook Prata",
      itemAmount1: 10300.00
    })
    // .. do something
  }
}

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

(Classe de Negócio)

SalePayment

(Classe de Persistência)

PagseguroApi

(API PagSeguro)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

type TransactionData = {
  paymentMode: string,
  paymentMethod: string,
  currency: string,
  itemDescription1: string,
  itemAmount1: number
}

class PagseguroApi {
  // ... connection stuff

  makeTransaction(data: TransactionData) {
    this.http.post(this.url, data)
  }
}

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

// Business Class
class SalePayment {
  constructor(
    private pagseguroApi: PagseguroApi
  ) {}

  execute() {
    // some logic and validations...

    this.pagseguroApi.makeTransaction({
      paymentMode: "default",
      paymentMethod: "creditCard",
      currency: "BRL",
      itemDescription1: "Notebook Prata",
      itemAmount1: 10300.00
    })
  }
}

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

(Classe de Negócio)

SalePayment

(Classe de Persistência)

PagseguroApi

(API PagSeguro)

(Interface)

PaymentGateway

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

type Transaction = {
  paymentMethod: string,
  currency: string,
  items: object[]
}

type TransactionOutput = {
  id: string,
  createdAt: Date
}

interface PaymentGateway {
  makeTransaction(transaction: Transaction): Promise<TransactionOutput>
}

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

type TransactionData = {
  paymentMode: string,
  paymentMethod: string,
  currency: string,
  itemDescription1: string,
  itemAmount1: number,
  // others fields...
}

class PagseguroApi implements PaymentGateway {
  // ... connection stuff

  makeTransaction(transaction: Transaction): Promise<TransactionOutput> {
    this.http.post(this.url, this.mapTransactionToData(transaction))
  }
  
  private mapTransactionToData(transaction: Transaction): TransactionData {
    // ... translate `Transaction` to `TransactionData`  
  }
}

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

// Business Class
class SalePayment {
  constructor(
    private paymentGateway: PaymentGateway
  ) {}

  execute() {
    // some logic and validations...

    this.paymentGateway.makeTransaction({
      paymentMethod: "creditCard",
      currency: "BRL",
      items: [
        {...}, {...}, {...}
      ]
    })
  }
}

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

(Classe de Negócio)

SalePayment

(Classe de Persistência)

PagseguroApi

(API PagSeguro)

(Interface)

PaymentGateway

(Classe de Persistência)

GetNetApi

(API GetNet)

(API Cielo)

(Classe de Persistência)

CieloApi

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

"De todos os princípios, a SRP provavelmente é o menos compreendido.

Em geral, os programadores imaginam que todos os módulos devem fazer apenas uma coisa."

Fonte: Livro Clean Architecture

Robert C. Martin

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Fonte: Livro Clean Architecture

Robert C. Martin

"Historicamente, o SRP tem sido descrito como:
Um módulo deve ter uma, e apenas uma,
 razão para mudar."

(Um módulo deve ser responsável por um, e apenas um, ator.)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

A idéia é que você não marrete tudo numa única classe, criando uma classe Deus

Mas como definir responsabilidade?

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Como muitas coisas na Engenharia de Software: DEPENDE!!!

É algo muito subjetivo e torna muito difícil dá uma definição concreta

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Mas existem alguns "indicadores" como tamanho de classes, tamanho de métodos que vai te dar um norte.

(Object Calisthenics)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Fonte: Livro Clean Architecture

Robert C. Martin

"Os sistemas mais flexíveis são aqueles em que as dependências de código-fonte se referem apenas a abstrações e não a itens concretos."

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Fonte: Livro Clean Architecture

Robert C. Martin

impraticável tratar essa ideia como uma regra, pois os sistemas de software dependem de muitas facilidades concretas."

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Fonte: Livro Clean Architecture

Robert C. Martin

"Por exemplo, a classe String no Java , DOMDocument  e  Array Object do PHP são concretas e não seria prático forçá-la a ser abstrata"

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

(Classe de Negócio)

SalePayment

(Classe de Persistência)

PagseguroApi

(API PagSeguro)

(Classe de Negócio)

SalePayment

(Classe de Persistência)

PagseguroApi

(API PagSeguro)

(Interface)

PaymentGateway

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

É uma abordagem focada na comunicação com especialistas do negócio

(Domain Experts)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

"Uma coisa é entender a Essência do Negócio e outra coisa é codar esta mesma essência de forma adequada"

Rodrigo Branas

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

"Domain Model: é o entendimento a respeito do domínio. Há um passado não tão distante onde muitas pessoas associavam Modelagem de domínio com criar um D.E.R. e ligar diretamente com estrutura de tabelas de um banco de dados relacional, e não é isso."

Rodrigo Branas

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Modelar domínio é sobre a linguagem ubíqua

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

~> São artefatos que possuem identidade e estado, podendo ser construídas por outras Entidades ou Value Objects (Objetos de Valor).

~> O estado desses artefatos podem e vão mudar de acordo com seu ciclo de vida

Entities (Entidades)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

  • Um pagamento pode estar aberto e depois ficar pago;
  • Um cliente pode estar adimplente e depois inadimplente;
  • Uma venda pode estar pendente ou cancelada;

Entities (Entidades) Exemplos:

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

enum PaymentStatus {
  OPENED,
  PAID,
  CANCELED
}

class Payment {
  private status: PaymentStatus
  private code: string

  constructor(
    private amount: number,
    private dueDate: Date
  ) {
    if (amount <= 0) {
      throw new Error('Amount must be greater than zero')
    }

    this.status = PaymentStatus.OPENED
    this.code = 'aa-bb-cc'
  }

  pay () {
    this.status = PaymentStatus.PAID
  }
}

!!! Alerta de polêmica !!!

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Entidades não tem absolutamente NADA a ver com mapeamento de um ORM.

Entities (Entidades)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Esse termo foi "patenteado" pelos ORM's, mas lá nada mais é do que um Mapper ou um espelho de uma classe para uma tabela do banco de dados.

Entities (Entidades)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Geralmente são objetos auto validaveis e imutáveis porque a sua identidade está no seu valor, ou seja, se por caso eu mudar o valor de um V.O. eu estou mudando ela inteira.

Value Objects (Objetos de Valor)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Geralmente são usados para compor as Entities, por exemplo:

Value Objects (Objetos de Valor)

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

enum PaymentStatus {
  OPENED,
  PAID,
  CANCELED
}

class Payment {
  private status: PaymentStatus
  private code: Code

  constructor(
    private amount: number,
    private dueDate: Date
  ) {
    if (amount <= 0) {
      throw new Error('Amount must be greater than zero')
    }

    this.status = PaymentStatus.OPENED
    this.code = new Code('aa-bb-cc')
  }

  pay () {
    this.status = PaymentStatus.PAID
  }
}
class Code {
  private code: string
  
  construct(code: string) {
    if (code.length < 3 && code.length > 10) {
      throw new Error('Invalid Payment Code')
    }
    
    this.code = code
  }

  value(): string {
    return this.code
  }
  
  toString(): string {
    return this.value()
  }
}

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Aggregates (Agregados)

De forma resumida é um conjunto de Entidades que colaboram entre si podendo até ser tratados como uma unidade.

Formam um contexto transacional.

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Aggregates (Agregados)

Se nós tempos um Pedido, muito provavelmente teremos um Item do Pedido, Desconto, Frete e etc, porém a Raiz do Agregado (Aggregate Root) é o Pedido.

(Entidade)

Pedido

(Entidade)

ItemPedido

(Entidade)

Desconto

(Entidade)

Frete

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Aggregates (Agregados)

A tendência natural é que quando essa entidade for abastecida lá no repositório eu estarei lidando com Agregados, ou seja, com o "Lider da Tribo"

(Aggregate Root)

Pedido

(Entidade)

ItemPedido

(Entidade)

Desconto

(Entidade)

Frete

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Repositories (Repositórios)

É uma abstração de persistência de dados. Nesse ponto não sabemos como ou onde isso vai ser persistido, você só estará dizendo que haverá uma persistência

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Repositories (Repositórios)

O objetivo dessa abstração é desacoplar o seu domínio da sua camada de infraestrutura.

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Application Services

São objetos que vão executar operações que não estão na entidade.

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Application Service

Também chamadas de use case, aqui você terá um objeto que irá fazer a famosa "Dança das entidades" para algum fim, por exemplo: fazer matrícula, efetuar login, cancelar venda e etc;

Pré-requisitos

DTO • Adapter • SRP • DIP • DDD

Anti-Corruption Layer (ACL)

ACL na Clean Architecture é o papel feito pela camada Interface Adapters, onde teremos adaptadores ou tradutores de uma camada para outra.

(Classe de Negócio)

SalePayment

(Classe de Persistência)

PagseguroApi

(API PagSeguro)

(Interface)

PaymentGateway

uffffaaa !!!

Agora respira e vamos falar de Clean Architecture

Fonte: Livro Clean Architecture

Robert C. Martin

Fonte: Livro Clean Architecture

Robert C. Martin

interprise business rules

São regras corporativas que valem independente da aplicação em que ela esteja.

interprise business rules

Supondo que estamos num domínio de vendas e temos cálculos de multa e juros. Se eu for calcular isso numa ação de venda, de NF, de estoque a regra continuaria a mesma. A aplicação muda mas a regra de juros e multa não muda.

USE cases

~> Não deve saber quem o está utilizando, se está consumindo JSON, XML, CSV, Texto puro

~> Não sabe que formato de dados será retornado, só retorna um DTO de Saída (Output Data) quando requerido

~> Lançam Exceções de Negócio

interface adapters

~> Fazem a "tradução" entre o mundo externo e as regras de negócio

~> Convertem dados para mecanismos de I/O

~> Análogo a ACL do DDD

Frameworks and Drivers

~> Essa é a camada mais Low Level;

~> São frameworks e outras estruturas externas que fazem o I/O com aplicação;

E por onde começar?

SugesTão de pastas

Entities + Use Cases

Interface Adapters

Framewors and Drivers

Main Layer

Clash Royale - Deck

Deck: para criar um deck é preciso ter: capacidade, cards, dono e possibilidade de mostrar a média de elixir com 1 casa decimal.

Card: um card representa um personagem com os atributos: nome, nível (nível máx: 13) e elixir (nível máx: 9)

Player: todo deck pertence a um player com os atributos: nome, qtd. troféus e o clã que ele pertence

Clash Royale - Battle

Deck Player 01

Deck Player 02

Player 01

Player 02

Caso de Uso Batalha

  1. É obrigatório adicionar exatamente 2 decks;
     
  2. A diferença entre os troféus dos players NÃO pode ser maior que 800;
     
  3. Caso dê tudo certo:
    1. deverá informar qual player e deck venceu;
        
    2. Deve somar 30 troféus para o player vencedor;
       
    3. Deve subtrair 30 troféus para o player perdedor;

Obrigado! =D

Kilderson Sena

fb.com/kilderson.sena

@derson_sena

@derson_sena

dersonsena

Clean Architecture sem "Arrodeios"

By Kilderson Sena

Clean Architecture sem "Arrodeios"

  • 193