¿Existe un buen patrón formal para administrar el estado en MVVM?

21

He comenzado a aprender sobre Redux y React en el mundo web, y cuanto más aprendo sobre él, más me doy cuenta de lo dolorosa que es la administración de estado en el mundo de los escritorios con la arquitectura de estilo MVVM de WPF (usando Caliburn específicamente para vincular Vistas a ViewModels).

Redux tiene algunos principios simples que dictan cómo se debe administrar el estado, haciendo que las actualizaciones de la interfaz de usuario, el manejo de eventos y los cambios de estado sean mucho más predecibles. Los principios son:

  • Una única fuente de verdad (todo el estado mutable se almacena en un único objeto compartido).
  • El estado es de solo lectura. Los componentes no pueden modificarlo en todo el código, que normalmente es lo que sucede en WPF.
  • El estado solo puede ser modificado por funciones puras.

La arquitectura MVVM de WPF le permite crear vistas interactivas muy rápidamente, pero los problemas de depuración cuando varios modelos de vista y eventos cambian de estado es una pesadilla. Por ejemplo: se activó un evento que cambió una vista e intentó establecer una pestaña predeterminada, pero los datos no terminaron de cargarse de forma asíncrona desde un servicio web, por lo que la pestaña no existe (todavía), por lo que no sucede nada

He pasado horas dibujando diagramas para tratar de comprender interacciones complejas entre componentes viewModels interrelacionados que se actualizan entre sí.

Entiendo que Redux tiene como objetivo resolver parte de esta imprevisibilidad del estado. ¿Hay algo similar o un patrón arquitectónico que encajaría bien con WPF para ayudar a administrar mejor el estado? No estoy seguro de qué tan bien funcionarían los principios de Redux en .NET ya que aún no los he probado. ¿Quizás alguien tiene alguna experiencia que pueda dar algún consejo?

willem
fuente
Tenemos un tipo similar de problemas en el navegador. El Javascript directo se ejecutará tan pronto y el DOM aún no está construido, por lo que no se pueden encontrar elementos de la interfaz de usuario. Afortunadamente, hay una serie de eventos que podemos utilizar para activar la ejecución retrasada de un script hasta que otras cosas estén más avanzadas. (Como DOMContentLoaded.)
Erik Eidt
1
El estado en redux se actualiza realmente, nunca se modifica.
Andy
1
Sé que llego tarde a la fiesta, pero hay un proyecto llamado React.NET que trae la arquitectura Redux a .NET.
SiberianGuy
Para aquellos a quienes les gusta el enfoque de ngrx / store en proyectos angulares, existe NetRx.Store : administración de estado para proyectos .Net, inspirada en ngrx / store. Lo puedes encontrar en Nuget también. También hay una buena muestra de uso NetRx.Store con patrón MVVM en el proyecto WPF
Vitalii Ilchenko

Respuestas:

8

Creo que sé a qué te refieres. Básicamente, usted resuelve el problema agregando un modelo de vista 'controlador' o 'maestro' (excusa psudocódigo)

es decir

public class MasterVM
{
    public ChildVM View1 {get;set;}
    public ChildVM View2 {get;set;}

    private Data data;
    public MasterVM()
    {
        View1.OnEvent += updateData;
    }

    private Action<int> updateData(int value)
    {
         View2.Value = value;
    }
}

cuando haces esto con el patrón de mediador, pienso en la clase como un controlador. es decir.

public class Controller
{
    public Controller(MediatorService m)
    {
        m.Subscribe("valueupdated", updateData);
    }

    private Action<int> updateData(int value)
    {
         m.Publish("showvalue", value);
    }
}

public class View2
{
    public View2(MediatorService m)
    {
        m.Subscribe("showvalue", (int v)=> {Value = v;});
    }
}

Este tipo de cosas le permite poner su 'lógica de flujo' u Orquestación de eventos en estas clases persistentes de alto nivel y mantener el código de VMs ligero. Si desea cambiar "cuando el usuario hace clic en COMPRAR, el pedido se procesa" cosas que sabe que debe buscar en el "OrderFlowController" o "OrderProcessVM" o como quiera nombrarlos. En lugar de una combinación de BasketVM, PaymentVM, 3dSecureVM, etc., etc.

Entonces, en su ejemplo específico de la 'pestaña aún no lista', es posible que tenga

public class Controller
{
    bool dataLoadCompleted;
    public Controller(MediatorService m)
    {
        m.Subscribe("setTabRequest", setTab); //message from view model with set tab button
        m.Subscribe("dataLoadComplete", dataLoadComplete); //message from data loading view model or some other controller?
    }

    private Action<int> setTab(int value)
    {
         if(!dataLoadCompleted)
         {
             m.Publish("error", "Please wait for data to load"); //message for error alert view model
         }
         else
         {
             m.Publish("setDefaultTab", value); //message for tab viewmodel
         }
    }

    private Action dataLoadComplete()
    {
         //persist state;
         dataLoadCompleted = true;
    }
}
Ewan
fuente