Faz um tempo que não escrevo mais aqui, mas vamos lá, hoje vamos conversar sobre a arquitetura que eu uso no Laravel.
A estrutura de pastas e arquivos que eu desenvolvi ao longo do tempo me ajudou a ganhar agilidade desenvolvendo no dia a dia.
Estrutura de pasta
projeto
|--app
| |--Infra
| |--Models
| |--Modules
|--config
| |--develop
| |--prod
Fora do padrão que temos do Laravel ao dar o start, tenho essa estrutura.
Em Infra, deixo tudo que se aplica a escopo global da aplicação, como por exemplo, controllers a serem estendidos, validadores de requisições, enums, abstrações uteis (email, sentry), middle wares e outras coisas mais...
Em Models, fica os models do Laravel, organizados por módulo, então se eu tenho um model de usuário e outro de endereço do usuário, ambos ficam na pasta User.
Em módules fica todos os módulos separados por pasta, de forma clara a qual módulo estamos trabalhando. Dentro de cada módulo tenho as pastas referente a controllers, enum, use cases e demais itens específicos para esse módulo.
As pastas develop e prod, eu deixo os docker files, docker compose, configurações do opcache, etc...
Controllers
Eu gosto de trabalhar com controllers basic para fazer todo o trabalho repetitivo. Dentro de Infra/Controllers tenho um controller basic para cada ação do CRUD.
Exemplo de controller basic:
abstract class BaseCreateController extends Controller
{
abstract protected function getUseCase(): ICreateUseCase;
abstract protected function getRules(): array;
abstract protected function getModelName(): string;
public function __invoke(Request $request): JsonResponse
{
ForbiddenException::validatePolicy(GatesAbilityEnum::Create, $this->getModelName());
Validator::validateRequest($request, $this->getRules());
$result = $this->getUseCase()->execute($request->json()->all());
if ($result) {
return ResponseApi::renderCreated($result);
}
return ResponseApi::renderInternalServerError('Erro ao inserir item.');
}
}
Por esse controller, já temos todas as tratativas para exceptions de request inválida e falta de acesso ao endpoint, tratados no middle ware do Laravel.
O controller de um módulo fica assim:
class ClientCreateController extends BaseCreateController
{
public function __construct(protected ClientCreateUseCase $useCase)
{
}
protected function getUseCase(): ICreateUseCase
{
return $this->useCase;
}
protected function getRules(): array
{
return [
// regras do json de request
];
}
protected function getModelName(): string
{
return Client::class;
}
}
Use Cases
Todo use case implementa uma interface, para garantir a comunicação correta com o controller.
O use case do controller acima, ficaria assim:
class ClientCreateUseCase implements ICreateUseCase
{
public function execute(array $data): array
{
$client = Client::create([
// campos
]);
return $client->toArray();
}
}
Dessa forma, com poucos arquivos, temos uma estrutura robusta e fácil de se trabalhar, já que não precisarei me preocupar com padrão de response, validar request e acesso do usuário. Isso tudo já fica abstraído no controller base.
O máximo que terei que fazer é criar e registrar policy para o módulo.
Por hoje é só pessoal. Até a próxima.
Top comments (0)