Acerca de las entidades, DTO, ORM y carga diferida

El paradigma orientado a objetos es el estándar para el software de aplicación. DBMS relacional: un estándar para almacenar datos en software de aplicación. Sí, puede escribir en Haskell y almacenar datos exclusivamente en ClickHouse. Pero se trata de la corriente principal.

ORM le permite tirar de un búho en un globo para pretender que no hay RDBMS y que los datos se almacenan en un modelo de objeto que es más adecuado para OOP. Sigue existiendo un problema "pequeño": esta abstracción, como muchas otras, "fluye". Donde en el modelo de objetos hay un enlace a otro objeto en la clave externa y la identificación de la base de datos. En el momento de la materialización de la entidad, nos enfrentamos a una elección:

  1. Descargue todo y caiga de la memoria / tiempo de espera
  2. Indique explícitamente qué dependencias queremos descargar y cuáles no, y viole el principio de no preguntar
  3. Cargue dependencias implícitamente bajo demanda usando Lazy Load y obtenga problemas de rendimiento en algún lugar del código llamado

¿Qué tipo de pierna debes cortar: izquierda o derecha?

TLDR Lazy Load no es tan malo si se usa solo para escribir y no se usa al leer. Pero no todo es tan simple y hay muchos matices.

Con el tiempo, llegué a la conclusión de que Lazy Load y / o la dependencia de las entidades en la implementación de ORM es el mal menor en ciertas condiciones.

En el subsistema de lectura, siempre lea solo DTO


En el 90% de los casos, los problemas con Lazy Load surgen precisamente al leer. Obtenemos la lista de entidades, la revisamos y repetimos y comenzamos a seleccionar todos los datos necesarios. Recibimos un montón de consultas a la base de datos. En este caso, la mayoría de las veces lo único que debe hacerse es obtener los datos, serializarlos y enviarlos nuevamente en forma de JSON. ¿Por qué, entonces, cargar entidades en absoluto? No es necesario agregar estos datos al rastreador de cambios UOW, para leer toda la entidad junto con los campos "extra". En su lugar, siempre puede escribir Select o ProjectTo . Lazy Load no es necesaria porque el código C # de Select se traducirá a SQL y se ejecutará en el lado de la base de datos.

¿Qué pasa si mi lógica no se traduce a SQL?


Recomiendo mantener la evaluación del cliente desactivada. En primer lugar, puede "ayudar" y agregar soporte para las funciones necesarias directamente en sub . No es una mala opción cuando se trata de computación simple, no de reglas comerciales. Opción número dos: extraer la interfaz de la entidad e implementarla tanto en la entidad como en el DTO.

Por ejemplo, en la base de datos hay dos campos: "precio sin descuento" y "precio con descuento". Si se completa el campo "precio de descuento", utilícelo, si no, utilice el campo con el precio habitual. Agrega una regla más. Al comprar 3 productos, solo paga por los 2 más caros, mientras que también se tienen en cuenta los descuentos regulares.

La implementación puede ser la siguiente:

 public interface IHasProductPrice { decimal BasePrice { get; } decimal? SalePrice { get; } } public class Product: IHasProductPrice { // ... a lot of code public decimal BasePrice { get; protected set;} public decimal? SalePrice { get; protected set;} } public class ProductDto: IHasProductPrice { public decimal BasePrice { get; set;} public decimal? SalePrice { get; set;} } public static class ProductCalculator { public static void decimal Calculate(IEnumerable<IHasProductPrice> prices) } 

En el subsistema de escritura, Lazy Load no da tanto miedo


En el subsistema de escritura, por el contrario, a menudo solo la identificación para escribir no es suficiente. Todo tipo de comprobaciones a menudo le hacen leer toda la entidad, porque el paradigma del objeto implica combinar datos y operaciones en ellos dentro del objeto de clase y su invariante. Si el proyecto usa DDD, las operaciones de escritura / cambio deben realizarse a través de la raíz de agregación y, por lo tanto, solo sobre un objeto y sus dependencias. Una gran cantidad de consultas solo puede ocurrir cuando se trabaja con colecciones relacionadas.

Colecciones asociadas en agregados


Si hay demasiados datos en la máquina, esto puede indicar un problema de diseño. Raíces típicas de agregación: canasta, orden, paquete. Las personas generalmente no trabajan con datos de miles de filas, por lo que descargar toda la colección vinculada puede no ser la operación más productiva, pero no mortal. Pero si hay miles de objetos en la colección, es posible que realmente no exista tal raíz de agregación y los desarrolladores se le ocurrió, porque era muy simple hacerlo usando herramientas improvisadas.

¿Qué pasa si todavía hay miles de registros en el agregado?


Pase el DbContext al constructor y lea de él solo los datos necesarios en el contexto de la operación. Sí, violar DIP. O eso, o no use la unidad en este caso.

Operaciones masivas


Importar un archivo de 10,000 líneas es un gran objetivo para Lazy Load. Aquí, a todos los problemas del subsistema de lectura, también se agregan los frenos de ChangeTracker. Para la grabación masiva, debe usar herramientas separadas. Prefiero Batch Extensions, porque de nuevo puedes hacerlo sin crear entidades. Para casos especialmente severos, existen buenos procedimientos almacenados e incluso herramientas especiales de DBMS .

Hack de vida


Si necesita implementar tanto una operación en masa como una convencional, debe comenzar con una operación en masa. Una operación normal es solo un caso especial de código de masa en una secuencia con un solo elemento.

Source: https://habr.com/ru/post/437418/


All Articles