Usar objetos de negocio en modelos de vista

11

Cuando se utilizan objetos de negocio reutilizables, ¿qué se considera la mejor práctica al crear modelos de vista?

Usamos un objeto que llamamos Builderpara construir nuestros modelos de vista. Un generador para cada unidad lógica de vistas (pedidos, usuarios, etc.), donde cada unidad puede contener varios modelos de vistas diferentes (los pedidos contienen resumen, líneas de pedido, etc.).

Un constructor puede extraer datos a través de uno o más objetos comerciales estándar para construir un modelo de vista.

¿Qué se considera la mejor práctica cuando se trata de usar objetos / modelos comerciales en modelos de vista?

Enfoque 1

¿Permitir el uso de objetos comerciales en el modelo de vista?

//Business object in some library
public class Order
{
    public int OrderNum;
    public int NumOrderLines;
    //...
}

//Order builder in website
public class OrderBuilder
{
    public OrderSummary BuildSummaryForOrder(int OrderNum)
    {
        Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
        //Any exception handling, additional logic, or whatever

        OrderSummary obModel = new OrderSummary();
        obModel.Order = obOrder;

        return obModel;
    }
}

//View model
public class OrderSummary
{
    public Some.Business.Logic.Order Order;
    //Other methods for additional logic based on the order
    //and other properties
}

Enfoque 2

Tome solo los datos necesarios de los objetos comerciales

//Business object in some library
public class Order
{
    public int OrderNum;
    public int NumOrderLines;
    //...
}

//Order builder in website
public class OrderBuilder
{
    public OrderSummary BuildSummaryForOrder(int OrderNum)
    {
        Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
        //Any exception handling, additional logic, or whatever

        OrderSummary obModel = new OrderSummary()
        {
            OrderNum = obOrder.OrderNum,
            NumOrderLnes = obOrder.NumOrderLines,
        }

        return obModel;
    }
}

//View model
public class OrderSummary
{
    public int OrderNum;
    public int NumOrderLines
    //Other methods for additional logic based on the order
    //and other properties
}

Puedo ver los beneficios y los inconvenientes de ambos, pero me pregunto si hay un enfoque aceptado. En el enfoque 1, no hay duplicación de código alrededor de los modelos, pero crea una dependencia en la lógica de negocios. En el enfoque 2, solo toma los datos necesarios para la vista, pero duplica el código alrededor de los modelos.

Andy Hunt
fuente

Respuestas:

12

La opción 1 crea un acoplamiento estrecho entre el modelo de dominio y la vista. Esto contraviene los problemas que los modelos de vista están diseñados para resolver.

Un modelo de vista "razón para cambiar" es si la vista en sí misma cambia. Al poner un objeto de modelo de dominio en el modelo de vista, está introduciendo otra razón para cambiar (por ejemplo, el dominio cambió). Esta es una clara indicación de una violación del principio de responsabilidad única. Tener dos o más razones para cambiar conduce a ver modelos que requieren mucho mantenimiento, probablemente más que el costo de mantenimiento percibido de la duplicación entre modelos de dominio / vista.

Siempre recomendaría el enfoque 2. A menudo, los modelos de vista pueden parecer muy similares, incluso idénticos a los objetos de modelo de dominio, pero la distinción que mencioné es sus diferentes razones para el cambio.

MattDavey
fuente
¿Estoy en lo cierto al pensar que por "razón para cambiar" te refieres a un cambio en un sentido de mantenimiento, no un cambio en un sentido de actualización (por ejemplo, evento ui)?
Andy Hunt
@AndyBursh sí, eso es correcto. Vea este artículo , particularmente la línea "Robert C. Martin define una responsabilidad como una razón para cambiar, y concluye que una clase o módulo debe tener una, y solo una, razón para cambiar".
MattDavey
Me gusta su respuesta, pero algunas ideas ... El modelo de vista no necesariamente cambia solo porque el modelo cambia. Solo si fuera vinculante o usara una propiedad específica que cambió, esto sería un problema, ya que su referencia es al objeto completo. Tener una referencia al objeto de dominio hace que sea más fácil hacer cambios y guardarlo nuevamente. Sus métodos de guardado también dependen del objeto de dominio, por lo que debe volver a convertir el modelo de vista o configurar su método de negocio para aceptar modelos de vista, lo que tampoco es bueno. Sigo pensando que el # 2 tiene más sentido, pero solo por dos centavos.
KingOfHypocrites
Si no puede tener objetos de dominio en una VM, ¿cómo representaría algo más complicado como una matriz de Órdenes?
Jeff
Entonces, ¿esto significa que cosas como, por ejemplo, formatear una marca de tiempo para la visualización del usuario, deberían pertenecer a la capa de vista, no a la capa de dominio, y los objetos de nivel de dominio deberían devolver solo una marca de tiempo sin formato y sin formato a los objetos de vista y esto último es lo que debería contener la lógica del formateador?
The_Sympathizer
2

La opción 1 es preferible ya que evita la duplicación de código. Eso es.

Si el modelo de dominio cambia significativamente, es casi seguro que la vista tendrá que cambiar de todos modos. Con la opción 2, debe cambiar el modelo de vista Y el constructor, así como la vista misma. Ese tipo de cosas es un veneno absoluto para la mantenibilidad. YAGNI

El punto de tener un modelo de vista separado es mantener un estado que sea significativo solo para la vista (por ejemplo, qué pestaña está seleccionada actualmente) separada del modelo de negocio. Pero los datos comerciales en sí deberían reutilizarse en lugar de duplicarse.

Michael Borgwardt
fuente
YAGNI: el asesino secreto de resolver la mayoría de los problemas de diseño de software.
Martin Blore
66
Lo siento, pero este es un consejo horrible para todas las aplicaciones menos las más triviales. Ver modelos no tienen estado. Son objetos de transferencia de datos. La pestaña seleccionada es parte de la ESTRUCTURA de la vista y no tiene NADA que ver con los DATOS en el modelo de vista. El mantenimiento no es una pesadilla si estructura su programa correctamente y usa algo como Automapper para hidratar sus modelos de vista.
Lucifer Sam
"Si el modelo de dominio cambia significativamente, es casi seguro que la vista tendrá que cambiar de todos modos". - De acuerdo. Pero, ¿qué pasa cuando tienes un pequeño cambio en el dominio? Con la opción uno, cada pequeño cambio en el dominio (incluso solo cambiar el nombre de una propiedad) requiere un cambio correspondiente en la vista. Esto también es un veneno absoluto para la mantenibilidad.
MattDavey
@MattDavey: si cambia el nombre de una propiedad, entonces con un modelo de vista separado también debe cambiar la vista (o cualquier mapa entre el dominio y el modelo de vista) y ahora tiene dos nombres diferentes para la misma cosa, lo que seguramente causará confusión.
Michael Borgwardt
@Lucifer Sam: obviamente tenemos conceptos muy diferentes de lo que es un modelo de vista. El tuyo me suena muy, muy extraño, como si estuvieras describiendo aplicaciones de mainframe para terminales tontas, pero ciertamente no aplicaciones web modernas o de cliente pesado.
Michael Borgwardt
2

Los principios y mantras a veces son valiosos para guiar el diseño ... pero aquí está mi respuesta práctica:

Imagine que sus modelos de vista se serializan en JSON o XML. Si intentas serializar tus modelos de dominio, terminarás con un horrible desastre de texto y muy probablemente tendrás problemas con referencias circulares y otros problemas.

El propósito de un modelo de vista no es agrupar modelos de dominio para que la vista pueda consumirlos. En cambio, el modelo de vista debería ser un modelo completamente plano de la vista ... lo que estás viendo en la pantalla. Su lógica de vista solo debe ocuparse de estructurar los datos que están presentes en el modelo de vista.

Idealmente, su modelo de vista debería estar compuesto casi por completo por cadenas preformateadas. Piénselo ... ni siquiera quiere un DateTime o un decimal en su modelo de vista porque entonces está atrapado haciendo lógica de formato en C #, Javascript, Objective-C, etc.

Lucifer Sam
fuente
2
Nunca he tenido ningún problema con la serialización de modelos de dominio. ¿Y convertir todo en cadenas en un modelo? ¿Seriamente?
Michael Borgwardt
3
@MichaelBorgwardt Sí, esto es lo que DEBE ser un modelo de vista. No desea serializar sus modelos de dominio y enviarlos a todos lados. Toda la lógica de negocios debe permanecer segura en casa en un solo lugar. Sin embargo, las vistas deben ser flexibles y poder reproducirse en cualquier dispositivo, razón por la cual desea separar por completo su ESTRUCTURA, DATOS y ESTILO.
Lucifer Sam
Lo siento, pero ESO es un consejo horrible para cualquier aplicación, punto. Conduce a aplicaciones de ingeniería excesiva llenas de código duplicado que son exactamente lo opuesto a flexible.
Michael Borgwardt
1
@MichaelBorgwardt parece que estás acostumbrado a trabajar con modelos de dominio anémico donde las entidades son poco más que bolsas de propiedades con poco o ningún comportamiento. En ese caso, sí, un modelo DTO / View sería básicamente un duplicado. Sin embargo, si tiene un modelo de dominio rico con relaciones complejas, se hace necesaria una capa de DTO / modelos de vista, y no serán tan similares a las entidades de dominio.
MattDavey
@MattDavey: Parece que los modelos de dominio con los que está acostumbrado a trabajar no son solo cleptócratas ricos sino verdaderos. Tampoco me gustan los modelos anémicos, pero siguen siendo modelos, y su comportamiento debe limitarse a representar el dominio. Principio de responsabilidad única y todo eso ...
Michael Borgwardt