Dentro do contexto de deste post de Igor Minar, líder do AngularJS:
MVC vs MVVM vs MVP . Que tópico controverso sobre o qual muitos desenvolvedores podem passar horas e horas debatendo e discutindo.
Por vários anos, o AngularJS esteve mais próximo do MVC (ou melhor, de uma de suas variantes do lado do cliente), mas com o tempo e graças a muitas refatorações e melhorias na API, agora está mais próximo do MVVM - o objeto $ scope pode ser considerado o ViewModel que está sendo decorado por uma função que chamamos de controlador .
Ser capaz de categorizar uma estrutura e colocá-la em um dos buckets MV * tem algumas vantagens. Ele pode ajudar os desenvolvedores a se sentirem mais confortáveis com suas APIs, facilitando a criação de um modelo mental que representa o aplicativo que está sendo construído com a estrutura. Também pode ajudar a estabelecer a terminologia usada pelos desenvolvedores.
Dito isso, eu prefiro ver os desenvolvedores criar aplicativos chiques que são bem projetados e seguem a separação de preocupações, do que vê-los perder tempo discutindo sobre o absurdo do MV *. E por esse motivo, declaro que o AngularJS é a estrutura MVW - Model-View-Whatever . Onde o que quer que seja significa "o que funciona para você ".
O Angular oferece muita flexibilidade para separar bem a lógica da apresentação da lógica comercial e do estado da apresentação. Use-o para aumentar sua produtividade e capacidade de manutenção de aplicativos, em vez de discussões acaloradas sobre coisas que no final do dia não importam tanto.
Existem recomendações ou diretrizes para implementar o padrão de design do AngularJS MVW (Model-View-Whatever) em aplicativos do lado do cliente?
fonte
Respostas:
Graças a uma enorme quantidade de fontes valiosas, eu tenho algumas recomendações gerais para implementar componentes nos aplicativos AngularJS:
Controlador
O controlador deve ser apenas um interlayer entre o modelo e a visualização. Tente torná-lo o mais fino possível.
É altamente recomendável evitar a lógica de negócios no controlador. Deve ser movido para o modelo.
O controlador pode se comunicar com outros controladores usando a invocação de método (possível quando os filhos desejam se comunicar com os pais) ou os métodos $ emit , $ broadcast e $ on . As mensagens emitidas e transmitidas devem ser reduzidas ao mínimo.
O controlador não deve se preocupar com a apresentação ou manipulação do DOM.
Tente evitar controladores aninhados . Nesse caso, o controlador pai é interpretado como modelo. Injete modelos como serviços compartilhados.
O escopo no controlador deve ser usado para vincular o modelo com vista e
encapsular o modelo de vista como para o padrão de design do modelo de apresentação .
Escopo
Trate o escopo como somente leitura em modelos e somente gravação em controladores . O objetivo do escopo é se referir ao modelo, não ao modelo.
Ao fazer a ligação bidirecional (modelo ng), certifique-se de não ligar diretamente às propriedades do escopo.
Modelo
O modelo no AngularJS é um singleton definido pelo serviço .
O modelo fornece uma excelente maneira de separar dados e exibição.
Os modelos são candidatos principais para testes de unidade, pois normalmente possuem exatamente uma dependência (alguma forma de emissor de evento, no caso comum o $ rootScope ) e contêm lógica de domínio altamente testável .
O modelo deve ser considerado como uma implementação de determinada unidade. É baseado no princípio de responsabilidade única. Unidade é uma instância que é responsável por seu próprio escopo de lógica relacionada, que pode representar uma entidade única no mundo real e descrevê-la no mundo da programação em termos de dados e estado .
O modelo deve encapsular os dados do aplicativo e fornecer uma API para acessar e manipular esses dados.
O modelo deve ser portátil para que possa ser facilmente transportado para aplicação semelhante.
Ao isolar a lógica da unidade em seu modelo, você facilitou a localização, atualização e manutenção.
O modelo pode usar métodos de modelos globais mais gerais, comuns a todo o aplicativo.
Tente evitar a composição de outros modelos em seu modelo usando injeção de dependência, se não for realmente dependente de diminuir o acoplamento de componentes e aumentar a testabilidade e a usabilidade da unidade .
Tente evitar o uso de listeners de eventos nos modelos. Isso os torna mais difíceis de testar e geralmente mata modelos em termos de princípio de responsabilidade única.
Implementação do modelo
Como o modelo deve encapsular alguma lógica em termos de dados e estado, ele deve restringir arquitetonicamente o acesso a seus membros, para que possamos garantir um acoplamento flexível.
A maneira de fazer isso no aplicativo AngularJS é defini-lo usando o tipo de serviço de fábrica . Isso nos permitirá definir propriedades e métodos privados com muita facilidade e também retornar os acessíveis publicamente em um único local, o que o tornará realmente legível para o desenvolvedor.
Um exemplo :
Criando novas instâncias
Tente evitar ter uma fábrica que retorne uma nova função capaz, pois isso começa a interromper a injeção de dependência e a biblioteca se comportará de maneira desajeitada, principalmente para terceiros.
Uma maneira melhor de realizar a mesma coisa é usar a fábrica como uma API para retornar uma coleção de objetos com métodos getter e setter anexados a eles.
Modelo Global
Em geral, tente evitar tais situações e projetar seus modelos adequadamente, para que possam ser injetados no controlador e usados em sua visão.
Em particular, alguns métodos requerem acessibilidade global dentro do aplicativo. Para tornar isso possível, você pode definir a propriedade ' common ' em $ rootScope e vinculá-la ao commonModel durante a inicialização do aplicativo:
Todos os seus métodos globais viverão dentro de propriedades ' comuns '. Este é algum tipo de espaço para nome .
Mas não defina nenhum método diretamente no seu $ rootScope . Isso pode levar a um comportamento inesperado quando usado com a diretiva ngModel dentro do seu escopo de exibição, geralmente desarrumando seu escopo e levando a métodos de escopo a substituir problemas.
Recurso
O recurso permite que você interaja com diferentes fontes de dados .
Deve ser implementado usando o princípio de responsabilidade única .
Em particular, é um proxy reutilizável para terminais HTTP / JSON.
Os recursos são injetados nos modelos e oferecem a possibilidade de enviar / recuperar dados.
Implementação de recursos
Uma fábrica que cria um objeto de recurso que permite interagir com fontes de dados RESTful do lado do servidor.
O objeto de recurso retornado possui métodos de ação que fornecem comportamentos de alto nível sem a necessidade de interagir com o serviço $ http de baixo nível.
Serviços
Modelo e recurso são serviços .
Os serviços não são associados, são fracamente acoplados unidades de funcionalidade que são independentes.
Os serviços são um recurso que o Angular traz para os aplicativos Web do lado do cliente, do lado do servidor, onde os serviços costumam ser usados há muito tempo.
Serviços em aplicativos angulares são objetos substituíveis que são conectados usando injeção de dependência.
Angular vem com diferentes tipos de serviços. Cada um com seus próprios casos de uso. Leia Noções básicas sobre tipos de serviço para obter detalhes.
Tente considerar os principais princípios da arquitetura de serviço em seu aplicativo.
Em geral, de acordo com o Glossário de Serviços da Web :
Estrutura do lado do cliente
Em geral, o lado do cliente do aplicativo é dividido em módulos . Cada módulo deve ser testável como uma unidade.
Tente definir módulos dependendo do recurso / funcionalidade ou exibição , não por tipo. Veja a apresentação de Misko para detalhes.
Os componentes do módulo podem ser agrupados convencionalmente por tipos como controladores, modelos, visualizações, filtros, diretivas etc.
Mas o próprio módulo permanece reutilizável , transferível e testável .
Também é muito mais fácil para os desenvolvedores encontrarem algumas partes do código e todas as suas dependências.
Por favor, consulte Organização de código em aplicativos AngularJS e JavaScript grandes para obter detalhes.
Um exemplo de estruturação de pastas :
Um bom exemplo de estruturação angular de aplicativos é implementado pelo angular-app - https://github.com/angular-app/angular-app/tree/master/client/src
Isso também é considerado pelos geradores de aplicativos modernos - https://github.com/yeoman/generator-angular/issues/109
fonte
searchModel
não segue os conselhos de reutilização. Seria melhor a importação das constantes através doconstant
serviço. 3. Qualquer explicação que se entende aqui ?:Try to avoid having a factory that returns a new able function
prototype
quebras de propriedade da herança, em vez pode-se usarCar.prototype.save = ...
object
expressão de ligação bidirecional para garantir que você escreva na propriedade ousetter
função exata . No caso de usar a propriedade direta do seu escopo ( sem um ponto ), você corre o risco de ocultar a propriedade de destino desejada com a recém-criada no escopo superior mais próximo da cadeia de protótipos ao escrever nela. Isto é melhor explicado na apresentação do MiskoAcredito que a opinião de Igor sobre isso, como pode ser visto na citação que você forneceu, seja apenas a ponta do iceberg de um problema muito maior.
MVC e seus derivados (MVP, PM, MVVM) são bons e elegantes dentro de um único agente, mas uma arquitetura servidor-cliente é para todos os efeitos um sistema de dois agentes, e as pessoas geralmente são tão obcecadas com esses padrões que esquecem que o problema em questão é muito mais complexo. Ao tentar aderir a esses princípios, eles acabam tendo uma arquitetura defeituosa.
Vamos fazer isso pouco a pouco.
As diretrizes
Visualizações
Dentro do contexto Angular, a visualização é o DOM. As diretrizes são:
Faz:
Não faça:
Tão tentador, curto e inofensivo:
Significa praticamente qualquer desenvolvedor que agora compreenda como o sistema funciona para inspecionar os arquivos Javascript e HTML.
Controladores
Faz:
Não faça:
A razão para a última diretriz é que controladores são irmãs de visões, não entidades; nem são reutilizáveis.
Você poderia argumentar que as diretivas são reutilizáveis, mas as diretivas também são irmãs de visualizações (DOM) - elas nunca foram destinadas a corresponder a entidades.
Claro, algumas vezes as visualizações representam entidades, mas esse é um caso bastante específico.
Em outras palavras, os controladores devem se concentrar na apresentação - se você aplicar a lógica de negócios, não apenas terá um controlador inflável e pouco gerenciável, mas também violará o princípio da separação de interesses .
Como tal, os controladores no Angular são realmente mais do Presentation Model ou MVVM .
E assim, se os controladores não deveriam lidar com a lógica de negócios, quem deveria?
O que é um modelo?
Seu modelo de cliente geralmente é parcial e obsoleto
A menos que você esteja escrevendo um aplicativo Web offline ou um aplicativo extremamente simples (poucas entidades), é altamente provável que seu modelo de cliente seja:
O modelo real deve persistir
No MCV tradicional, o modelo é a única coisa que persiste . Sempre que falamos de modelos, eles devem ser persistidos em algum momento. Seu cliente pode manipular modelos à vontade, mas até que a ida e volta ao servidor seja concluída com êxito, o trabalho não será concluído.
Consequências
Os dois pontos acima devem servir como cautela - o modelo que seu cliente possui pode envolver apenas uma lógica comercial parcial, na maioria simples.
Como tal, talvez seja sábio, dentro do contexto do cliente, usar letras minúsculas
M
- portanto, é realmente mVC , mVP e mVVm . O grandeM
é para o servidor.Logíca de negócios
Talvez um dos conceitos mais importantes sobre modelos de negócios seja que você pode subdividi-los em dois tipos (eu omito a terceira visão de negócios, pois essa é uma história para outro dia):
firstName
esirName
propriedades, um getter comogetFullName()
pode ser considerado independente de aplicativo.É importante enfatizar que ambos no contexto do cliente não são lógicas de negócios 'reais' - eles lidam apenas com a parte importante para o cliente. A lógica do aplicativo (não a lógica do domínio) deve ter a responsabilidade de facilitar a comunicação com o servidor e a maior parte da interação do usuário; enquanto a lógica do domínio é amplamente em pequena escala, específica da entidade e orientada à apresentação.
A questão ainda permanece - onde você as joga dentro de uma aplicação angular?
Arquitetura de 3 vs 4 camadas
Todas essas estruturas MVW usam 3 camadas:
Mas há duas questões fundamentais com isso quando se trata de clientes:
Uma alternativa para essa estratégia é a estratégia de 4 camadas :
O negócio real aqui é a camada de regras de negócios do aplicativo (Casos de Uso), que geralmente fica errada nos clientes.
Essa camada é realizada pelos interatores (tio Bob), que é praticamente o que Martin Fowler chama de camada de serviço de script de operação .
Exemplo concreto
Considere o seguinte aplicativo da web:
Algumas coisas devem acontecer agora:
Onde jogamos tudo isso?
Se sua arquitetura envolve um controlador que chama
$resource
, tudo isso acontecerá dentro do controlador. Mas existe uma estratégia melhor.Uma solução proposta
O diagrama a seguir mostra como o problema acima pode ser resolvido adicionando outra camada lógica do aplicativo nos clientes Angular:
Então, adicionamos uma camada entre o controlador ao $ resource, essa camada (vamos chamá-lo de interator ):
UserInteractor
.E assim, com os requisitos do exemplo concreto acima:
validate()
validate()
método de modelo .createUser()
fonte
Um pequeno problema em comparação aos grandes conselhos da resposta de Artem, mas em termos de legibilidade do código, achei melhor definir a API completamente dentro do
return
objeto, para minimizar a alternância entre o código e a aparência das variáveis definidas:Se o
return
objeto parecer "muito cheio", isso é um sinal de que o Serviço está fazendo muito.fonte
O AngularJS não implementa o MVC da maneira tradicional, mas implementa algo mais próximo do MVVM (Model-View-ViewModel), o ViewModel também pode ser chamado de fichário (no caso angular, pode ser $ scope). O Modelo -> Como sabemos, o modelo em angular pode ser apenas objetos JS antigos simples ou os dados em nosso aplicativo
A View -> a view em angularJS é o HTML que foi analisado e compilado por angularJS aplicando as diretivas, instruções ou ligações. O ponto principal aqui é que a entrada não é apenas a string HTML simples (innerHTML), mas sim é o DOM criado pelo navegador.
O ViewModel -> ViewModel é na verdade o fichário / ponte entre sua visualização e modelo no caso do angularJS, é $ scope, para inicializar e aumentar o $ scope que usamos Controller.
Se eu quiser resumir a resposta: No aplicativo angularJS, $ scope tem referência aos dados, o Controller controla o comportamento e o View lida com o layout, interagindo com o controller para se comportar de acordo.
fonte
Para ser mais preciso, a Angular usa diferentes padrões de design que já encontramos em nossa programação regular. 1) Quando registramos nossos controladores ou diretivas, fábrica, serviços etc. em relação ao nosso módulo. Aqui está ocultando os dados do espaço global. Qual é o padrão do módulo . 2) Quando o angular usa sua verificação suja para comparar as variáveis de escopo, aqui ele usa o Padrão do Observador . 3) Todos os escopos filho pai em nossos controladores usam padrão Prototypal. 4) No caso de injetar os serviços, utiliza o Padrão de Fábrica .
No geral, ele usa diferentes padrões de design conhecidos para resolver os problemas.
fonte