Estoy diseñando mi primer esquema de comercio electrónico. He estado leyendo sobre el tema por un tiempo, y estoy un poco confundido acerca de la relación entre un order_line_item
y unproduct
A product
puede ser comprado. Tiene varios detalles, pero lo más importante es unit_price
.
Un order_line_item
tiene una clave externa para el producto product_id
comprado, quantity
comprado y unit_price
en el momento en que el cliente compró el producto.
La mayor parte de lo que he leído dice que el unit_price
en el order_line_item
debe añadirse de forma explícita (es decir, no a través de la referencia product_id
). Tiene sentido, ya que la tienda podría cambiar el precio en el futuro, lo que estropearía los informes de pedidos, el seguimiento, la integridad, etc.
Lo que no entiendo, es ¿por qué guardar directamente el unit_price
valor en order_line_item
?
¿No sería mejor crear una tabla de auditoría / historial que documente el unit_price
cambio de a product
?
Cuando order_line_item
se crea una, product_audit
se agrega la clave externa de la tabla y el precio se puede recuperar (por referencia) desde allí.
Me parece que hay muchos aspectos positivos al usar este enfoque (menos duplicación de datos, historial de cambios de precios, etc.), entonces, ¿por qué no se usa con más frecuencia? No he encontrado un ejemplo de un esquema de comercio electrónico que utilice este enfoque, ¿me estoy perdiendo algo?
UDPATE: Parece que mi pregunta se relaciona con Dimensión que cambia lentamente . Sin embargo, todavía estoy confundido ya que la Dimensión de cambio lento se relaciona con el almacén de datos y OLAP. Entonces, ¿se pueden aplicar los tipos Slowy Changing Dimension a mi base de datos de proceso de transacciones comerciales (OLTP) principal? Me pregunto si estoy mezclando muchos conceptos, agradecería mucho alguna orientación.
fuente
Respuestas:
Como ha identificado, almacenar el precio en el pedido facilita la implementación técnica. Sin embargo, hay varias razones comerciales por las que esto puede ser beneficioso.
Además de las transacciones web, muchas empresas apoyan las ventas a través de otros canales, por ejemplo:
En estos casos, el pedido puede ingresarse en el sistema en algún momento después de que se haya realizado la transacción. En estas circunstancias, puede ser difícil o imposible identificar correctamente qué registro histórico de precios se debe usar: almacenar el precio unitario directamente en el pedido es la única opción viable.
Los canales múltiples a menudo presentan otro desafío: precios diferentes para el mismo producto. Los recargos por pedidos telefónicos son comunes, y algunos clientes pueden negociar un descuento. Es posible que pueda representar todos los precios posibles para todos los canales en el esquema de su producto, pero incorporar esto en sus tablas de pedidos puede volverse (muy) complejo.
En cualquier lugar donde se permita la negociación, se hace muy difícil vincular el historial de precios con el precio del pedido acordado (a menos que los agentes tengan límites de negociación muy estrechos). Necesita almacenar el precio en el pedido mismo.
Incluso si solo admite transacciones web y tiene una estructura de precios relativamente simple, todavía hay un problema interesante que superar: ¿cómo deben manejarse los aumentos de precios en las transacciones de vuelo? ¿El negocio insiste en que el cliente debe pagar los aumentos o respeta el precio original (cuando el producto se agregó a la cesta)? Si es lo último, la implementación técnica es complicada: debe encontrar una manera de asegurarse de mantener la versión de precio en la sesión correctamente.
Finalmente, muchas empresas están comenzando a usar precios altamente dinámicos. Es posible que no haya un precio fijo para un producto determinado: siempre se calcula en tiempo de ejecución en función de factores como la hora del día, la demanda del producto, etc. ¡En estos casos, el precio no puede almacenarse contra el producto en primer lugar!
fuente
Agregaré algunos puntos prácticos que he visto.
Los productos son transitorios.
Lo que pueden significar hoy, puede no ser lo mismo que solían significar hace un año. El mismo código sku (y, por lo tanto, el product_id), podría referirse a diferentes variantes / tipos del producto en diferentes etapas.
No todos entienden todas las preocupaciones en cuestión; por lo tanto, un usuario puede cambiar los atributos del producto original en lugar de crear uno nuevo por su propia ignorancia. Muchas veces, esto podría suceder debido al plan en el que está un usuario (¡Oye! Solo puedo tener 100 sku, así que ¿por qué no seguir cambiando los más antiguos en lugar de actualizar el plan?) , un producto nunca significará lo mismo para siempre.
Diferentes precios según las condiciones de pedido y envío.
Como ha mencionado el usuario @Chris, pueden aplicarse diferentes precios en diferentes escenarios.
En la mayoría de los carros, encontrará al menos 3 campos diferentes almacenados: el precio unitario, el monto del descuento y el precio con descuento. En los más avanzados, encontrará 2 más: precio unitario con impuestos, precio con descuento con impuestos. Puede encontrar un par de campos más para describir los cargos del método de envío y los cargos adicionales del método de pago. Los porcentajes de impuestos pueden variar según el estado, el producto, el país, el método de envío, etc., y también lo hacen los otros costos. Del mismo modo, los descuentos pueden variar según la geografía, las promociones, el momento de la venta, etc. Por lo tanto, hay información que se puede obtener solo a nivel de pedido, y esta información combinada no se puede generar a partir de los datos en la tabla de productos solo.
Separación de intereses
Se implementan muchos carritos de una manera, para que diferentes equipos puedan tener control sobre diferentes partes de datos. Alguien que gestiona el sistema de pedidos no siempre necesita saber qué productos están en stock, cuáles eran los precios en diferentes momentos, cuáles son las alternativas para un sku determinado, etc. Mantener los datos relacionados con el producto junto con los datos del pedido ayuda a lograr una separación de preocupaciones. Esto también podría ser cierto en las etapas de desarrollo, si diferentes equipos administran diferentes partes del sistema.
Escalabilidad más fácil en múltiples sistemas.
Muchas veces, el Sistema de gestión de pedidos, el Motor de reglas, el Motor de catálogo, el Sistema de gestión de contenido se crean / mantienen como sistemas separados. Esto ayuda a optimizar las diversas condiciones de carga y generar inteligencia especializada para cada uno de los sistemas. Por lo tanto, un sistema no puede ser retenido por falta de disponibilidad de información de otro sistema.
Desarrollo y tiempo de ejecución más rápidos.
He usado el término "tiempo de desarrollo" aquí, aunque usar "tiempo de depuración" sería más adecuado. Cada vez que ocurra un nuevo desarrollo, será más rápido si los datos necesarios están disponibles sin agregar complejidad propia, porque entonces, habrá ciclos de depuración comparativamente más pequeños.
Imagine que se le solicita que genere informes a pedido para los descuentos que se ofrecen a diario durante un mes determinado medio año atrás. Si tiene el precio original, el precio con descuento en 1-2 tablas junto con el pedido, los detalles del artículo del pedido, esto es bastante sencillo. Sin embargo, si tiene que ir y buscar los precios de otra tabla, y luego los descuentos aplicables de otra tabla, y luego averiguar los detalles, tanto el desarrollo como el tiempo de ejecución serán más altos.
Un buen diseño debería tratar de optimizar tanto para el futuro como para el presente.
fuente
Puede terminar costando más en el almacenamiento, pero prefiero alojar todos los detalles relevantes de la venta con la transacción en sí misma, de modo que si por alguna razón nuestro seguimiento de auditoría se rompe, o un administrador anula las garantías implementadas, los detalles del venta como: moneda utilizada, precio unitario, cantidad, impuestos aplicados y el valor al que llegaron, etc. están disponibles. Generalmente lo guardo como XML para que pueda ser flexible de venta en venta.
EDITAR: para ampliar lo que dije brevemente arriba, en mi comentario de seguimiento a continuación, y lo que @a_horse_with_no_name mencionó anteriormente, la redundancia en los datos de transacciones no solo es importante, sino que también es necesaria a escala.
Supongo que está construyendo utilizando OOP, por lo que probablemente debería tener un objeto de transacción y un objeto de producto que lo abarque todo y / o un objeto de precio. En mi propia experiencia personal, prefiero ser detallado en mi historia, el almacenamiento es relativamente barato.
Lo que hemos hecho es crear un historial de objetos que puede facilitar utilizando un RDBMS existente o algún tipo de almacenamiento de valor de clave NOSQL (o incluso mejor un RDBMS que permita conexiones NoSQL como handlersocket o memcache), y almacenamos el historial de objetos de esa manera, con cada detalle y cambio de precio en un solo lugar, fácil y rápidamente disponible. Si habla en serio, incluso podría usar DIFF para ahorrar en el almacenamiento y solo almacenar los cambios hacia adelante, aunque tiene sus propias advertencias. Eso debería ocuparse de su historial, y la ventaja de los objetos serializados es que su sistema podrá / debería poder recuperarlos como los objetos que almacenaron. Eso cuida la historia.
Con respecto a mi sugerencia, almacenar los detalles de la transacción como impuestos, moneda, etc. con la transacción en sí misma significa que no hay necesidad de buscar esos detalles, su objeto de transacción estará al tanto de sus propiedades y sus vistas pueden encargarse de presentar los datos variables como mejor le parezca. Obtiene acceso rápido a la instantánea y tiene el beneficio adicional de registros redundantes y verificables.
¡Vale la pena, confía en mí!
fuente
SELECT ExtractValue(field_name, '/x/path/');
que poder filtrar por cosas como, todas las transacciones en una moneda específica, o todas las transacciones con un cierto valor mínimo de impuestos, o lo que sea. Se pueden hacer informes a mayor escala desde el historial de objetos. Para informes de mayor escala, puede configurar unelasticsearch
servidor / instancia que tenga informes de estilo BigData y se escale fácilmente a los muchos millones de documentos +.Mi voto sería almacenar el precio unitario en su línea de pedido y rastrear el historial de precios de sus productos en una tabla separada. Mi justificación para esto es para mayor flexibilidad.
Incluso si su estructura de precios es rígida y bien definida y no permite las variaciones que @Chris Saxon mencionó anteriormente, ¿está seguro de que siempre será así? Incluso si tienes confianza, ¿por qué pintarte en una esquina? Creo que sería una buena idea almacenar esto en los detalles de la línea de pedido porque no se me ocurre una razón convincente para mantenerlo separado.
En cuanto al almacenamiento de su historial de precios, existe un valor definitivo en el almacenamiento por separado, ya que podría haber cambios en el precio de un artículo y nadie lo compró. Eso definitivamente sería información útil para saber si un cambio de precio no fue efectivo. Como mencionó, este es un caso de uso clásico de una Dimensión de tipo 2 que cambia lentamente en un escenario de depósito de datos. Por lo general, se capturaría cualquier cambio de precio en su tabla de productos y se agregaría una nueva fila a la tabla de dimensiones con el precio actualizado y una marca de tiempo para indicar cuándo tuvo lugar este cambio. La fila anterior tendría su fecha de finalización actualizada para indicar que ya no es el precio efectivo. Entonces, un enfoque sería rastrear este tipo de cambio en un almacén de datos.
Sin embargo, si no desea preocuparse por diseñar un esquema de depósito de datos y un proceso ETL al mismo tiempo que diseña su base de datos de comercio electrónico OLTP, entonces este historial podría capturarse en nuestra base de datos de comercio electrónico. Esto se puede hacer como lo describió al crear una tabla separada de product_audit que se cuelga de la tabla de productos y contiene las fechas de inicio y finalización de cuándo estaba vigente esa versión de un producto. También se puede hacer en la propia tabla de productos agregando fechas de inicio y finalización a la tabla para indicar qué producto está actualmente activo. Sin embargo, dependiendo de la cantidad de productos y la cantidad o los cambios de precios que sufra su empresa, esto podría hacer que su tabla de productos sea mucho más grande de lo previsto y podría causar problemas de rendimiento de consultas más adelante.
Por último, separar su historial de precios del precio unitario real en la línea de pedido definitivamente podría brindar algunas otras oportunidades analíticas para ver cuándo se vendió un producto a un precio que estaba por encima o por debajo del precio indicado en ese momento.
fuente
Estoy totalmente de acuerdo con la idea principal de mantener unida la información relacionada con el orden (contexto). Solo una pequeña nota al margen de que tal situación surgirá solo cuando esté diseñando su aplicación muy centrada en la base de datos y todo gire en torno al gran db gordo. Si cambia su punto de vista mirando el dominio del problema desde un ángulo diferente, observará claramente que el orden es una instantánea capturada de un evento muy especial en el ciclo de vida de su aplicación. Cuando maneja problemas basados en el contexto, los problemas de la base de datos se volverán secundarios y la complejidad que todo el mundo tiene miedo de consultar y hacer informes se manejará sin problemas en el modelo de dominio.
fuente