Thiago Cantero

Tecnologia e Entretenimento

Arquitetura de SoftwareLaravelPadrões de ProjetoPHP

Classes de Serviços no Laravel – Organização e Otimização do Código

Olá, Mundo!
Tudo bem?

Espero que sim, um pouco afastado devido assuntos do trabalho, dentre outros que estive a frente, enfim muita correria… ; )

Quando estamos atuando em um projeto, precisamos otimizar os códigos com o intuito de facilitar a manutenção e, até mesmo, aumentar através de uma nova funcionalidade. Prerrogativas como esta são práticas de uma boa Arquitetura de Software, promovendo um código limpo, ágil e de fácil entendimento/documentação. Uma classe de Serviço nos capacita a tratar determinada funcionalidade do Software de maneira pragmática, deixando mais fácil desacoplar o código de uma Biblioteca, por exemplo, além de possibilitar diversas novas opções, como acessar um serviço através de uma API, etc. Os benefícios são enormes.

No exemplo utilizarei em uma aplicação Laravel, o conceito do Factory Pattern.

Os frameworks usam classes abstratas para definir e manter relacionamentos entre
objetos. Um framework é frequentemente responsável também pela criação desses
objetos.
Considere um framework para aplicações que podem apresentar múltiplos
documentos para o usuário. Duas abstrações-chave nesse Framework são as classes
Application (aplicação) e Document (documento). As duas classes são abstratas, e os
clientes devem prover subclasses para realizar suas implementações específicas para
a aplicação. Por exemplo, para criar uma aplicação de desenho, definimos as classes
DrawingApplication e DrawingDocument. A classe Application é responsável pela
administração de Documents e irá criá-los conforme exigido – quando o usuário
seleciona Open (abrir) ou New (novo), por exemplo, num menu.
Uma vez que a subclasse Documenta ser instanciada é própria da aplicação
específica, a classe Application não pode prever a subclasse de Document a ser
instanciada – a classe Application somente sabe quando um documento deve ser
criado, e não que tipo de Document criar. Isso cria um dilema: o framework deve
instanciar classes, mas ele somente tem conhecimento de classes abstratas, as quais
não pode instanciar.
O padrão Factory Method oferece uma solução. Ele encapsula o conhecimento
sobre a subclasse de Document que deve ser criada e move este conhecimento para
fora do framework. – Padrões de projeto: soluções reutilizáveis de software orientado
a objetos I Erich Gamma, Richard Helm, Ralph Johnson e John
Vlissides – Pág. 112 – Editora Bookman 2000.

undefined
Factory Pattern UML – Disponível em: https://en.wikipedia.org/wiki/Factory_method_pattern

Como podemos ver acima, no diagrama, a classe “Creator 1” herda da classe abstrata “Creator” o método factoryMethod(), nele se cria um produto, que poderá ser de vários gêneros. Assim você reutiliza o código de maneira inteligente.

Vamos ao trabalho\\

Iremos utilizar em nosso exemplo uma aplicação que possui duas formas de pagamentos, vamos criar uma Interface de Pagamento PagamentoInterface e duas classes que implementarão essa interface: PagueAki e BufunfaPay.

Supondo que temos um projeto novo em Laravel, iremos criar em nossa pasta App a pasta Actions

Iremos organizar o código, desta maneira:

PagamentoFactory.php

<?php

namespace App\Actions;

interface PagamentoFactory
{
    public function pagamento();

}

PagueAkiFactory.php

<?php 

namespace App\Actions;

class PagueAkiFactory implements PagamentoFactory{

    public function pagamento(){

    }

}

BufunfaPayFactory.php

<?php 

namespace App\Actions;

class BufunfaPayFactory implements PagamentoFactory{

    public function pagamento(){

    }

}

O próximo passo é criarmos nossa classe de Serviço, ela nos fará o trabalho de atuar com esta funcionalidade na aplicação, desta maneira conseguimos modular cada parte dos atributos que temos, possibilitando identificar e aperfeiçoar, inclusive até maneira de estabelecer métricas (dependendo de como os dados são trafegados). Em nosso exemplo para a realização de pagamentos, em que nesta classe haveremos um método handle() que irá manipular a transação final e escolher qual a forma de pagamento escolhida pelo usuário, bem como tratar alguma exceção.

Vamos criar a pasta Services e uma subpasta Pagamentos em nosso pasta App.

MetodoPagamentoService.php

<?php

namespace App\Services\Pagamentos;

use App\Actions\BufunfaPayFactory;
use App\Actions\PagueAkiFactory;

class MetodoPagamentoService
{

    public $metodo;

    const BufunfaPay = 'bufunfapay';
    const pagueAki = 'pagueaki';
    public function __construct($metodo)
    {
        $this->metodo = $metodo;
    }

    public function handle()
    {
        switch ($this->metodo) {
              return (new BufunfaPayFactory())->pagamento();
            case self::pagueAki:
                return (new PagueAkiFactory())->pagamento();
            default:
                return 'Método de Pagamento inválido!';
        }
    }

}

Finalizando nosso exemplo faremos nosso Controller que irá enviar para a rota a forma de pagamento que o usuário desejar.

<?php

namespace App\Http\Controllers;


use App\Services\Pagamentos\MetodoPagamentoService;

class PagamentoController extends Controller
{
    public function pagar($metodo){
        
        return (new MetodoPagamentoService($metodo))->handle();
    }
}

Como vimos o exemplo facilita muito para alterarmos o Gateway de Pagamentos, aumentar o número inclusive, desacoplar o código, otimizar a flexibilidade e manutenção do Código.

Por hoje é só, até breve! ; )

Há duas maneiras de ser enganado. Uma é acreditar no que não é verdade; a outra é recusar a acreditar no que é verdade.