Aplicações Laravel que gritam seu domínio

pedrovian4

pedrovian4

Posted on December 20, 2023

Aplicações Laravel que gritam seu domínio

Laravel rapidamente se tornou a estrela do desenvolvimento PHP, e temos Taylor Otwell para agradecer por isso. Não é apenas um framework; é quase uma varinha mágica para desenvolvedores PHP, transformando tarefas complexas em algo simples. Com seu tool-kit completo, o Laravel revolucionou a maneira como pensamos o desenvolvimento em PHP, tornando-o mais acessível e prazeroso.

Mas aqui está uma consideração importante: às vezes, nossos queridos apps Laravel tendem a se parecer um pouco... uniformes. Não é um "erro" do Laravel, mas sim uma característica intrínseca a muitos frameworks fullstack ou REST que são ricos em funcionalidades. Eles oferecem tantas ferramentas e estruturas prontas que, sem um esforço consciente, podemos acabar com aplicações que, apesar de altamente funcionais, não se destacam em termos de personalidade única (Não gritam seu domínio).

Domínios Gritantes

Image Pessoa gritando

Sabe como uma primeira impressão pode dizer muito? No mundo do desenvolvimento de software, isso é regra de ouro. E é aqui que a ideia dos "Domínios Gritantes" entra em cena, um conceito cunhado pelo famoso Uncle Bob como "Screaming Architecture". O truque é simples: só de dar uma olhada na estrutura de um software, você já capta a ideia geral do que ele se propõe a fazer. É como se o próprio código te desse as boas-vindas e te contasse sua história.

Aqui um simples exemplo: nosso domínio será uma empresa de logística. Antes de te da contexto gostaria apenas que olhasse para a estrutura de pastas desse modelo de negócio. Leia cuidadosamente cada nome de pasta e cada nome de classe (Lembrando arquitetura de software não é somente organização de arquivos)
Importante: nome de arquivos estão em português para fins de clareza na explicação, mas sempre escreva software em inglês.

app/

├── Models/
│ ├── Produto.php
│ ├── Armazem.php
│ ├── Transporte.php
│ └── Rastreio.php
| |__ ProdutoTipo.php
| |__ RastreioTipo.php

├── Repositories/
│ ├── ProdutoRepository.php
│ ├── ArmazemRepository.php
│ ├── TransporteRepository.php
│ └── RastreioRepository.php

├── Contracts/
│ ├── TransporteStrategyInterface.php
│ ├── RastreioStrategyInterface.php
│ ├── VeiculoInterface.php
│ └── RastreioInterface.php

├── Strategies/
│ ├── TransporteStrategy.php
│ ├── RastreioStrategy.php
│ ├── Transporte/
│ │ ├── NavioStrategy.php
│ │ ├── CaminhaoStrategy.php
│ │ └── AviaoStrategy.php
│ └── Rastreio/
│ ├── RastreioTempoRealStrategy.php
│ └── RastreioPadraoStrategy.php

├── Services/
│ ├── RastreioService.php
│ ├── TransporteService.php
│ ├── ArmazenamentoService.php
│ └── EntregaService.php

├── Controllers/
│ ├── ProdutoController.php
│ ├── TransporteController.php
│ ├── RastreioController.php
│ └── ArmazenamentoController.php

├── Factories/
│ ├── VeiculoFactory.php
│ └── RastreioFactory.php

└── Jobs/
├── AtualizarStatusEntrega.php
├── NotificarRastreioTempoReal.php
└── ProcessarEntregaProduto.php

Entendo Domínio de logística

No nosso modelo de logística, lidamos principalmente com transporte, o que implica que estamos sempre movimentando algo – neste caso, produtos. No nosso modelo de negócio, oferecemos o rastreamento desses produtos, e o tipo de rastreamento varia conforme o veículo utilizado. Cada meio de transporte, seja por terra, mar ou ar, possui um sistema de rastreio adaptado às suas características e necessidades específicas.

No nosso projeto Laravel voltado para logística, cada elemento da arquitetura não apenas cumpre uma função específica, mas também ecoa fortemente o domínio a que pertence. Com a Separação de Preocupações (SoC) e injeções de dependência bem implementadas, criamos um sistema onde a função de cada parte é claramente definida, refletindo as complexidades do setor logístico. Os Models estabelecem a base, simbolizando entidades críticas como produtos e veículos. As Strategies de transporte e rastreio, adaptáveis através de injeções de dependência, mostram a flexibilidade necessária para o dinamismo do nosso negócio. Os Repositories se dedicam exclusivamente à interação com o banco de dados, mantendo essa lógica separada da camada de negócios.

Os Services lidam com a lógica de negócios, enquanto os Controllers fazem parte da camada de acesso, gerenciando a interação entre a lógica de negócios e as demais interfaces do sistema. Essa clara demarcação de responsabilidades não só atende às necessidades específicas da logística, mas também transmite com clareza o propósito do sistema. Cada componente do software se alinha perfeitamente a um aspecto específico do negócio, tornando a arquitetura intuitiva e eficaz. É uma representação fiel do domínio de logística, facilitando tanto o desenvolvimento quanto a operacionalização do sistema.

