DEV Community

Cover image for Laravel Modular [anotações]
Clinton Rocha
Clinton Rocha

Posted on

Laravel Modular [anotações]

Salve, tudo bem com você?

Então, este conteúdo é basicamente composto pelas minhas anotações feitas enquanto eu estudava sobre arquitetura modular no Laravel. Meu objetivo durante o estudo era entender quais configurações necessárias no Laravel que me permitiriam utilizar esse tipo de estratégia na arquitetura. Acabou que encontrei informações sobre essas configurações, mas, além disso, também descobri ferramentas que agilizam esse processo.

Além do conteúdo sobre modularização, aqui você vai encontrar informações sobre padrões de design e um pouco de boas práticas. Como isso aqui é um subproduto dos meus estudos, tem muita coisa sobre o Laravel que acabei aprendendo durante o processo. Pode parecer óbvio para você, mas não para o “eu” do passado.

Já adianto: meu objetivo aqui é trazer pontos que considerei relevantes durante os estudos. Para você, talvez pareçam “informações jogadas”, mas eu geralmente estudo a partir de palavras-chave ou tópicos, usando-os como ponto de partida para outras pesquisas (Google, YouTube e artigos). Então, não tenho a pretensão de ensinar algo neste conteúdo.

Disclaimer

Foi utilizada IA para organizar as minhas anotações em tópicos e gerar o table of contents. Você provavelmente não entenderia a sopa de letrinhas e inúmeros bullet points que produzi durante meus estudos, então utilizei essa famosa ferramenta para auxiliar na organização de tudo por aqui.


Table of contents

1. Configuração de Módulos

1.1. Autoloading (PSR-4)

Para usar o Laravel de forma modular, é necessário informar o novo módulo no arquivo composer.json. Isso permite que o autoload funcione, garantindo que as classes do seu módulo sejam encontradas.

Se quiser saber mais: Link

1.2. Service Providers

O Service Provider é o coração de um módulo. Cada provider herda de uma classe base chamada ServiceProvider e possui dois métodos principais:

  • register(): Chamado primeiro, para registrar serviços e bindings.
  • boot(): Chamado após todos os métodos register() dos providers, para inicializar serviços e configurar funcionalidades (como rotas e views).

Para registrar um novo provider, ele deve ser informado no arquivo config/app.php (ou configurado para ser descoberto automaticamente).

A doc do Laravel é uma mãe, se quiser mais informação sobre o tópico, recomendo!

1.3. Testes Modulares

Para usar testes de forma modular, é necessário registrar o módulo no arquivo phpunit.xml.

2. Modelos, Factories e DTOs

2.1. Conectando Factories a Models

  • Por padrão, as factories procuram pelas models no namespace App\Models.
  • Para conectar uma factory a uma model, defina a model no atributo $model da factory.
  • Quando o caminho padrão da factory é alterado, é preciso informar ao Laravel onde ela está. Use o método estático newFactory() na model:
use Modules\Product\Database\Factories\ProductFactory;

class Product extends Model
{
    protected static function newFactory()
    {
        return ProductFactory::new();
    }
}
Enter fullscreen mode Exit fullscreen mode

Saiba mais em:

2.2. Actions

No Laravel, é uma convenção usar a pasta Actions para agrupar comportamentos e ações, similar aos conceitos de usecase ou service.

2.3. Definindo Status de Modelos

É possível definir um status em uma instância de model sem persistir no banco de dados. Exemplo:

public const STATUS_PENDING = 'pending';

public static function startForUser(int $userId): self
{
    return self::make([
        'user_id' => $userId,
        'status' => self::STATUS_PENDING,
    ]);
}
Enter fullscreen mode Exit fullscreen mode

2.4. Data Transfer Objects (DTOs)

Os DTOs são úteis para reduzir o acoplamento e melhorar a clareza. Eles podem ser usados para:

  • Comunicação entre diferentes camadas do código.
  • Reduzir o número de parâmetros em métodos.
  • Representar coleções de dados (Collections).
  • Usar métodos estáticos (fromEloquentModel ou fromEloquentCollection) para instanciar DTOs a partir de models ou collections do Eloquent.
class ProductDTO
{
    public function __construct(
        public readonly int $id,
        public readonly string $name,
        public readonly string $sku,
        public readonly float $price,
    ) {}

    public static function fromEloquentModel(Product $product): self
    {
        return new self(
            id: $product->id,
            name: $product->name,
            sku: $product->sku,
            price: $product->price,
        );
    }

    public static function fromEloquentCollection(Collection $products): Collection
    {
        return $products->map(fn (Product $product) => self::fromEloquentModel($product));
    }
}
Enter fullscreen mode Exit fullscreen mode

Saiba mais em:

3. IDE Helper

3.1. Instalação e Uso

O IDE Helper Generator for Laravel gera PHPDocs que ajudam a IDE a entender as models.

Instalação:

composer require --dev barryvdh/laravel-ide-helper
Enter fullscreen mode Exit fullscreen mode

