¿Qué son los patrones de diseño para admitir campos personalizados en una aplicación?


Desarrollamos una aplicación comercial. Nuestros clientes piden soporte de campos personalizados. Por ejemplo, quieren agregar un campo al formulario de cliente.

¿Cuáles son los patrones de diseño conocidos para almacenar los valores de campo y los metadatos sobre los campos?

Veo estas opciones por ahora:

Opción 1: Agregar columnas Field1, Field2, Field3, Field4 de tipo varchar a mi tabla de clientes.

Opción 2: Añadir una sola columna de tipo XML en el tabla del cliente y almacenar los valores de los campos personalizados en xml.

Opción 3: Agregue una tabla CustomerCustomFieldValue con una columna de tipo varchar y almacene valores en esa columna. Esa tabla también tendría un CustomerID, un CustomFieldID.

CustomerID,  CustomFieldID, Value
10001,       1001,          '02/12/2009 8:00 AM'
10001,       1002,          '18.26'
10002,       1001,          '01/12/2009 8:00 AM'
10002,       1002,          '50.26'

CustomFieldID sería un ID de otra tabla llamada CustomField con estas columnas: CustomFieldID, FieldName, FieldValueTypeID.

Opción 4: Agregue una tabla CustomerCustomFieldValue con una columna de cada posible valor escriba y almacene valores en la columna de la derecha. Similar a #3, pero los valores de campo se almacenan utilizando una columna de tipo fuerte.

CustomerID,  CustomFieldID, DateValue,           StringValue,       NumericValue                 
10001,       1001,          02/12/2009 8:00 AM,  null,              null
10001,       1002,          null,                null,              18.26
10002,       1001,          01/12/2009 8:00 AM,  null,              null
10002,       1002,          null,                null,              50.26

Opción 5: Las opciones 3 y 4 utilizan un cuadro específico para un único concepto (Cliente). Nuestros clientes están pidiendo campo personalizado en otras formas también. ¿Deberíamos tener un sistema de almacenamiento de campo personalizado para todo el sistema? Así que en lugar de tener varias tablas como CustomerCustomFieldValue, EmployeeCustomFieldValue, InvoiceCustomFieldValue, tendríamos un ¿una sola tabla llamada CustomFieldValue? Aunque me parece más elegante, ¿no causaría eso un cuello de botella de rendimiento?

¿Ha utilizado alguno de esos enfoques? ¿Tuviste éxito? ¿Qué enfoque elegirías? ¿Sabes algún otro enfoque que deba considerar?

Además, mis clientes quieren que el campo personalizado pueda hacer referencia a datos en otras tablas. Por ejemplo, un cliente podría querer agregar un campo "Método de pago Favorito" al Cliente. Los métodos de pago están definidos en otras partes del sistema. Eso trae el tema de las "claves foráneas" en la imagen. ¿Debería intentar crear restricciones para garantizar que los valores almacenados en las tablas de campos personalizados sean valores válidos?

Gracias

======================

EDITAR 07-27-2009:

Gracias por sus respuestas. Parece que la lista de enfoques es ahora bastante completa. He seleccionado la opción 2 (una sola columna XML). Fue el más fácil de implementar por ahora. Probablemente lo haré tengo que refractor a un enfoque más fuertemente definido como mis requisitos se volverán más complejos y como el número de campos personalizados para apoyar se hará más grande.

Author: Sylvain, 2009-07-14

7 answers

