MVVM es una curita para capas de enlace de datos mal diseñadas. En particular, se ha visto mucho uso en el mundo WPF / silverlight / WP7 debido a las limitaciones en el enlace de datos en WPF / XAML.
De ahora en adelante, voy a asumir que estamos hablando de WPF / XAML ya que esto aclarará las cosas. Veamos algunas de las deficiencias que MVVM se propone resolver en WPF / XAML.
Forma de datos vs forma de IU
La 'VM' en MVVM crea un conjunto de objetos definidos en C # que se asignan a un conjunto de objetos de presentación definidos en XAML. Estos objetos de C # generalmente están conectados a XAML a través de las propiedades de DataContext en los objetos de presentación.
Como resultado, el gráfico del objeto viewmodel necesita mapearse en el gráfico del objeto de presentación de su aplicación. Eso no quiere decir que la asignación debe ser uno a uno, pero si un control de lista está contenido por un control de ventana, entonces debe haber una manera de pasar del objeto DataContext de la ventana a un objeto que describa los datos de esa lista.
El gráfico de objeto de modelo de vista desacopla el gráfico de objeto de modelo del gráfico de objeto de interfaz de usuario con éxito, pero a expensas de una capa de modelo de vista adicional que debe construirse y mantenerse.
Si quiero mover algunos datos de la pantalla A a la pantalla B, necesito jugar con los modelos de vista. En la mente de un hombre de negocios, este es un cambio de interfaz de usuario. Se debe tener lugar exclusivamente en el mundo de XAML. Lamentablemente, rara vez puede. Peor aún, dependiendo de cómo estén estructurados los modelos de vista y de cuán activamente cambien los datos, podría requerirse un poco de enrutamiento de datos para lograr este cambio.
Trabajando alrededor del enlace de datos no expresivo
Los enlaces WPF / XAML son insuficientemente expresivos. Básicamente, puede proporcionar una forma de llegar a un objeto, una ruta de propiedad para atravesar y convertir convertidores para adaptar el valor de la propiedad de datos a lo que requiere el objeto de presentación.
Si necesita vincular una propiedad en C # a algo más complejo que eso, básicamente no tiene suerte. Nunca he visto una aplicación WPF sin un convertidor vinculante que se haya convertido en verdadero / falso en Visible / Contraído. Muchas aplicaciones WPF también tienden a tener algo llamado NegatingVisibilityConverter o similar que cambia la polaridad. Esto debería estar activando las alarmas.
MVVM le brinda pautas para estructurar su código C # que puede usarse para suavizar esta limitación. Puede exponer una propiedad en su modelo de vista llamada SomeButtonVisibility y simplemente vincularla a la visibilidad de ese botón. Su XAML es agradable y bonito ahora ... pero se ha convertido en un empleado; ahora tiene que exponer + actualizar enlaces en dos lugares (la interfaz de usuario y el código en C #) cuando su interfaz de usuario evoluciona. Si necesita el mismo botón para estar en otra pantalla, debe exponer una propiedad similar en un modelo de vista al que pueda acceder esa pantalla. Peor aún, no puedo simplemente mirar el XAML y ver cuándo el botón ya estará visible. Tan pronto como los enlaces se vuelvan un poco triviales, tengo que hacer un trabajo de detective en el código C #.
El acceso a los datos tiene un alcance agresivo
Dado que los datos generalmente ingresan a la interfaz de usuario a través de las propiedades de DataContext, es difícil representar datos globales o de sesión de manera consistente en toda la aplicación.
La idea del "usuario actualmente conectado" es un gran ejemplo: a menudo es algo realmente global en una instancia de su aplicación. En WPF / XAML es muy difícil garantizar el acceso global al usuario actual de manera consistente.
Lo que me gustaría hacer es usar la palabra "CurrentUser" en los enlaces de datos libremente para referirse al usuario actualmente conectado. En cambio, tengo que asegurarme de que cada DataContext me da una manera de llegar al objeto de usuario actual. MVVM puede acomodar esto, pero los modelos de vista van a ser un desastre ya que todos tienen que proporcionar acceso a estos datos globales.
Un ejemplo donde MVVM se cae
Digamos que tenemos una lista de usuarios. Al lado de cada usuario, queremos mostrar un botón de "eliminar usuario", pero solo si el usuario actualmente conectado es un administrador. Además, los usuarios no pueden borrarse a sí mismos.
Los objetos de su modelo no deberían saber sobre el usuario actualmente conectado: solo representarán los registros de usuario en su base de datos, pero de alguna manera el usuario actualmente conectado debe estar expuesto a enlaces de datos dentro de las filas de su lista. MVVM dicta que debemos crear un objeto de modelo de vista para cada fila de la lista que compone el usuario actualmente conectado con el usuario representado por esa fila de lista, luego exponer una propiedad llamada "DeleteButtonVisibility" o "CanDelete" en ese objeto de modelo de vista (dependiendo de sus sentimientos) sobre convertidores vinculantes).
Este objeto se parecerá muchísimo a un objeto Usuario en la mayoría de las otras formas: es posible que deba reflejar todas las propiedades del objeto modelo del usuario y reenviar actualizaciones a esos datos a medida que cambien. Esto se siente realmente asqueroso: de nuevo, MVVM lo convierte en un empleado al obligarlo a mantener este objeto similar al del usuario.
Considere: probablemente también tenga que representar las propiedades de su usuario en una base de datos, el modelo y la vista. Si tiene una API entre usted y su base de datos, entonces es peor: están representados en la base de datos, el servidor API, el cliente API, el modelo y la vista. Realmente dudaría en adoptar un patrón de diseño que agregue otra capa que deba tocarse cada vez que se agregue o cambie una propiedad.
Peor aún, esta capa se escala con la complejidad de su interfaz de usuario, no con la complejidad de su modelo de datos. A menudo, los mismos datos se representan en muchos lugares y en su interfaz de usuario; esto no solo agrega una capa, sino que agrega una capa con una gran superficie adicional.
Cómo podrían haber sido las cosas
En el caso descrito anteriormente, me gustaría decir:
<Button Visibility="{CurrentUser.IsAdmin && CurrentUser.Id != Id}" ... />
CurrentUser estaría expuesto globalmente a todos los XAML en mi aplicación. Id haría referencia a una propiedad en el DataContext para mi fila de la lista. La visibilidad se convertiría de booleana automáticamente. Cualquier actualización de Id, CurrentUser.IsAdmin, CurrentUser o CurrentUser.Id activaría una actualización de la visibilidad de este botón. Pan comido.
En cambio, WPF / XAML obliga a sus usuarios a crear un desastre completo. Por lo que puedo decir, algunos bloggers creativos le pusieron un nombre a ese desastre y ese nombre era MVVM. No se deje engañar: no está en la misma clase que los patrones de diseño de GoF. Este es un truco feo para evitar un sistema de enlace de datos feo.
(Este enfoque a veces se conoce como "Programación funcional reactiva" en caso de que esté buscando lecturas adicionales).
En conclusión
Si debe trabajar en WPF / XAML, todavía no recomiendo MVVM.
Desea que su código esté estructurado como en el ejemplo anterior "cómo podrían haber sido las cosas": modelo expuesto directamente a la vista, con expresiones de enlace de datos complejas + coacciones de valor flexibles. Es mucho mejor: más legible, más grabable y más fácil de mantener.
MVVM le dice que estructurar su código de una manera más detallada y menos mantenible.
En lugar de MVVM, cree algunas cosas para ayudarlo a aproximarse a la buena experiencia: desarrolle una convención para exponer el estado global a su IU de manera consistente. Construya algunas herramientas con convertidores de enlace, MultiBinding, etc. que le permitan expresar expresiones de enlace más complejas. Construya una biblioteca de convertidores vinculantes para ayudar a que los casos comunes de coerción sean menos dolorosos.
Aún mejor: reemplace XAML con algo más expresivo. XAML es un formato XML muy simple para crear instancias de objetos C #: no sería difícil encontrar una variante más expresiva.
Mi otra recomendación: no use kits de herramientas que fuercen este tipo de compromisos. Dañarán la calidad de su producto final al empujarlo hacia basura como MVVM en lugar de centrarse en su dominio problemático.