Alternativas para HMVC com Laravel

O HMVC é um padrão de projeto realmente eficaz e tem sido amplamente utilizado, porém entre a comunidade do Laravel, este padrão de projeto é considerado de alguma forma uma “prática ruim”.

Em seu livro From Apprentice to Artisan , Taylor Otwell – Laravel Framework Author – sugere:

HMVC é geralmente um indicador de projeto ruim

Além disso, em seu capítulo Estrutura do aplicativo , Taylor estava falando sobre como

“MVC está matando você”

e como não devemos tornar meras convenções “sagradas” a ponto de nunca abrir caminho por conta própria e quebrá-las.

e então logo depois

Diga adeus às modelos!

Não é à toa que o Laravel 5 não vem sem pasta de modelos.

O que Taylor estava sugerindo neste capítulo é um convite para pensar fora da caixa, pensar fora do padrão de projeto MVC usual, então talvez você tenha um código melhor, elegante e fácil de manter.

Não é à toa que o Laravel 5 não vem com a pasta ‘modelos’.

Portanto, pensei que valeria a pena entreter as idéias e sugestões que Taylor estava tentando comunicar.

MVC, HMVC encorajam controladores Fat.


Normalmente, seguindo um padrão de design MVC, muitos desenvolvedores acabam com o que é chamado de Fat Controllers .

Controladores gordos são simplesmente controladores que possuem muita lógica de negócios, muitas linhas de código volumoso.

A menos que você seja um dos poucos que criou a servicespasta, ainda sofrerá com essas classes de gordura.

Solução: SoC , separação de preocupações

Taylor não deixou isso sem solução; na verdade, ele sugeriu
SoC como uma solução.

Ótimo, mas como usamos isso?

<br>
Tendo dito Tchau! para modelos, e tentando pensar fora da caixa, Taylor Otwell propôs um caminho em seu livro, que implica criar um interfaceque é implementado por um repositoryque busca dados que são relativos à objectclasse que você deseja.

De uma forma mais concreta de explicação, usar ‘ SoC ‘ será semelhante a algo como ele se sugeriu .

Eu realmente aceitei essa ideia e a implementei, e isso deixou meus controladores menos gordos.

Contudo

Não encontrei na internet uma maneira de substituir o HMVC …

Então, eu vim com o que decidi chamar de Multi-Layering .

A multicamadas é uma forma de desacoplar e tornar modular o código de seu aplicativo em analogia às n camadas da teoria de desenvolvimento da web.

Não é nada novo.

Deixe-me explicar,

A multicamadas descreve duas camadas principais, a e a DATA-layerHTTP-Layer

E cada um desses dois tem três partes.

Esses dois interagem entre si por meio de pontos de junção onde se conectam explicitamente e fornecem uma maneira em caixa e protegida de buscar dados do banco de dados, de adicionar lógica de negócios a seus controladores e de apresentá-los ao mundo externo.

A camada DATA divide-se em três partes:

  • Contratos:
  • Objetos
  • Repositórios

Enquanto a camada HTTP se divide em três partes:

  • Motores
  • Traços
  • Controladores

<br> <br> <br>

A camada de dados.

<br>

A camada de dados nada mais é do que a implementação do que Taylor Otwell apresentou como sugestões no capítulo que mencionei acima.

*
Caso você não tenha lido o link que forneci, vá em frente, mas continuarei com uma forma semelhante de explicação e usarei trechos de código para tornar minhas idéias mais claras e concretas.
*

Vamos começar com uma breve definição das três partes dessa camada.

  • Contratos:

Contratos são interfaces que nossos repositórios irão implementar

  • Objetos:

Objetos são os bons e velhos modelos, as classes que descrevem um objeto em relação a uma tabela no banco de dados.

  • Repositórios:

Repositórios são classes que encaixam os dados recuperados do banco de dados, eles usam uma classe de objeto e implementam uma interface


Agora, vamos contretizar nossa conversa com alguns exemplos.

Vamos emular um exemplo de aplicativo de blog.
Neste aplicativo, queremos descrever o que é uma postagem e usá-la.

Vamos começar com as objectaulas.

 //this is the object class.

class Post extends Eloquent
{

protected $attributes = ['title', 'content'];

protected $table = 'posts';

public function someRelationship()
{
this->hasMany('SomeObjectClass');
}
}

<br> <br>
Em seguida, o contracts.

//This the interface 

interface PostsInterface
{

public function getAll();
public function getAllAsc();
public function getOldest();
public function getNewest();

}

<br>
Agora, o repositories, onde toda a magia acontece.

Basicamente, é aqui que você ocultará o código do restante do aplicativo.

Isso deve implementar um certo contractentre você e o resto do aplicativo.

Um contrato, como o que escrevemos acima.

Usando o contrato e a objectClass que criamos, nosso repositório terá a seguinte aparência:

class PostsRepositories implements PostsInterface
{

public function getAll()
{
return Post::all();
}

}

Isso deve servir para o .DATA-layer

<br> <br> <br>

A camada HTTP.

<br>

Vamos também definir as três partes dessa camada.

  • Motores:

Os motores são o ponto de junção entre a camada de dados e o httplayer, um motor é uma classe que injeta um repositório e usa uma característica de ações, para que possa buscar dados do banco de dados e adicionar qualquer lógica de negócios a ele e apresentá-los ao mundo se ele precisa.

Posteriormente, um motor é injetado em um controlador, para que o controlador possa buscar dados através deste mesmo motor, também chamar qualquer ação que este motor descreve / define.

  • Traços:

