tabla de pedidos de comercio electrónico. ¿Guardar precios o utilizar una tabla de auditoría / historial?


13

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_itemy unproduct

A productpuede ser comprado. Tiene varios detalles, pero lo más importante es unit_price.

Un order_line_itemtiene una clave externa para el producto product_idcomprado, quantitycomprado y unit_priceen el momento en que el cliente compró el producto.

La mayor parte de lo que he leído dice que el unit_priceen el order_line_itemdebe 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_pricevalor en order_line_item?

¿No sería mejor crear una tabla de auditoría / historial que documente el unit_pricecambio de a product?

Cuando order_line_itemse crea una, product_auditse 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.


Realmente haría ambas cosas: almacenar el precio de venta en la línea de pedido y almacenar un historial de precios de productos. Porque ambos sirven para diferentes propósitos. Almacenar el precio de venta hará que las consultas sobre pedidos sean mucho más fáciles y rápidas. Por otro lado, es posible que desee recuperar precios antiguos incluso para productos que no se han vendido.
a_horse_with_no_name

Sin duda, hacer que las consultas de pedidos sean más fáciles no puede ser el único motor para ahorrar el precio final cada vez. Es una base de datos relacional después de todo, las consultas no serían mucho más difíciles.
Gaz_Edge

3
Almacenar el precio de venta en la línea de pedido no es "no relacional" (en mi opinión, considero que está normalizado y que los precios de venta son un atributo directo de la línea de pedido). El arte de usar una base de datos relacional se trata de conocer los límites. Si está ejecutando una tienda con 100 productos y 5 pedidos al día, entonces almacenar y recuperar precios antiguos no es un problema. Si está ejecutando un mercado con millones de productos, miles de distribuidores y cientos de pedidos por minuto, entonces consultar ese historial será un problema.
a_horse_with_no_name

¿Dónde guardarías los precios históricos? Por lo que estoy leyendo, necesitaría dos bases de datos. Uno para OLTP y otro para OLAP. La historia va en el OLAP?
Gaz_Edge

Respuestas:


10

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:

  • Sobre el telefono
  • Agentes de ventas "en el camino"
  • En una ubicación física (por ejemplo, tienda, oficina)

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!


3

Agregaré algunos puntos prácticos que he visto.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.


2

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í!


eso realmente no tiene sentido decir que no lo usas porque podría eliminarse. Puede anular cualquier dato. Cualquier estrategia debe mantener copias de seguridad
Gaz_Edge

@Gaz_Edge No son mutuamente excluyentes, aún puede utilizar una pista de auditoría y almacenar los detalles con las transacciones, la redundancia en este caso está justificada. Nunca se deje con datos tan importantes sujetos a un solo punto de falla. En nuestro caso, utilizo un almacén de objetos centralizado como mecanismo de historial en lugar de intentar crear un historial por tipo de objeto (como una transacción o producto en este caso). Tendré tanto el objeto de transacción completo en el historial como todos los detalles más importantes con el registro en sí. Eso es completamente aparte de los objetos del producto en la historia.
oucil

¿Y si quisiera ejecutar informes sobre pedidos? ¿Como cuántos productos x se han comprado? ¿O cuántos pedidos sobre y cantidad? El ahorro como medios XML se pierde la capacidad de consultar los datos, si no me equivoco
Gaz_Edge

@Gaz_Edge No es cierto, si estás hablando de MySQL, tienes 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 un elasticsearchservidor / instancia que tenga informes de estilo BigData y se escale fácilmente a los muchos millones de documentos +.
oucil

@Gaz_Edge También debería mencionar que los informes de los que habla (compras por valor, ventas de productos, etc.) son informes comunes y deben tener valores almacenados como valores en columnas con el registro de transacciones para un procesamiento más rápido. Cualquier cosa que sea importante pero no necesariamente informada a menudo puede ir al XML. Los datos de la instantánea son realmente solo para dos cosas, 1. el problema de la Dimensión que cambia lentamente, y 2. Validación y comparación cuando un cliente se queja, y necesita ver rápidamente quién tiene la razón. No es para uso diario.
oucil

1

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.


gracias por la respuesta. Creo que la estrategia con la que voy a ir será diseñar el OLTP según el libro. De hecho, me gusta la idea de tener un almacén de datos para informar. Aunque agrega algo de trabajo adicional (crear un nuevo esquema), el esquema se puede adaptar a OLAP. Es como evitar un escenario en el que intento colocar una clavija cuadrada en un agujero redondo.
Gaz_Edge

@Gaz_Edge: Estoy en una situación similar en términos de tomar una decisión para mi nuevo proyecto. ¿Puede por favor compartir sus pensamientos sobre el enfoque de diseño que tomó hace un año? ¿Funcionó bien?
Codificador absoluto

@CoderAbsolute mi solución original estaba muy orientada a la 'base de datos'. Mi aplicación ahora se centra en una Arquitectura Orientada a Servicios. Ahora tengo muchos pequeños esquemas de bases de datos desacoplados en lugar de uno estrechamente acoplado. La necesidad de 3N en un esquema masivo ha desaparecido. Así que ahora solo agrego el precio unitario directamente al pedido. Mantener los cambios históricos en los precios de los productos ahora ha desaparecido, ya que no era un requisito comercial. Espero que ayude.
Gaz_Edge

0

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.


Algunos pequeños ejemplos harán que su respuesta sea mucho más fácil de entender. Especialmente oraciones como 'orden es una instantánea capturada de un evento muy especial en el ciclo de vida de su aplicación'.
dezso
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.