En el contexto de esta publicación de Igor Minar, líder de AngularJS:
MVC vs MVVM vs MVP . Qué tema tan controvertido que muchos desarrolladores pueden pasar horas y horas debatiendo y discutiendo.
Durante varios años, AngularJS estuvo más cerca de MVC (o más bien una de sus variantes del lado del cliente), pero con el tiempo y gracias a muchas refactorizaciones y mejoras de la API, ahora está más cerca de MVVM : el objeto $ scope podría considerarse el ViewModel que se está utilizando decorado por una función que llamamos un controlador .
Ser capaz de clasificar un marco y ponerlo en uno de los cubos MV * tiene algunas ventajas. Puede ayudar a los desarrolladores a sentirse más cómodos con sus apis al facilitar la creación de un modelo mental que represente la aplicación que se está creando con el marco. También puede ayudar a establecer la terminología utilizada por los desarrolladores.
Una vez dicho esto, prefiero ver a los desarrolladores crear aplicaciones innovadoras que estén bien diseñadas y seguir la separación de preocupaciones, que verlas perder el tiempo discutiendo sobre las tonterías de MV *. Y por esta razón, declaro que AngularJS es el marco MVW: Model-View-Whatever . Donde lo que significa " lo que sea que funcione para usted "
Angular le brinda mucha flexibilidad para separar muy bien la lógica de presentación de la lógica de negocios y el estado de presentación. Úselo para aumentar su productividad y el mantenimiento de la aplicación en lugar de acaloradas discusiones sobre cosas que al final del día no importan tanto.
¿Hay alguna recomendación o guía para implementar el patrón de diseño AngularJS MVW (Model-View-Whatever) en aplicaciones del lado del cliente?
fuente
Respuestas:
Gracias a una gran cantidad de fuentes valiosas, tengo algunas recomendaciones generales para implementar componentes en aplicaciones AngularJS:
Controlador
El controlador debe ser solo una capa intermedia entre el modelo y la vista. Intenta hacerlo lo más delgado posible.
Se recomienda evitar la lógica de negocios en el controlador. Se debe mover al modelo.
El controlador puede comunicarse con otros controladores mediante la invocación de métodos (posible cuando los niños desean comunicarse con los padres) o $ emit , $ broadcast y $ on . Los mensajes emitidos y emitidos deben mantenerse al mínimo.
El controlador no debería preocuparse por la presentación o la manipulación DOM.
Intenta evitar los controladores anidados . En este caso, el controlador principal se interpreta como modelo. Inyecte modelos como servicios compartidos en su lugar.
Ámbito de aplicación en el controlador se debe utilizar para la unión modelo con vista y
encapsular Ver Modelo como para Presentación modelo patrón de diseño.
Alcance
Trate el alcance como de solo lectura en las plantillas y de solo escritura en los controladores . El propósito del alcance es referirse al modelo, no ser el modelo.
Al hacer un enlace bidireccional (ng-model) asegúrese de no enlazar directamente a las propiedades del alcance.
Modelo
El modelo en AngularJS es un singleton definido por servicio .
El modelo proporciona una forma excelente de separar datos y mostrarlos.
Los modelos son candidatos principales para pruebas unitarias, ya que generalmente tienen exactamente una dependencia (alguna forma de emisor de eventos, en el caso común $ rootScope ) y contienen una lógica de dominio altamente comprobable .
El modelo debe considerarse como una implementación de una unidad particular. Se basa en el principio de responsabilidad única. La unidad es una instancia responsable de su propio alcance de lógica relacionada que puede representar una entidad única en el mundo real y describirla en el mundo de la programación en términos de datos y estado .
El modelo debe encapsular los datos de su aplicación y proporcionar una API para acceder y manipular esos datos.
El modelo debe ser portátil para que pueda transportarse fácilmente a una aplicación similar.
Al aislar la lógica de la unidad en su modelo, ha facilitado la localización, actualización y mantenimiento.
Model puede usar métodos de modelos globales más generales que son comunes para toda la aplicación.
Intente evitar la composición de otros modelos en su modelo utilizando inyección de dependencia si no es realmente dependiente para disminuir el acoplamiento de componentes y aumentar la capacidad de prueba y usabilidad de la unidad .
Trate de evitar el uso de oyentes de eventos en modelos. Los hace más difíciles de probar y generalmente mata modelos en términos del principio de responsabilidad única.
Implementación del modelo
Como el modelo debería encapsular cierta lógica en términos de datos y estado, debería restringir arquitectónicamente el acceso a sus miembros para que podamos garantizar un acoplamiento flexible.
La forma de hacerlo en la aplicación AngularJS es definirlo usando el tipo de servicio de fábrica . Esto nos permitirá definir propiedades y métodos privados de manera muy fácil y también devolver los de acceso público en un solo lugar que lo hará realmente legible para el desarrollador.
Un ejemplo :
Crear nuevas instancias
Intente evitar tener una fábrica que devuelva una nueva función capaz, ya que esto comienza a descomponer la inyección de dependencia y la biblioteca se comportará de manera incómoda, especialmente para terceros.
Una mejor manera de lograr lo mismo es usar la fábrica como API para devolver una colección de objetos con métodos getter y setter adjuntos.
Modelo global
En general, trate de evitar tales situaciones y diseñe sus modelos correctamente para que pueda inyectarse en el controlador y utilizarse a su vista.
En particular, algunos métodos requieren accesibilidad global dentro de la aplicación. Para hacerlo posible, puede definir la propiedad ' común ' en $ rootScope y vincularla a commonModel durante el arranque de la aplicación:
Todos sus métodos globales vivirán dentro de la propiedad ' común '. Este es algún tipo de espacio de nombres .
Pero no defina ningún método directamente en su $ rootScope . Esto puede conducir a un comportamiento inesperado cuando se usa con la directiva ngModel dentro de su alcance de vista, generalmente ensuciando su alcance y conduce a problemas de anulación de métodos de alcance.
Recurso
Resource le permite interactuar con diferentes fuentes de datos .
Debe implementarse utilizando el principio de responsabilidad única .
En particular, es un proxy reutilizable para puntos finales HTTP / JSON.
Los recursos se inyectan en modelos y brindan la posibilidad de enviar / recuperar datos.
Implementación de recursos
Una fábrica que crea un objeto de recurso que le permite interactuar con fuentes de datos RESTful del lado del servidor.
El objeto de recurso devuelto tiene métodos de acción que proporcionan comportamientos de alto nivel sin la necesidad de interactuar con el servicio $ http de bajo nivel.
Servicios
Tanto el modelo como el recurso son servicios .
Los servicios son unidades de funcionalidad no asociadas y poco acopladas que son independientes.
Los servicios son una característica que Angular ofrece a las aplicaciones web del lado del cliente desde el lado del servidor, donde los servicios se han utilizado comúnmente durante mucho tiempo.
Los servicios en aplicaciones angulares son objetos sustituibles que se conectan entre sí mediante inyección de dependencia.
Angular viene con diferentes tipos de servicios. Cada uno con sus propios casos de uso. Lea los Tipos de servicio de comprensión para obtener más detalles.
Intente considerar los principios principales de la arquitectura de servicio en su aplicación.
En general, según el Glosario de servicios web :
Estructura del lado del cliente
En general, el lado del cliente de la aplicación se divide en módulos . Cada módulo debe ser comprobable como una unidad.
Intente definir módulos dependiendo de la característica / funcionalidad o vista , no por tipo. Vea la presentación de Misko para más detalles.
Los componentes del módulo se pueden agrupar convencionalmente por tipos como controladores, modelos, vistas, filtros, directivas, etc.
Pero el módulo en sí sigue siendo reutilizable , transferible y comprobable .
También es mucho más fácil para los desarrolladores encontrar algunas partes del código y todas sus dependencias.
Consulte la Organización del código en grandes aplicaciones AngularJS y JavaScript para más detalles.
Un ejemplo de estructuración de carpetas :
Buen ejemplo de estructuración angular de aplicaciones es implementado por angular-app - https://github.com/angular-app/angular-app/tree/master/client/src
Esto también es considerado por los generadores de aplicaciones modernas: https://github.com/yeoman/generator-angular/issues/109
fuente
searchModel
no sigue los consejos de reutilización. Sería mejor importar las constantes a través delconstant
servicio. 3. ¿Alguna explicación de lo que se quiere decir aquí ?:Try to avoid having a factory that returns a new able function
prototype
propiedad del objeto rompe la herencia, en su lugar se puede usarCar.prototype.save = ...
object
expresión de enlace bidireccional para asegurarte de escribir en la propiedad osetter
función exacta . En caso de usar la propiedad directa de su alcance ( sin un punto ), tiene el riesgo de ocultar la propiedad de destino deseada con la recién creada en el alcance superior más cercano en la cadena de prototipos al escribir en ella. Esto se explica mejor en la presentación de MiskoCreo que la opinión de Igor sobre esto, como se ve en la cita que ha proporcionado, es solo la punta del iceberg de un problema mucho mayor.
MVC y sus derivados (MVP, PM, MVVM) son buenos y elegantes dentro de un solo agente, pero una arquitectura servidor-cliente es para todos los propósitos un sistema de dos agentes, y las personas a menudo están tan obsesionadas con estos patrones que olvidan que El problema en cuestión es mucho más complejo. Al tratar de adherirse a estos principios, en realidad terminan con una arquitectura defectuosa.
Hagamos esto poco a poco.
Las directrices
Puntos de vista
Dentro del contexto angular, la vista es el DOM. Las pautas son:
Hacer:
No:
Como tentador, breve e inofensivo, esto se ve:
Significa prácticamente cualquier desarrollador que ahora, para comprender cómo funciona el sistema, necesitan inspeccionar tanto los archivos Javascript como los HTML.
Controladores
Hacer:
No:
La razón de la última directriz es que los controladores son hermanas de las vistas, no entidades; ni son reutilizables
Se podría argumentar que las directivas son reutilizables, pero las directivas también son hermanas para las vistas (DOM); nunca tuvieron la intención de corresponder a entidades.
Claro, a veces las vistas representan entidades, pero ese es un caso bastante específico.
En otras palabras, los controladores se centrarán en la presentación: si incluye la lógica empresarial, no solo es probable que termine con un controlador inflado y poco manejable, sino que también viola el principio de separación de preocupaciones .
Como tal, los controladores en Angular son realmente más de Presentation Model o MVVM .
Y entonces, si los controladores no deberían tratar con la lógica de negocios, ¿quién debería?
¿Qué es un modelo?
Su modelo de cliente es a menudo parcial y obsoleto
A menos que esté escribiendo una aplicación web fuera de línea, o una aplicación que es terriblemente simple (pocas entidades), es muy probable que su modelo de cliente sea:
El modelo real debe persistir.
En MCV tradicional, el modelo es lo único que se persiste . Siempre que hablemos de modelos, estos deben persistir en algún momento. Su cliente puede manipular modelos a voluntad, pero hasta que el viaje de ida y vuelta al servidor se haya completado con éxito, el trabajo no está hecho.
Consecuencias
Los dos puntos anteriores deberían servir como precaución: el modelo que tiene su cliente solo puede involucrar una lógica comercial parcial, en su mayoría simple.
Como tal, quizás sea aconsejable, dentro del contexto del cliente, usar minúsculas
M
, por lo que es realmente mVC , mVP y mVVm . Lo grandeM
es para el servidor.Lógica de negocios
Quizás uno de los conceptos más importantes sobre los modelos de negocio es que puede subdividirlos en 2 tipos (omito el tercer punto de vista de negocios, ya que es una historia para otro día):
firstName
ysirName
propiedades, un getter comogetFullName()
puede considerarse independiente de la aplicación.Es importante enfatizar que ambas cosas dentro del contexto del cliente no son lógicas comerciales 'reales' , solo tratan con la parte que es importante para el cliente. La lógica de la aplicación (no la lógica de dominio) debe tener la responsabilidad de facilitar la comunicación con el servidor y la mayor parte de la interacción del usuario; mientras que la lógica del dominio es en gran medida a pequeña escala, específica de la entidad y basada en la presentación.
La pregunta sigue siendo: ¿dónde los arrojas dentro de una aplicación angular?
Arquitectura de 3 vs 4 capas
Todos estos marcos MVW usan 3 capas:
Pero hay dos problemas fundamentales con esto cuando se trata de clientes:
Una alternativa a esta estrategia es la estrategia de 4 capas :
El verdadero negocio aquí es la capa de reglas de negocio de la aplicación (casos de uso), que a menudo va mal en los clientes.
Esta capa es realizada por interactores (tío Bob), que es más o menos lo que Martin Fowler llama una capa de servicio de script de operación .
Ejemplo concreto
Considere la siguiente aplicación web:
Algunas cosas deberían pasar ahora:
¿Dónde tiramos todo esto?
Si su arquitectura involucra un controlador que llama
$resource
, todo esto sucederá dentro del controlador. Pero hay una mejor estrategia.Una solución propuesta
El siguiente diagrama muestra cómo se puede resolver el problema anterior al agregar otra capa lógica de aplicación en clientes Angulares:
Entonces agregamos una capa entre el controlador a $ resource, esta capa (llamémosla interactor ):
UserInteractor
.Y así, con los requisitos del ejemplo concreto anterior:
validate()
validate()
método modelo .createUser()
fuente
Un problema menor en comparación con los excelentes consejos en la respuesta de Artem, pero en términos de legibilidad del código, encontré lo mejor para definir la API completamente dentro del
return
objeto, para minimizar el ir y venir en el código para ver las variables de desplazamiento:Si el
return
objeto se ve "demasiado lleno", es una señal de que el Servicio está haciendo demasiado.fuente
AngularJS no implementa MVC de la manera tradicional, sino que implementa algo más cercano a MVVM (Model-View-ViewModel), ViewModel también puede denominarse aglutinante (en caso angular puede ser $ scope). El Modelo -> Como sabemos, el modelo en angular puede ser simplemente objetos JS viejos o los datos en nuestra aplicación
La Vista -> la vista en angularJS es el HTML que ha sido analizado y compilado por angularJS aplicando las directivas o instrucciones o enlaces. El punto principal aquí es angular, la entrada no es solo la cadena HTML simple (innerHTML), sino es DOM creado por el navegador.
ViewModel -> ViewModel es en realidad la carpeta / puente entre su vista y modelo en caso angularJS, es $ scope, para inicializar y aumentar el $ scope que usamos Controller.
Si quiero resumir la respuesta: en la aplicación angularJS $ scope tiene referencia a los datos, el Controlador controla el comportamiento y View maneja el diseño al interactuar con el controlador para comportarse en consecuencia.
fuente
Para ser claro acerca de la pregunta, Angular utiliza diferentes patrones de diseño que ya encontramos en nuestra programación regular. 1) Cuando registramos nuestros controladores o directivas, fábrica, servicios, etc. con respecto a nuestro módulo. Aquí está ocultando los datos del espacio global. Cuál es el patrón del módulo . 2) Cuando angular usa su verificación sucia para comparar las variables de alcance, aquí usa el Patrón de Observador . 3) Todos los ámbitos padre-hijo en nuestros controladores utilizan el patrón Prototypal. 4) En caso de inyectar los servicios, utiliza Factory Pattern .
En general, utiliza diferentes patrones de diseño conocidos para resolver los problemas.
fuente