Um traço é um traço de ações que um controlador terá por meio de seu motor injetado.

  • Controladores

* Nossos bons e velhos controladores, mas desta vez, injetando um motor. *

Honestamente, esses são apenas termos sofisticados para a boa e velha noção de código modular, nós simplesmente separamos partes de nosso código de uma maneira organizada para tornar a manutenção de longo prazo menos uma nuance e economizar mais tempo. Portanto, não se preocupe com eles .

* Prestar respeito a eles é apenas uma conveniência para facilitar o trabalho em equipe. Então, por favor, faça isso. *

Vou começar esta parte esclarecendo Motors.

Basicamente, para cada um Repositoryque precisaremos buscar, deve haver um motor.

Cada motor estende-se de uma classe base Motor.

Aqui está o que é:

abstract class Motor 
{
protected $repository;
protected $views;

use GlobalCRUDtrait;
}

Então, isso é o que será compartilhado entre todos os motores, uma CRUDcaracterística básica , então evitamos duplicar o código para ações básicas do CRUD. Além disso, três atributos, um que é o repositorypróprio e o segundo que é views.

Portanto, os motores são onde os repositórios são injetados, e são os que são injetados nos controladores.

Você entenderá bem isso ao ler este artigo.

Agora, vamos implementar este motor.

Vamos criar um PostsMotorque interaja com Postsa camada DATA.

//PostsRepoInterface is just an allias for PostsRepository

use PostsRepoInterface;

class PostsMotor extends Motor
{
public function __construct(PostsRepoInterface $myRepo)
{
$this
->repository = $myRepo;
$this
->views = 'posts';
}

}

Ok, aqui, injetamos o repositório em nosso motor e estamos prontos.

Por favor, veja que nosso motor está usando GlobalCRUDtrait, será algo assim.

trait GlobalCRUDtrait
{
public function index()
{
$all
= $this->repository->getAll();
return view($this->views.'.index', ['all' => $all]);
}

public function show($id)
{
$instance
= $this->repository->show();
return view($this->view.'.show', ['instance' => $instance]);

//etc

}

E agora estamos prontos para colocar tudo junto.

o Controller!

Primeiro,
vamos ajustar a classe do controlador abstrato assim:

abstract class Controller
{
protected $motors = array();
}

Assim, poderemos injetar nossos motores de maneira adequada.

Como você pode ver, $motorsé apenas um array, que será associativo, onde armazenaremos cada motor injetado.

E então iremos criar nosso controlador desejado.

class PostsController extends Controller
{
public function __construct(PostsMotor $posts)
{
$this
->motors['posts'] = $posts;
}

public function getPostsIndex()
{
return $this->motors['posts']->index();
}

}

Tadaaaaa!

Dessa forma, não apenas desacoplamos completamente o back-end do front-end da lógica de negócios, mas nos tornamos capazes de escapar do modo HMVC.

Como?

Simplesmente, adicione ao seu controlador outro motor e use-o como quiser!

Não há necessidade de chamar nenhum controlador dentro de outro, basta chamar a ação específica que você deseja.

Por exemplo, no HMVC, você queria ligar UsersControllerdentro de PostsController.

Agora, tudo o que você vai fazer é criar um controlador, injetar os dois motores dentro dele e fazer a chamada desejada.

Concretização

class mySingleController extends Controller
{

public function __construct(UsersMotor $users, PostsMotor $posts)
{
$this
->motors['users'] = $users;
$this
->motors['posts'] = $posts;
}

public function getUsers()
{
return $this->motors['users']->getAll();
}

public function getPosts()
{
return $this->motors['posts']->getAll();
}

}

E nós terminamos.

Não apenas desacoplamos completamente a camada, nosso código se tornou completamente elegante.

Os controladores estão longe de ser gordos e são bastante adequados e fáceis de manter.

Além disso, dessa forma, os desenvolvedores de front-end não se importarão tanto com o código subjacente quanto eles.

O fluxo de trabalho.

Lembre-se de que usamos um traço CRUD comum entre todos os motores, onde chamamos os métodos corretos de cada repo e retornamos as visualizações apropriadas para cada um.

Isso aumentará visivelmente seu fluxo de trabalho, especialmente se você tiver tantos repositórios para lidar.

Tudo o que você precisa fazer é criar um motor que herda da classe base Motor e criar as visualizações para ele, e você está pronto para começar! chame-os como quiser do seu controlador de implemento motorizado!

Isso é altamente configurável, pois para adicionar novas ações a um motor específico, tudo o que você precisa fazer é ir até o seu motor e começar a descrever a nova ação que deseja, e chamá-la da forma normal que mencionei acima.

Mas, por que se preocupar com tudo isso?

A verdadeira questão é: por que não?

Esta é apenas uma forma de organizar o código e possibilitar a manutenção de longo prazo, além de produzir um código elegante e bastante fácil de depurar.

Padrões de design claros, ou arquiteturas, organização e elegância de código nunca devem ser uma segunda prioridade para qualquer desenvolvedor, portanto, sinta a necessidade de jogar a preguiça pela janela e fazer um trabalho organizado e bem executado.

Feliz codificação a todos.

Atualização: Pacote para Comandos Artisan

Então pensei, já que o objetivo é acelerar o fluxo de trabalho de forma eficiente, decidi fazer um pequeno pacote para gerar todas as coisas mencionadas acima.


Links: