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 services
pasta, 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 interface
que é implementado por um repository
que busca dados que são relativos à object
classe 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-layer
HTTP-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 object
aulas.
//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 contract
entre 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 Repository
que 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 CRUD
caracterí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 repository
pró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 PostsMotor
que interaja com Posts
a 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 UsersController
dentro 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.