En un mail reciente de MSDN apareció un artículo que toca un tema de diseño interesante.
Y es el patrón Model-ViewModel-Model, en el artículo se hace referencia a su implementación en WPF/Silverlight, sin embargo es un tema con el que seguramente muchos se topen al incursionar en Asp.Net Mvc, y en realidad es imposible de esquivar en cualquier implementación de MVC, ya sea en Asp.Net, WinForms, Ruby o Haskell.
Para hacerlo menos abstracto, pongamos un ejemplo, imaginemos que nuestros amigos de Facebook® quieren construir la pagina de perfil de un usuario:
supongamos también que utilizan MVC y construyen su "Model" de la siguiente forma:
La duda es cómo obtener los datos resaltados en amarillo en la captura de ejemplo
Estas clases extienden a las anteriores con campos calculados, listas filtradas, consolidaciones, u otro tipo de transformaciones sobre el modelo original.
Para ilustrar ésta sería una versión (muy naive) de la nueva propiedad "CommonFriends" en UserVM:
Si usted ya estuvo agregando este tipo de campos calculados y transformaciones, y se sentía culpable por haber violado la prístina pureza de su modelo MVC
siéntase liberado de la culpa!, esto ya tiene nombre y se llama ViewModel!
Véase este gráfico explicativo que tomé prestado de un artículo que resume muy bien el tema
Rápidamente puede verse el problema burocrático en que nos pone este enfoque, ya que implica tener para casi todas las clases del modelo, una equivalente en ViewModel, con wrappers para todas las propiedades visibles. Imagínense la flagrante violación al principio DRY que supone crear páginas y páginas de código como este:
Tiene que haber algo mejor!
Existen también librerías muy famosas para la creación de proxies dinámicos (que funcionan con .Net 2.0), como por ejemplo las que son usadas en NHibernate (para crear proxies que soporten lazy loading):
que hacen fácil agregarle a estos proxies interceptores en getters y setters (u otros métodos) que implementen notificación de cambios, logging, autorización y otras cosas por el estilo.
Y finalmente...KISS! es importante recordar que puede resultar más que suficiente con mantener las clases en el modelo accesibles a las vistas, y las clases "ViewModel" mezcladas con el "Model", o separadas simplemente por namespace.
Y es el patrón Model-ViewModel-Model, en el artículo se hace referencia a su implementación en WPF/Silverlight, sin embargo es un tema con el que seguramente muchos se topen al incursionar en Asp.Net Mvc, y en realidad es imposible de esquivar en cualquier implementación de MVC, ya sea en Asp.Net, WinForms, Ruby o Haskell.
ViewModels
Cuando trabajamos con un modelo MVC, especialmente si elegimos algún mecanismo de DataBinding entre M y V (y en un sentido amplio de la palabra consideremos los templates (como las vistas de Asp.Net Mvc) como un caso particular de DataBinding), resulta inmediato que cualquier aplicación que escape de lo elemental necesita algun tipo de transformación entre un Modelo y su Vista, ahi es donde llegan al rescate los ViewModels.Para hacerlo menos abstracto, pongamos un ejemplo, imaginemos que nuestros amigos de Facebook® quieren construir la pagina de perfil de un usuario:
Perfil de Usuario |
Nota: el modelo real de Facebook® podría ser más complejo |
- Antigüedad del mensaje de estado ("lunes pasado").
- Ubicación ("Villa Devoto" en lugar de el GUID correspondiente)
- Amigos en común (con el usuario logueado actualmente)
Estas clases extienden a las anteriores con campos calculados, listas filtradas, consolidaciones, u otro tipo de transformaciones sobre el modelo original.
Para ilustrar ésta sería una versión (muy naive) de la nueva propiedad "CommonFriends" en UserVM:
public IList<UserVM> CommonFriends { get { return this.Friends .Where(f => f.Friends .Any(ff => ff.UserID == User.CurrentUserID)) .ToList(); } }
Si usted ya estuvo agregando este tipo de campos calculados y transformaciones, y se sentía culpable por haber violado la prístina pureza de su modelo MVC
siéntase liberado de la culpa!, esto ya tiene nombre y se llama ViewModel!
Separación Model-ViewModel-Model
Por supuesto en esto podemos imaginar un gradiente de creciente complejidad:- Propiedades agregadas directamente a las entidades de nuestro modelo
- Algunas clases nuevas y propiedades agregadas a entidades que descienden de las de nuestro modelo
- Una capa de ViewModels que se interpone y separa completamente View de Model.
Véase este gráfico explicativo que tomé prestado de un artículo que resume muy bien el tema
Rápidamente puede verse el problema burocrático en que nos pone este enfoque, ya que implica tener para casi todas las clases del modelo, una equivalente en ViewModel, con wrappers para todas las propiedades visibles. Imagínense la flagrante violación al principio DRY que supone crear páginas y páginas de código como este:
public string Fullname { get { return InnerEntity.Fullname; } set { InnerEntity.Fullname = value; } }
Tiene que haber algo mejor!
Dynamic Proxies
Como dice la máxima "El mejor código es aquel que no se escribe", y el artículo al que hice referencia propone generar estos ViewModels como proxies dinámicos, implementar esos "gets" y "sets" automáticamente con el nuevo tipo dynamic en .Net 4.0, un poco de Reflection y unos atributos muy elegantes para declarar dependencias entre propiedades (ej: Edad <-depende de-> FechaDeNacimiento).Existen también librerías muy famosas para la creación de proxies dinámicos (que funcionan con .Net 2.0), como por ejemplo las que son usadas en NHibernate (para crear proxies que soporten lazy loading):
que hacen fácil agregarle a estos proxies interceptores en getters y setters (u otros métodos) que implementen notificación de cambios, logging, autorización y otras cosas por el estilo.
Y finalmente...KISS! es importante recordar que puede resultar más que suficiente con mantener las clases en el modelo accesibles a las vistas, y las clases "ViewModel" mezcladas con el "Model", o separadas simplemente por namespace.