Gerando PHPDocs:

  • Para o caminho padrão das models:
php artisan ide-helper:models
Enter fullscreen mode Exit fullscreen mode
  • Para um caminho personalizado (ex: na pasta modules):
php artisan ide-helper:models --dir=modules
Enter fullscreen mode Exit fullscreen mode

3.2. Scripts para Composer

É possível encurtar o comando adicionando um script no composer.json:

"scripts": {
    "ide": "php artisan ide-helper:models --dir=modules"
}
Enter fullscreen mode Exit fullscreen mode

Uso na CLI:

composer run ide
Enter fullscreen mode Exit fullscreen mode

4. Views Modulares

Para trabalhar com views modulares fora de resources/views, use o método boot() do Service Provider para registrar os novos caminhos.

class OrderServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->loadViewsFrom(__DIR__ . '/../../Ui/Views', 'order');
        Blade::anonymousComponentPath(__DIR__ . '/../../Ui/Views/components', 'order');
        Blade::componentNamespace('Modules\\Order\\Ui\\ViewComponents', 'order');
    }
}
Enter fullscreen mode Exit fullscreen mode

4.1. Views Normais

Use loadViewsFrom() para registrar pastas de views com um namespace, evitando conflitos entre módulos.

  • Exemplo: view('order::checkout') buscará a view em Modules/Order/Ui/Views/checkout.blade.php.

4.2. Componentes Anônimos

Use Blade::anonymousComponentPath() para registrar uma pasta de componentes Blade anônimos.

  • Eles não têm classe PHP associada, apenas arquivos .blade.php.
  • Exemplo: <x-order::alert /> renderiza Modules/Order/Ui/Views/components/alert.blade.php.

4.3. Componentes de Classe

Use Blade::componentNamespace() para registrar um namespace PHP de componentes de classe.

  • São componentes criados com php artisan make:component.
  • Exemplo: <x-order::checkout-summary /> renderiza o componente de classe CheckoutSummary no namespace Modules\Order\Ui\ViewComponents.

O básico sobre views no Laravel:

5. Ferramentas e Plugins

5.1. Laravel IDEA

  • É um plugin do PHPStorm que oferece suporte ao Laravel.
  • Na seção Module System, permite definir a estrutura modular do projeto (ex.: diretório modules com namespace Modules).
  • Funciona bem quando a estrutura interna de cada módulo segue o padrão do Laravel.

Se não me engano o Laravel IDEA agora tá de graça no PHPStorm, é uma ótima ferramenta, recomendo

5.2. Pacote Laravel Modules

  • O pacote Laravel Modules é uma ferramenta popular para modularizar projetos.
  • Fornece uma estrutura organizada para criar e gerenciar módulos.
  • O Laravel IDEA possui suporte nativo para este pacote.

6. Organização de Código: Abordagens

6.1. Vertical Slice

  • Organiza o código agrupando por funcionalidade (ex.: Order > Checkout), e não por tipo de arquivo.
  • Todos os arquivos relacionados a uma funcionalidade ficam juntos.
  • Facilita a visualização e manutenção de cada funcionalidade como uma unidade coesa.

Mais sobre esse tópico em:

6.2. Agrupamento por Tipo (Grouping by Type)

  • Organiza o código por tipo de arquivo (ex.: models, controllers).
  • É possível combinar esta abordagem com a modularização criando uma pasta src dentro do módulo para agrupar arquivos como Models, Events, etc., seguindo o padrão padrão do Laravel.

7. Padrões de Design e Boas Práticas

7.1. Acoplamento Temporal vs. Eventos

  • Problema: Acoplamento Temporal: Ocorre quando uma ação depende estritamente de outra para ser executada, criando uma ordem rígida e dificultando a manutenção.
  • Solução: Usar Eventos: Em vez de executar tudo em um único método, separe as responsabilidades. O fluxo principal dispara um evento e outras partes do sistema podem "escutar" e reagir de forma independente (ex.: enviar um e-mail de confirmação ou atualizar o estoque).

8. Outros Tópicos

8.1. Sequences em Factories

  • O Sequences do Laravel permite variar valores de atributos ao criar várias instâncias de uma factory.
  • É útil para criar dados de teste com pequenas variações.
use Illuminate\Database\Eloquent\Factories\Sequence;

User::factory()
    ->count(3)
    ->state(new Sequence(
        ['name' => 'Alice'],
        ['name' => 'Bob'],
        ['name' => 'Carol'],
    ))
    ->create();
Enter fullscreen mode Exit fullscreen mode

Referencia: Laravel: sequences

Top comments (2)

Collapse
 
danielhe4rt profile image
Daniel Reis

Ótimo estudo! Em breve a gente arruma um projetinho pra aplicar essas features.

Collapse
 
ketutdana profile image
Ketut Dana

Hi, you can try veltophp it is modular with HMVC pattern. Your feedback is welcome 🔥

veltophp.com

Some comments may only be visible to logged-in visitors. Sign in to view all comments.