Clean Archictecture T2:E1 – Explicação e Estrutura inicial

Depois de algum tempo totalmente ausente aqui no blog, hoje estou voltando com mais uma temporada da Clean Architecture. Dessa vez eu vou me aprofundar bem mais no assunto, aplicar algumas técnicas e dicas visando o que usei em um projeto real nesses últimos 3 meses focando mais em PRODUTIVIDADE, porém, respeitando as fronteiras e camadas.

PS: Acredito que vai ter muita coisa boa (assim espero rssrs).

Agradecimentos

Antes de qualquer coisa quero deixar um forte abraço e um cheiro no coração para todas essas pessoas lindas por toda paciência, troca de ideias e a troca de experiências

Foto Will Correa - clean architecture
Will Correa (PHP BR)
Foto Junior Grossi - clean architecture
Junior Grossi (PHP MG)
Foto Vinicius Dias - clean architecture
Vinícius Dias (PHP Rio)
Foto Rodrigo Branas - clean architecture
Rodrigro Branas
Foto Marcel dos Santos - clean architecture
Marcel dos Santos (PHPSP)
Foto Erandir Junior - clean architecture
Erandir (PHP com Rapadura)
Foto Rodrigo Manguinho - clean architecture
Rodrigo Manguinho
Foto Otávio Lemos - clean architecture
Otávio Lemos

Redesenhos do livro Clean Architecture

Nesses últimos meses muitas pessoas que estão estudando esse assunto vieram me pedir para explicar um pouco mais sobre o tão famoso diagrama da Clean Architecture. Como vi que o volume só aumentava, decidi fazer um redesenho de 2 diagramas do livro deles com o um pouco mais de detalhes.

Como que vai funcionar?

Para você não ficar muito perdido aqui, peço que você assista a 1ª Temporada da Clean Arch onde eu faço um tour pelas camadas da Clean Architecture, um pouco de teoria e alguns exemplos simples de aplicação. Eu não vou me aprofundar em coisas que eu já disse lá porque ficaria algo muito repetitivo e esse conteúdo aqui ficaria bem mais maçante.

Seguindo a ideia que eu fiz na Live do Rodrigro Branas vamos criar alguns casos de uso mais rebuscados do Jogo Clash Royale.

Capa do jogo Clash Royale

Calma jovem… Se você não conhece esse jogo não tem problemas, pois eu vou explicar bem como serão os casos de uso. Abaixo vou listar como vai ser a pegada dessa nova temporada:

  • Utilizarei a linguagem de programação PHP 8.1 (Se você não é do mundo PHP, não se preocupe, os conceitoas serão os mesmos para a sua linguagem preferida);
  • NÃO será falado muito de teoria, a ideia aqui é ser algo mais prático do que a 1ª temporada;
  • Serão feitas pequenas analogias/comparações com a 1ª Temporada;
  • Público Alvo: essa temporada é focado para um público intermediário e avançado, então, muitas coisas de PHP, Design não serão muito aprofundados aqui;
  • Mostrar como eu trabalhei nos meus últimos projetos, nas minhas últimas experiências para ter um equilíbreio entre uma arquitetura boa e produtiva;
  • Reutilizar o domínio criado aqui nos principais frameworks do mercado. (Laravel, Symfony, Cake e etc.)

Estrutura do Projeto com Clean Architecture

Precisamos fazer um setup básico do projeto para depois a por a mão na massa de fato. Vou criar uma estrutura bem semelhante que uso no meu dia-a-dia que vem funcionando muito bem para mim.

Git

Vamos criar um arquivo .gitignore e por o conteúdo:

# phpstorm project files
.idea

# netbeans project files
nbproject

# zend studio for eclipse project files
.buildpath
.project
.settings

# windows thumbnail cache
Thumbs.db

# composer vendor dir
/vendor

# composer itself is not needed
composer.phar

# Mac DS_Store Files
.DS_Store

# phpunit itself is not needed
phpunit.phar

phpunit.xml
.php_cs.cache
.phpunit.result.cache
tests/coverage
tests/.phpunit.result.cache
.docker

Docker

Eu simplesmente adoro as imagens Docker do Webdevops e vou utilizar ela aqui também para nosso projeto. Então vamos criar um arquivo chamado docker-compose.yml e colocar o conteúdo:

version: '3'
services:
  app:
    container_name: clash-royale-app
    image: webdevops/php-nginx-dev:8.1
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      - WEB_DOCUMENT_ROOT=/app/public
      - PHP_DISPLAY_ERRORS=1
      - PHP_MEMORY_LIMIT=256M
      - PHP_MAX_EXECUTION_TIME=120
      - PHP_POST_MAX_SIZE=50M
      - PHP_UPLOAD_MAX_FILESIZE=50M
      - PHP_DEBUGGER=xdebug
      - PHP_IDE_CONFIG=serverName=_
      - XDEBUG_MODE=debug
      - XDEBUG_IDE_KEY=PHPSTORM
      - XDEBUG_MAX_NESTING_LEVEL=1500
      - XDEBUG_START_WITH_REQUEST=yes
      - XDEBUG_CLIENT_HOST=host.docker.internal
      - XDEBUG_CLIENT_PORT=9003
      - XDEBUG_OUTPUT_DIR=/app/.xdebug
    ports:
      - '8080:80'
      - '443:443'
    networks:
      - clash-royale-network
    depends_on:
      - db

  db:
    image: mysql:5.7
    container_name: clash-royale-db
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_DATABASE=clash_royale
    networks:
      - clash-royale-network
    volumes:
      - ./.docker/data:/var/lib/mysql