Estoy de acuerdo con los carteles de abajo en que las opciones 3, 4 o 5 son las más apropiadas. Sin embargo, cada una de sus implementaciones sugeridas tiene sus beneficios y costos. Yo sugeriría elegir uno ajustándolo a sus requisitos específicos. Por ejemplo:

  1. Opción 1 pros: Rápido de implementar. Permite acciones de BD en campos personalizados (búsqueda, ordenación.)
    Opción 1 contras: Los campos personalizados son genéricos, por lo que no hay campos fuertemente escritos. Tabla de base de datos es ineficiente, tamaño-sabio con muchos campos extraños que nunca serán utilizados. El número de campos personalizados permitidos debe anticiparse.
  2. Opción 2 pros: Rápido de implementar. Flexible, permitiendo número arbitrario y tipo de campos personalizados.
    Opción 2 contras: No hay acciones de base de datos posibles en campos personalizados. Esto es mejor si todo lo que necesita hacer es mostrar los campos personalizados, más tarde, o realizar manipulaciones menores de los datos solo por cliente.
  3. Opción 3 pros: Flexible y eficiente. Se pueden realizar acciones de base de datos, pero los datos se normalizan un poco para reducir el espacio desperdiciado. Estoy de acuerdo con la sugerencia de unknown (Google) de que agregue una columna adicional que se pueda usar para especificar el tipo o la información de origen. Opción 3 contras: Ligero aumento en el tiempo de desarrollo y la complejidad de sus consultas, pero realmente no hay demasiados contras, aquí.
  4. La opción 4 es la misma que la Opción 3, excepto que los datos escritos se pueden operar en el nivel de base de datos. La adición de información de tipo a la tabla de enlaces en la opción 3 le permite hacer más operaciones a nivel de nuestra aplicación, pero la base de datos no será capaz de hacer comparaciones o ordenar, por ejemplo. La elección entre 3 y 4 depende de este requisito.
  5. La opción 5 es la misma que la 3 o la 4, pero con aún más flexibilidad para aplicar la solución a muchos cuadros diferentes. El costo en este caso será que el tamaño de esta tabla crecerá mucho más grande. Si está realizando muchas operaciones de unión costosas para llegar a sus campos personalizados, esta solución puede no escalar bien.

P.d. Como se indica a continuación, el término "patrón de diseño" generalmente se refiere a la programación orientada a objetos. Está buscando una solución a un problema de diseño de base de datos, lo que significa que la mayoría de los consejos sobre patrones de diseño no serán aplicables.

 14
Author: Eric Nguyen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-06 16:11:58

En cuanto al código de la aplicación, no estoy seguro. Sé que los campos personalizados se benefician mucho de un modelo EAV en la base de datos.

Según los comentarios a continuación, el error más significativo que puede cometer con este modelo es poner claves foráneas en él. Nunca jamás poner algo como friendId o TypeID en este modelo. Utilice este modelo junto con el modelo relacional típico y mantenga los campos de clave foránea en columnas de tabla como deberían.

Un segundo un error significativo es colocar datos en este modelo que deben ser reportados con cada elemento. Por ejemplo, poner algo como Username en este modelo significaría que cada vez que desee acceder a un usuario y necesite conocer su nombre de usuario, se ha comprometido a unirse a best o 2n queries donde n es el número de usuarios que está mirando. Cuando considera que normalmente va a necesitar la propiedad Username para cada elemento User, se hace obvio que esto también debe permanecer en la tabla columna.

Sin embargo, si solo está utilizando este modelo con campos de usuario personalizados, estará bien. No puedo imaginar muchas situaciones en las que un usuario estaría ingresando datos relacionales y el modelo EAV no es demasiado perjudicial para las búsquedas.

Por último, no intente unir datos de esto y obtener un bonito conjunto de registros. Agarra el registro original y luego agarra el conjunto de registros de la entidad. Si te sientes tentado a unirte a las mesas probablemente hayas cometido el segundo error como se mencionó anteriormente.

 10
Author: Spencer Ruport,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-14 20:12:17

Si está desarrollando con un lenguaje orientado a objetos, estamos hablando de modelos de objetos adaptativos aquí. Hay bastantes artículos escritos sobre cómo puede implementarlos en oo-languages, pero no tanta información sobre cómo diseñar el lado del almacén de datos.

En la empresa donde trabajo, hemos resuelto el problema utilizando una base de datos relacional para almacenar datos de AOM. Tenemos una tabla de entidades central para presentar todas las diferentes "entidades" en el dominio, como personas, dispositivos de red, empresas, etc... Almacenamos los "campos de formulario" reales en tablas de datos que se escriben, por lo que tenemos una tabla para cadenas, una para fechas, etc. Todas las tablas de datos tienen una clave foránea que apunta a la tabla de entidades. También necesitamos tablas para presentar el lado del tipo, es decir, qué tipo de atributos (campos de formulario) puede tener cierta entidad y esta información se utiliza para interpretar los datos en tablas de datos.

Las ventajas de nuestra solución son que cualquier cosa se puede modelar sin cambios de código, incluyendo referencias entre las entidades, los múltiples valores y así sucesivamente. También es posible agregar reglas de negocio y validaciones a los campos y se pueden reutilizar en todas las formas. Las desventajas son que el modelo de programación no es muy fácil de entender y el rendimiento de la consulta será peor que con un diseño de base de datos más típico. Otra solución que la base de datos relacional podría haber sido mejor y más fácil para AOM.

