El comercio electrónico está capturando una cuota de mercado cada vez mayor de los compradores. Además de los requisitos de administrar una tienda física (como el seguimiento del inventario), las tiendas de comercio electrónico tienen desafíos adicionales, que incluyen llevar el producto al cliente, usar un procesador de pagos y establecer relaciones con los clientes.
Todas estas tareas requieren una infraestructura especializada, que a su vez interactúa con su base de datos. Gran parte de la eficiencia y eficacia de su tienda se basa en las decisiones que toma sobre la estructuración y el almacenamiento de datos. En este artículo, exploraremos los pros y los contras de una base de datos NoSQL para su aplicación de comercio electrónico y usaremos la opción de almacenamiento de documentos más popular, MongoDB, en nuestros ejemplos.
Nota: Este ejercicio de modelado de datos es una respuesta a los comentarios de Hacker News sobre una publicación anterior que publicamos sobre la creación de un modelo de datos de comercio electrónico escalable.
MongoDB admite transacciones ACID (atomicidad, consistencia, aislamiento y durabilidad), y su oferta en la nube cumple con PCI DSS (Estándar de seguridad de datos de la industria de tarjetas de pago). Es una de las mejores opciones de NoSQL para el comercio electrónico.
MongoDB también guarda documentos en un formato similar a JSON, lo que significa que es muy sencillo convertir consultas y resultados a un formato que comprenda su código de interfaz. Esto elimina la necesidad de escribir mapeadores de relación de objetos (ORM). Además, MongoDB escala extremadamente bien; está diseñado para ser distribuido a través de múltiples máquinas. Las bases de datos NoSQL no vienen con esquemas forzados. La introducción de nuevos campos es rápida porque no es necesario informar a la base de datos que va a cambiar el formato o incluso la existencia de un campo. Simplemente guarda lo que le dices, sin complicaciones.
Si bien esto es excelente para empresas en crecimiento donde los productos y los flujos de trabajo de pago evolucionan rápidamente, requiere mucho cuidado para garantizar que no cree datos que confundan a los futuros desarrolladores y versiones de la aplicación.
Antes de comenzar a modelar su sistema, tenga en cuenta que MongoDB no comienza con el control de acceso. No dejes que este valor predeterminado te tome por sorpresa. ¡ Habilite el control de acceso antes de escribir datos reales! El cumplimiento de PCI requiere que parte de la información del titular de la tarjeta se cifre en reposo y en movimiento. Puede habilitar el cifrado Transport Layer Security/Secure Sockets Layer (TLS/ SSL ) para proteger el tráfico de red.
Cubriremos el cifrado en reposo más adelante en el artículo, pero antes de publicar cualquier sitio que use MongoDB, revise la lista de verificación de seguridad para asegurarse de que está al día con las mejores prácticas.
Hay una variedad casi infinita de funciones que puede desarrollar para una tienda en línea, pero para los fines de este artículo, me centraré en modelar solo algunas: su catálogo de productos, información de usuario, pagos y pedidos.
Catalogo de producto
Para vender cosas, debe registrar lo que tiene para que los clientes puedan navegar antes de comprometerse con una compra. Lo esencial para un documento de producto es el
price
y un identificador único como un sku
. También es común incluir una descripción, la cantidad que tienes disponible y un archivo de imagen.Todos los ejemplos se ejecutan desde el shell de Mongo y deben adoptarse en un controlador para su aplicación.
db .inventory .insertOne ( { item : "journal" , price : 9.99 , qty : 25 , size : { h : 14 , l : 21 , w : 1 }, features : "Beautiful, handmade journal." , categories : [ "writing" , "bestseller" ], image : "items/journal.jpg" } )
Esto devolverá un mensaje de verificación como este:
{ "acknowledged" : true , "insertedId" : ObjectId( "600e814359ba901629a14e13" ) }
Si no proporciona MongoDB con un
_id
campo, crea uno usando un ObjectId
. los _id
El campo es un identificador único, inmutable e indexado. Para que su ejemplo sea más realista y muestre la flexibilidad de MongoDB, use el updateOne
comando y el devuelto _id
para dar cabida a dos productos relacionados con características ligeramente diferentes y lanzar una oferta promocional. db .inventory .updateOne ( { _id : ObjectId( "600e814359ba901629a14e13" ) }, { $ unset : { image : 1 , size : 1 , qty : 1 , price : 1 }, $ set : { item : "journal" , features : [ "Inner pocket" , "Durable cover" ], skus : [ { sku : "154A" , price : { base : NumberDecimal( 9.99 ), currency : "USD" }, quantity : 20 , options : { size : { h : 14 , l : 21 , w : 1 }, features : [ "72 sheets of premium lined paper" ], colors : [ "brown" , "red" ], ruling : "wide" , image : "images/journal1.jpg" } }, { sku : "154B" , price : { base : NumberDecimal( 14.99 ), currency : "USD" , discount : NumberDecimal( 4.00 ) }, quantity : 15 , options : { size : { h : 18 , l : 22 , w : 2 }, features : [ "140 sheets of premium paper" ], colors : [ "brown" ], ruling : "unlined" , image : "images/journals.jpg" } } ] } } )
Note que cambiamos la estructura del
features
campo de una cadena a una matriz. Esto destaca la poderosa (y peligrosa) flexibilidad de las bases de datos NoSQL: puede cambiar radicalmente la forma en que se formatean los datos sin un vistazo de la base de datos. En ese sentido, SQL de línea de comandos da miedo, pero MongoDB de línea de comandos es aterrador.También nos decidimos por un formato para los datos monetarios . Elija uno y apéguese a él a lo largo de su aplicación.
Puede obtener una ventaja inicial en el modelado de productos explorando los datos requeridos por los principales proveedores como Google Marketplace y Amazon Marketplace . También puede consultar el sistema de gestión de información de productos (PIM) de Fabric para aprender cómo una plataforma de comercio electrónico como Fabric maneja el modelado de productos.
Prácticas de diseño de consultas
El objetivo con MongoDB es estructurar su esquema para que su aplicación devuelva todos los datos que necesita en una sola consulta de lectura. Agregar complejidad en MongoDB a menudo implica agregar documentos incrustables a un registro. La indexación es importante para buscar información de manera eficiente. Sin un índice, MongoDB debe realizar un escaneo de colección, examinando cada documento en una colección antes de devolver el resultado. Indexe los campos que usará para buscar productos regularmente.
Una vez que tengas una idea de qué productos quieres ofrecer, piensa en cómo modelar a tus usuarios. Desea almacenar información que le permita construir una relación continua con un cliente, como poder mostrar elementos relevantes para él. Si está vendiendo productos físicos, cada cliente debe tener información de contacto.
Aquí hay un perfil de usuario simple para una tienda que necesita enviar un producto físico. Dado que desea imponer direcciones de correo electrónico únicas para los usuarios y usar ese campo para buscarlas, indexará ese campo y lo hará único. Podrías lograr esto con un comando como el siguiente:
db .customers .createIndex ( { email : 1 }, { unique : true } )
Pero este ejemplo usará el incorporado
_id
campo en su lugar. Recuerda que el _id
El campo es indexado, único e inmutable. Si un usuario cambia su correo electrónico, querrá volver a validarlo. En este caso, la inmutabilidad es un activo; si un usuario desea actualizar su dirección de correo electrónico, MongoDB exigirá una nueva entrada de colección y puede estar seguro de que su emailVerified
el campo se restablece a falso. db .customers .insertOne ( { _id : "journalfanatic@e/mail.com" , fname : "Journal" , lname : "Fanatic" , hashedAndSaltedPassword : "$1$O3JMY.Tw$AdLnLjQ/5jXF9.MTp3gHv/" , emailVerified : false, address : { country : "United States" , street1 : "99 Main Street" , street2 : "Apt #3" , city : "Boston" , state : "MA" , zip : "74586" } } )
Los perfiles de clientes son una parte complicada del comercio electrónico porque representan personas. A diferencia de los productos, las personas están fuera de tu control y cambian constantemente. Cambian de dirección, quieren que los paquetes se entreguen al trabajo y olvidan sus contraseñas.
Aquí es cuando debe comenzar a pensar en proteger la información de identificación personal (PII) y comenzar a trabajar para lograr el cumplimiento de la industria de tarjetas de pago (PCI) . Cumplir con PCI es una gran tarea y, a menudo, uno de los casos convincentes para usar un proveedor externo que ya cumple.
Actualicemos nuestro ejemplo simple para comenzar a manejar estos problemas. Primero, actualizará el código postal, luego agregará una nueva dirección de entrega y un campo de fecha de modificación.
db .customers .updateOne ( { _id : "journalfanatic@e/mail.com" }, { $ set : { "address.zip" : "60601" , shippingAddress : { street1 : "50 Work Street" , street2 : "Floor 16" , city : "Chicago" , state : "IL" , zip : "60601" } }, $ setOnInsert : { dateModified : new Date() } } )
Luego, ayude a su modelo a acomodar una solicitud de restablecimiento de contraseña que vence dentro de treinta minutos (y actualice el
dateModified
campo). Por motivos de rendimiento, debe asegurarse de que los documentos incrustados no se expandan infinitamente con el tiempo.Si un usuario restablece su contraseña cien veces, y cada vez que se agrega a la
customer
documento, obtener información sobre ese cliente para una acción simple (como un inicio de sesión) requerirá muchos recursos. Es mejor separar los eventos que se pueden repetir muchas veces, como pedidos, visitas al sitio y restablecimientos de contraseña en su propia colección, y usar referencias de documentos para vincular otras colecciones.Para hacer una colección de reinicio:
db .reset .insertOne ({ customerId : "journalfanatic@e/mail.com" , token: "493654" , status: "requested" , expiration: new Date (ISODate(). getTime () + ( 1000 * 60 * 30 )), requestTime: new Date () } )
Estas tablas pueden complicarse mucho más rápidamente, especialmente una vez que comienza a rastrear las sesiones de los usuarios, presenta contenido personalizado o vende productos a equipos de usuarios y desea rastrear la utilización y distribución de recursos.
Es posible que la validación no sea necesaria para los modelos de su producto, pero es absolutamente un requisito para los campos relacionados con el cliente. Las direcciones, los números de teléfono, los correos electrónicos y los números de tarjetas de crédito deben validarse para asegurarse de que no está abriendo su sitio a usuarios malintencionados y para evitar errores que impidan que los clientes obtengan sus productos.
MongoDB ofrece herramientas para validar esquemas a nivel de colección. También querrá implementar la validación a nivel de aplicación. Solo es posible validar la unicidad en MongoDB creando un índice y requiriendo que sea único. A
customers
La colección ya existe en nuestro ejemplo, así que use runCommand para agregar algo de validación. db .runCommand ( { collMod : "customers" , validator: { $jsonSchema: { bsonType: "object" , required: [ "hashedAndSaltedPassword" , "address" ], properties: { _id: { bsonType: "string" , pattern: '.+\@.+\..+' , description: "must match the email pattern" }, hashedAndSaltedPassword : { bsonType : "string" , minLength: 10 , description: "must be a string ten characters long, and is required" }, address : { bsonType : "object" , required: [ "street1" , "zip" ], properties: { zip: { pattern: "^[0-9]{5}(?:-[0-9]{4})?$" , description: "must match pattern and is required" }, street1 : { bsonType : "string" , description: "must be a string and is required" }, street2 : { bsonType : "string" , description: "must be a string" } }, description : " must be an object , and is required " } } } }, } )
Como puede ver, escribir la validación en MongoDB es engorroso. Solo validamos seis campos y el código tomó el doble de líneas que la creación del registro. Las bibliotecas como Mongoose pueden facilitar la validación, mientras que las API de comercio sin cabeza de terceros como Fabric] se encargarán de la validación de datos por usted.
Independientemente de cómo decida implementar la validación, es vital para la salud a largo plazo de su aplicación que audite sus datos y cree un esquema sólido y una validación a nivel de aplicación para cualquier campo ingresado por el usuario.
Modelar sus pagos es donde la seguridad de su aplicación es más importante. También es el primer lugar donde realmente necesita hacer referencia a otra tabla.
Puede ser tentador incluir pagos en la tabla de clientes agregando un documento anidado, pero no es una buena idea. La información de pago tiene preocupaciones de seguridad específicas y extremadamente importantes; es más sencillo auditar sus prácticas de cumplimiento de PCI si los pagos no se mezclan con muchos otros datos.
Comencemos con una implementación ingenua (¡y peligrosa!), luego analicemos algunas de las implicaciones de seguridad que pueden mejorar el modelo.
db .payments .insertOne ( { customerId : "journalfanatic@e/mail.com" , status : "verified" , gateway : "stripe" , type : "credit" , amount : NumberDecimal( 1.00 ), card : { brand : "Visa" , pan : "4242424242424242" , expirationMonth : 1 , expirationYear : 2090 , cvv : 123 } } )
Este es un punto de partida, pero los expertos en comercio electrónico entrarán en pánico al ver este esquema. Toda la información necesaria para usar esta tarjeta está en su base de datos en texto sin formato, lo que puede ser conveniente pero crea enormes responsabilidades legales para su empresa. El PCI DSS es muy claro, no almacene datos del titular de la tarjeta a menos que sea necesario, y nunca almacene datos de autenticación como el código de verificación.
MongoDB ofrece a los clientes empresariales la capacidad de cifrar archivos de datos y realizar el cifrado automático del lado del cliente . Cualquier usuario puede usar el cifrado manual del lado del cliente para cifrar campos individuales y luego establecer validaciones de esquema para aplicar el cifrado. Los campos cifrados se almacenan como datos binarios ( subtipo 6 ).
Con el cifrado manual, el controlador para el idioma elegido maneja la aplicación de una clave de cifrado adecuada, así como las operaciones de cifrado y descifrado.
Siguiendo las pautas de PCI DSS , no tiene que cifrar los campos de su base de datos si solo está almacenando los últimos cuatro dígitos de la tarjeta. Pero tenga en cuenta que cada vez que combine el número de cuenta principal (PAN) con otra información de identificación personal, ya sea en reposo o en movimiento, todos esos datos deben cifrarse. Intentemoslo de nuevo.
db .payments .remove ( { } ) db .payments .insertOne ( { customerId : "journalfanatic@e/mail.com" , status : "verified" , gateway : "stripe" , type : "credit" , amount : NumberDecimal( 1.00 ), card : { brand : "Visa" , panLastFour : "4242" , expirationMonth : 1 , expirationYear : 2090 , cvvVerified : true } } )
Ahora que eliminó los datos de autenticación y los reemplazó con un booleano, truncó el número de tarjeta para que no tenga que cifrar otra información como la fecha de vencimiento. En este ejemplo, ha manejado un tipo de opción de pago (tarjetas de crédito), pero hay muchas otras, cada una con sus propios requisitos de seguridad y modelos de datos. Investigue lo que necesitará y las mejores prácticas para manejar y almacenar esa información.
Una excelente manera de eliminar parte de la complejidad del cumplimiento de PCI es verificar si su procesador de pagos (como Stripe ) ofrece un servicio de tokenización. Tokenization ) reemplaza toda la información utilizada en el procesamiento de pagos con una cadena de token que sus sistemas pueden usar para realizar un seguimiento de los pagos. La tokenización es excelente ya que descarga los requisitos de cumplimiento de PCI a su procesador de pagos, lo que reduce su responsabilidad y simplifica su código.
Aquí hay un ejemplo de un modelo tokenizado:
db .payments .insertOne ( { customerId : "journalfanatic@e/mail.com" , status: "awaitingVerification" , gateway: "stripe" , type: "token" , token: "card_1IDHBZFdjJUqVVV2gPlbz8BC" } )
Los pedidos son donde el caucho se encuentra con el camino. Su cliente tiene un perfil que le permite enviarle algo, y usted tiene productos para enviarle y la capacidad de aceptar pagos. El pedido rastrea su producto a través del proceso de dejar su posesión y entrar en manos del cliente. Los pedidos son otro modelo de datos para rastrear el comportamiento humano, por lo que pueden complicarse. Los clientes pueden abandonar y modificar pedidos y solicitar reembolsos.
Este paso del ejemplo reúne todas las partes móviles de su modelo: los productos los compran los clientes mediante pagos. Nuestro modelo incluirá cosas que existen en otras colecciones, como direcciones de envío y descuentos.
En una base de datos SQL, esta información existiría como referencias a tablas existentes, pero recuerde que en MongoDB desea que todos los datos relevantes se devuelvan en un solo comando de lectura. Suele ser más eficiente hacer una copia de la información relevante y almacenarla en el
orders
recopilación. De esa manera, cuando un usuario verifica un pedido, no tiene que leer varias colecciones para mostrar la información que desea. db .orders .insertOne ( { customerId : "journalfanatic@e/mail.com" , paymentId : "600e6f37aa2232f59e273082" , paymentStatus : "paid" , status : "shippedAwaitingDelivery" , currency : "USD" , totalCost : NumberDecimal( 39.85 ) items : [ { sku : "154B" , quantity : "2" , price : NumberDecimal( 14.99 ), discount : NumberDecimal( 1.00 ), preTaxTotal : NumberDecimal( 27.98 ), tax : NumberDecimal( 1.00 ), total : NumberDecimal( 28.98 ), }, { sku : "154A" , quantity : "1" , price : NumberDecimal( 9.99 ), preTaxTotal : NumberDecimal( 9.99 ), tax : NumberDecimal(. 87 ), total : NumberDecimal( 10.86 ) } ], shipping : { address :{ street1 : "50 Work Street" , street2 : "Floor 16" , city : "Chicago" , state : "IL" , country : "USA" , zip : "60601" }, origin : { street1 : "1 Penn Ave" , street2 : "" , city : "New York" , state : "NY" , country : "USA" , zipCode : "46281" }, carrier : "USPS" , tracking : "123412341234" } } )
Aquí están todas las partes de su modelo de datos de comercio electrónico NoSQL hasta el momento:
Es un excelente comienzo, pero como dije al comienzo de esta guía, hay muchas facetas de una tienda de comercio electrónico y apenas hemos tocado la superficie. Otras actividades válidas para el modelo podrían incluir la adición de canales de distribución de productos , el manejo de la optimización de imágenes, la internacionalización, la validación de datos adicionales, la categorización de productos, la personalización de clientes, las suscripciones, los pagos recurrentes, los impuestos y las devoluciones.
Al modelar sus datos para el comercio electrónico, los errores pueden ser costosos y los clientes rara vez apreciarán la cantidad de esfuerzo que implica crear una base de datos finamente ajustada. Con eso en mente, puede considerar descargar la administración de su base de datos de comercio electrónico a un servicio. Puede hacer uso de solicitudes de API de bajo costo para almacenar sus datos y permitir que otra persona se mantenga al día con las mejores prácticas de la industria mientras se enfoca en mejorar su producto.
Publicado anteriormente en https://resources.fabric.inc/blog/nosql-ecommerce-data-model