Depois de criado, abra o seu terminal e digite: docker-compose up -d . Se ocorrer tudo certo, você terá 2 containers prontos para gente poder usar aqui nas nossas atividades.

Composer + Autoload (PSR-4)

Vamos criar agora o nosso arquivo composer.json que irá configurar as dependências do projeto e também o autoload:

{
    "name": "dersonsena/clash-royale",
    "authors": [
        {
            "name": "Cabra.dev",
            "email": "[email protected]"
        }
    ],
    "scripts": {
        "test": "phpunit --configuration tests/phpunit.xml",
        "test:unit": "phpunit --configuration tests/phpunit.xml --testsuite unit",
        "test:integration": "phpunit --configuration tests/phpunit.xml --testsuite integration",
        "test:filter": "phpunit --configuration tests/phpunit.xml --filter ",
        "test:coverage:text": "phpunit --configuration tests/phpunit.xml --coverage-text",
        "test:coverage:report": "phpunit --configuration tests/phpunit.xml --coverage-html tests/coverage/html"
    },
    "require": {
        "php": "^8.1",
        "ext-mbstring": "*",
        "ext-json": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^9.5",
        "dg/bypass-finals": "^1.3"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src\\"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests\\"
        }
    },
    "config": {
        "optimize-autoloader": true
    }
}

Abra seu terminal e digite o comando docker exec -it clash-royale-app composer install. Aqui serão instalados as dependências do projeto por dentro do container da aplicação. Aguarde alguns instantes até terminar esse processo.

PHPUnit

Agora vamos criar o arquivo de configuração do PHPUnit para que a gente possa criar nossos testes de Integração e de Unidade. Vamos criar uma pasta /tests e dentro dela vamos criar um arquivo chamado phpunit.xml e colocar o conteúdo:

<?xml version="1.0"?>
<phpunit
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    bootstrap="./bootstrap.php"
    backupGlobals="false"
    backupStaticAttributes="false"
    colors="true"
    convertErrorsToExceptions="true"
    convertNoticesToExceptions="true"
    convertWarningsToExceptions="true"
    testdox="true"
    processIsolation="true"
    stopOnFailure="false"
    xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
>
    <coverage processUncoveredFiles="true">
        <include>
            <directory suffix=".php">../src</directory>
        </include>
    </coverage>
    <testsuites>
        <testsuite name="unit">
            <directory suffix="Test.php">Unit</directory>
        </testsuite>
        <testsuite name="integration">
            <directory suffix="Test.php">Integration</directory>
        </testsuite>
    </testsuites>
</phpunit>

Perceba que no atributo bootstrap="./bootstrap.php" está apontando para o arquivo bootstrap.php que fica dentro do mesmo diretório. Então vamos criar esse camarada agora:

<?php

error_reporting(E_ALL);

$composerAutoload = __DIR__ . '/../vendor/autoload.php';

if (is_file($composerAutoload)) {
    require_once $composerAutoload;
}

DG\BypassFinals::enable();

Agora vamos criar uma Classe Base chamada TestCaseBase para todos os nossos testes e um script de teste simples chamado HelloWorldTest apenas para ver se está tudo com nossa configuração do PHPUnit:

namespace Tests;

use PHPUnit\Framework\TestCase;

abstract class TestCaseBase extends TestCase
{
} 
namespace Tests\Unit;

use Tests\TestCaseBase;

final class HelloWorldTest extends TestCaseBase
{
    public function testHelloWorld()
    {
        $this->assertTrue(true);
    }
}

Feito isso, vamos executar o script de teste com o comando:

docker exec -it clash-royale-app composer run test

Se dé tudo certo, você terá um teste passando na saída do comando.

Se você ainda não conhece o PHPUnit eu super indico essa playlist do canal Dias de Dev do meu amigo Vinícius Dias que foi citado no início do post.

Depois de feito tudo isso, no fim do dia deveremos ter uma estrutura de pastas semelhantes a essa abaixo:

Árvores de arquivos do projeto

Terminei. E agora já podemos aplicar Clean Architecture!?

Calma jovem, logo logo estaremos colocando a mão na massa! Bom, se tudo tiver dado certo então, você já está pronto para começar. Se você tiver algum problema com a configuração acima, por favor, não deixe de colocá-los nos comentários, será um prazer poder te ajudar com isso.

Por hoje é isso… espero que vocês estejam tão empolgados quanto eu com essa nova temporada de Clean Architecture. No próximo episódio vamos criar as features utilizando o Gherkin para esse projeto, para que fique bem claro o início, meio e fim de tudo.

Forte abraço =)