Construir un buen AOM con un almacén de datos que funcione es mucho trabajo y no lo recomendaría si usted no tiene desarrolladores altamente calificados. Tal vez un día habrá una solución de sistema operativo para este tipo de requisitos.

Los campos personalizados se han discutido antes en SO:

 4
Author: Kaitsu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:15

Algo así como la Opción 3 es el camino a seguir y he utilizado este método anteriormente. Cree una sola tabla para definir propiedades adicionales y sus valores correspondientes. Esto sería una relación 1-N entre su Cliente y CustomerCustomField table (respectivamente). Su segunda pregunta con respecto a la definición de relaciones con propiedades personalizadas sería algo en lo que pensar. Lo primero que viene a la mente es agregar un campo de fuente de datos, que contendría la tabla a la que el valor de la propiedad está obligado a. Así que esencialmente su CustomerCustomField se vería como:

  1. CustomerID
  2. Propiedad
  3. Valor
  4. ValueDataSource (nullable)

Esto debería permitirle enlazar a una estructura de datos específica o simplemente permitirle especificar valores no enlazados. Puede normalizar aún más este modelo, pero algo como esto podría funcionar y debería ser lo suficientemente fácil de manejar en código.

 3
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-14 17:41:04

La opción 4 o 5 sería mi elección. Si sus datos son importantes, no iría desechando su información de tipo con la opción 3. (Puede intentar implementar la comprobación de tipos completa usted mismo, pero es un trabajo bastante grande, y el motor de base de datos ya lo hace por usted.)

Algunos pensamientos:

  • Asegúrese de que su CustomFields tiene una columna DataType.
    • Use una restricción de comprobación basada en UDF en CustomFieldValues para asegurarse de que la columna especificada por CustomFields.DataType no sea nula.
    • Usted también querrá un restricción de comprobación estándar para asegurarse de que tiene exactamente un valor no nulo.
  • Con respecto a las claves foráneas, las modelaría como DataType por separado.
    • Cada posible referencia de la tabla cruzada requeriría su propia columna. Esto es bueno, porque mantiene la integridad referencial.
    • De todos modos, tendría que soportar estas relaciones en el código de la aplicación, por lo que el hecho de que estén codificadas en la base de datos no limita funcionalidad.
    • Esto también encajará bien con tuM, si estás usando uno.
  • Para la opción 5, utilice tablas intermedias para modelar las relaciones.
    • Usted todavía tendría un CustomerCustomFieldValue, pero en su lugar con solo CustomerID y CustomFieldValueID columnas.
  • Piensa largo y tendido sobre tus limitaciones en cada paso del camino. Esto es algo complicado, y un paso en falso puede causar un havok total en la línea.

Estoy usando esto en una aplicación actualmente en desarrollo. No ha habido ningún problema todavía, pero los diseños de EAV todavía me asustan. Sólo ten cuidado.

Como un aparte, XML también puede ser una buena opción. No se mucho al respecto por experiencia directa, pero fue una de las opciones que consideré al comenzar el diseño de datos, y parecía bastante prometedor.

 3
Author: WCWedin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-14 18:00:33

Si esos campos 'extra' son incidentales y no me importa hacer búsquedas en ellos, generalmente opto por la opción 2 (pero me gusta JSON mejor que XML). Si va a haber búsquedas en campos personalizados, la opción 3 no es difícil de hacer, y generalmente el optimizador SQL puede obtener un rendimiento razonable de ella.

 0
Author: Javier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-14 17:59:20

Actualmente estoy trabajando en un proyecto con este mismo problema, y he elegido usar la opción 3, pero agregué un campo FieldType y un campo ListSource en caso de que el FieldType="list". El campo ListSource podría ser una consulta, una vista sql, un nombre de función o algo que resulte en una lista de opciones para la lista. El mayor problema al tratar de almacenar campos como este en mi situación es que esta lista de campos puede cambiar, y los usuarios pueden editar los datos más tarde. Así que qué hacer si el la lista de campos ha cambiado y van a editar. Mi solución a ese escenario fue permitir la edición solo si la lista no ha cambiado y mostrar datos de solo lectura si lo ha hecho.

 0
Author: jbair,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-26 19:48:30