a nível de comparação, segue uma arquitetura simplista do que o laravel "impõe" para gerênciar esse domínio.
app/

├── Http/
│ ├── Controllers/
│ │ ├── ProdutoController.php
│ │ ├── TransporteController.php
│ │ ├── RastreioController.php
│ │ └── ArmazenamentoController.php
│ │
│ ├── Middleware/
│ │ ├── AuthMiddleware.php
│ │ ├── LoggingMiddleware.php
│ │ └── ...
│ │
│ ├── Requests/
│ │ ├── ProdutoRequest.php
│ │ ├── TransporteRequest.php
│ │ ├── RastreioRequest.php
│ │ └── ArmazenamentoRequest.php
│ │
│ └── ...

├── Models/
│ ├── Produto.php
│ ├── Transporte.php
│ ├── Rastreio.php
│ └── ...

└── ...

No caso acima, podemos notar uma arquitetura mais genérica, que pode atender ao domínio, mas ainda não temos uma noção clara do que essa transportadora faz em alguns aspectos.

Documentação viva

A Screaming Architecture, quando aplicada ao nosso domínio de logística, tornou a estrutura do código uma documentação viva e clara. Por exemplo, se tivermos diferentes tipos de veículos de transporte, como navios, caminhões e aviões, cada um com suas estratégias de rastreamento distintas, a arquitetura do código refletirá essa complexidade de maneira evidente.

Imagine que ao navegar pelo código-fonte, um novo desenvolvedor pode identificar facilmente as pastas e classes relacionadas a cada tipo de veículo e suas estratégias de rastreamento. Isso não elimina a necessidade de documentação externa, mas torna essa documentação mais sobre o domínio do negócio em sí do que sobre o que cada parte do código faz, pois a própria estrutura do código comunica como a aplicação lida com esses aspectos do negócio.

Essa clareza arquitetural não apenas simplifica o processo de integração de novos membros da equipe, mas também torna mais eficaz o trabalho contínuo de desenvolvimento, manutenção e expansão da aplicação.

Testabilidade

Quando a arquitetura "grita" o domínio, ela torna o teste de código mais simples e compreensível. Ainda no nosso contexto de logística e temos diferentes estratégias de rastreamento para navios, caminhões e aviões.

Com essa arquitetura clara, podemos facilmente criar testes para cada uma dessas estratégias. Por exemplo, podemos escrever um teste para garantir que a estratégia de rastreamento de navios funcione corretamente. Isso envolveria simular o comportamento de rastreamento de um navio em diferentes situações e verificar se os resultados estão corretos. Segue um exemplo abaixo:


<?php
use PHPUnit\Framework\TestCase;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

class NavioRastreioStrategyTest extends TestCase
{
    public function testDeveRetornarCidadeEstadoLink()
    {
        $mockResponse = new Response(200, [], json_encode([
            'latitude' => 42.12345,
            'longitude' => -71.98765,
        ]));
        $mock = new MockHandler([$mockResponse]);
        $handlerStack = HandlerStack::create($mock);
        $httpClient = new Client(['handler' => $handlerStack]);

        $navio = $this->getMockBuilder(Navio::class)
            ->disableOriginalConstructor()
            ->getMock();

        $navioMock->codigo = 'COD123';
        $navioMock->tipo_mercadoria = 'AD234BC';
        /**http client é um parametro opcional
        é setado automaticamente quando uam strategy é chamada
        mas para tornar a class testavel ele pode mockar essa cliente
        **/
        $strategy = new RastreioStrategy($httpClient);
        // Automaticamente chama NavioRastreioStrategy
        $result = $strategy->rastrear($navio);

        $data = json_decode($result, true);

        $this->assertEquals('Exemplo Cidade', $result->cidade->nome);
        $this->assertEquals('Exemplo Estado', $result->estado->nome);
        $this->assertEquals('https://maps.google.com/?q=42.12345,-71.98765', $result->googleMaps->link);
    }
}

Enter fullscreen mode Exit fullscreen mode

Nesse teste conseguimos testar a regra de négocio, é claro é um teste bem feliz que falharia provavelmente, mas a ideia central é como nossa arquitetura que grita tornou nosso caso de um uso tão óbvio a nossa aplicação que foi muito facil criar um teste para esse nosso subdomínio que é rastrear navios.

Conclusão

Usar uma arquitetura que claramente comunica os problemas que o software busca resolver é uma vantagem significativa para sistemas que visam longevidade. Embora possa haver preocupações futuras com a descontinuação do Laravel, não estamos considerando essa variável, pois é improvável.

As vantagens dessa abordagem incluem:

  • Documentação viva: A documentação do software se concentra no domínio do problema, tornando-a mais relevante e útil.
  • Testabilidade: A implementação de testes, incluindo o desenvolvimento orientado a testes, permite que o software seja atualizado facilmente a cada nova versão estável do framework.
  • Escalabilidade: Uma arquitetura gritante não apenas orienta os desenvolvedores sobre o que cada componente representa, mas também implicitamente indica como as coisas devem ser implementadas no futuro, facilitando a escalabilidade à medida que novos recursos são adicionados ao sistema.

Por fim: faça seu código ter personalidade

💖 💪 🙅 🚩
pedrovian4
pedrovian4

Posted on December 20, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related