¿Por qué todos los registros Activos odian? [cerrado]


A medida que aprendo más y más sobre OOP, y comienzo a implementar varios patrones de diseño, sigo volviendo a los casos en los que la gente odia Active Record.

A menudo, la gente dice que no se escala bien ( citando Twitter como su principal ejemplo) but pero en realidad nadie explica por qué no se escala bien; y / o cómo lograr los pros de AR sin los contras (a través de un patrón similar pero diferente?)

Esperemos que esto no se convierta en una guerra santa sobre los patrones de diseño all todo lo que quiero saber es ****específicamente**** lo que está mal con Active Record.

Si no se escala bien, ¿por qué no?

¿Qué otros problemas tiene?

Author: Adam Tuttle, 2008-08-11

14 answers

Está ActiveRecord el Patrón de Diseño y ActiveRecord la biblioteca Rails Rails, y también hay un montón de imitaciones para.NET y otros lenguajes.

Todas Estas son cosas diferentes. En su mayoría siguen ese patrón de diseño, pero lo extienden y modifican de muchas maneras diferentes, por lo que antes de que alguien diga "ActiveRecord apesta" debe calificarse diciendo "¿qué ActiveRecord, hay montones?"

Solo estoy familiarizado con el ActiveRecord de Rails, intentaré la dirección todas las quejas que se han planteado en el contexto de su uso.

@ BlaM

El problema que veo con los registros activos es, que siempre es casi una tabla

Código:

class Person
    belongs_to :company
end
people = Person.find(:all, :include => :company )

Esto genera SQL con LEFT JOIN companies on companies.id = person.company_id, y automáticamente genera objetos de Empresa asociados para que pueda hacer people.first.company y no necesita golpear la base de datos porque los datos ya están presentes.

@pix0r

El problema inherente con Activo Registro es que las consultas de base de datos se generan y ejecutan automáticamente para rellenar objetos y modificar registros de base de datos

Código:

person = Person.find_by_sql("giant complicated sql query")

Esto se desaconseja ya que es feo, pero para los casos en los que simplemente necesitas escribir SQL raw, es fácil de hacer.

@Tim Sullivan

...y seleccionas varias instancias del modelo, básicamente estás haciendo un "select * from"..."

Código:

people = Person.find(:all, :select=>'name, id')

Esto solo seleccione las columnas nombre e ID de la base de datos, todos los demás 'atributos' en los objetos asignados serán nil, a menos que vuelva a cargar manualmente ese objeto, y así sucesivamente.

 90
Author: Orion Edwards,
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
2008-08-11 21:11:09

Siempre he encontrado que ActiveRecord es bueno para aplicaciones rápidas basadas en CRUD donde el Modelo es relativamente plano (como en, no muchas jerarquías de clases). Sin embargo, para aplicaciones con jerarquías OO complejas, un DataMapper es probablemente una mejor solución. Mientras que ActiveRecord asume una relación de 1: 1 entre sus tablas y sus objetos de datos, ese tipo de relación se vuelve difícil de manejar con dominios más complejos. En su libro sobre patrones, Martin Fowler señala que ActiveRecord tiende a descomponerse bajo condiciones donde su Modelo es bastante complejo, y sugiere un DataMapper como alternativa.

He encontrado que esto es cierto en la práctica. En los casos en los que tiene mucha herencia en su dominio, es más difícil mapear la herencia a su RDBMS que mapear asociaciones o composición.

La forma en que lo hago es tener objetos "domain" a los que sus controladores acceden a través de estas clases DataMapper (o "service layer"). Estos no refleja directamente la base de datos, pero actúa como tu representación OO para algún objeto del mundo real. Supongamos que tiene una clase de usuario en su dominio y necesita tener referencias o colecciones de otros objetos ya cargadas cuando recupera ese objeto de usuario. Los datos pueden provenir de muchas tablas diferentes, y un patrón de ActiveRecord puede hacerlo realmente difícil.

En lugar de cargar el objeto User directamente y acceder a los datos mediante una API de estilo ActiveRecord, el código del controlador recupera un Usuario objeto llamando a la API del UserMapper.Método getUser (), por ejemplo. Es ese mapeador el responsable de cargar cualquier objeto asociado de sus respectivas tablas y devolver el objeto "dominio" de Usuario completado al llamador.

Esencialmente, solo está agregando otra capa de abstracción para hacer que el código sea más manejable. Si sus clases DataMapper contienen SQL personalizado sin procesar, o llamadas a una API de capa de abstracción de datos, o incluso acceder a un patrón de ActiveRecord ellos mismos, realmente no importa para el código del controlador que está recibiendo un objeto de usuario agradable y poblado.

De todos modos, así es como lo hago.

 51
Author: Sam McAfee,
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
2008-08-26 18:10:08

Creo que probablemente hay un conjunto muy diferente de razones entre por qué la gente está "odiando" en ActiveRecord y lo que está "mal" con él.

En el tema del odio, hay mucho veneno hacia cualquier cosa relacionada con Rails. En cuanto a lo que está mal con ella, es probable que sea como toda la tecnología y hay situaciones en las que es una buena opción y situaciones en las que hay mejores opciones. La situación en la que no puedes aprovechar la mayoría de las características de Rails ActiveRecord, en mi experiencia, es donde la base de datos está mal estructurada. Si está accediendo a datos sin claves primarias, con cosas que violan la primera forma normal, donde hay muchos procedimientos almacenados necesarios para acceder a los datos, es mejor usar algo que sea más bien un envoltorio SQL. Si su base de datos está relativamente bien estructurada, ActiveRecord le permite aprovechar eso.

Para agregar al tema de responder a los comentaristas que dicen que las cosas son difíciles en ActiveRecord con un fragmento de código réplica

@Sam McAfee Diga que tiene una clase de usuario en su dominio y que necesita tener referencias o colecciones de otros objetos ya cargadas cuando recupera ese objeto de usuario. Los datos pueden provenir de muchas tablas diferentes, y un patrón de ActiveRecord puede hacerlo realmente difícil.

user = User.find(id, :include => ["posts", "comments"])
first_post = user.posts.first
first_comment = user.comments.first

Al usar la opción include, ActiveRecord le permite anular el comportamiento de carga perezosa predeterminado.

 11
Author: MattMcKnight,
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
2008-09-22 21:35:07

Mi larga y tardía respuesta, ni siquiera completa, pero una buena explicación DE POR QUÉ odio este patrón, opiniones e incluso algunas emociones:

1) versión corta: Active Record crea una " capa delgada" de " enlace fuerte" entre la base de datos y el código de la aplicación. Lo que no resuelve ningún problema lógico, ningún problema, ningún problema en absoluto. En mi humilde opinión no proporciona NINGÚN VALOR, excepto algunos azúcar sintáctica para el programador (que luego puede utilizar una "sintaxis de objeto" para acceder a algunos datos, que existe en una base de datos relacional). El esfuerzo para crear un poco de comodidad para los programadores debe (IMHO...) es mejor invertir en herramientas de acceso a bases de datos de bajo nivel, por ejemplo, algunas variaciones de simples, fáciles, simples hash_map get_record( string id_value, string table_name, string id_column_name="id" ) y métodos similares (por supuesto, los conceptos y la elegancia varían mucho con el lenguaje utilizado).

2) versión larga: En cualquier proyecto basado en bases de datos en el que tuviera el "control conceptual" de las cosas, evitaba la realidad aumentada, y era bueno. Normalmente construyo un en capas arquitectura (tarde o temprano divides tu software en capas, al menos en proyectos de tamaño mediano a grande):

A1) la propia base de datos, tablas, relaciones, incluso alguna lógica si el DBMS lo permite (MySQL también es adulto ahora)

A2) muy a menudo, hay más que un almacén de datos: sistema de archivos (los blobs en la base de datos no siempre son una buena decisión...), sistemas heredados (imagínese "cómo" se accederá a ellos, muchas variedades posibles.. pero ese no es el punto...)

B) capa de acceso a la base de datos (en este nivel, métodos de herramientas, ayudantes para acceder fácilmente a los datos en la base de datos son muy bienvenidos, pero AR no proporciona ningún valor aquí, excepto algún azúcar sintáctico)

C) capa de objetos de aplicación: "objetos de aplicación" a veces son filas simples de una tabla en la base de datos, pero la mayoría de las veces son objetos compuestos de todos modos, y tienen alguna lógica superior adjunta, por lo que invertir tiempo en objetos AR en este nivel es simplemente inútil, codificadores tiempo, porque el" valor real", la" lógica superior " de esos objetos necesita ser implementado en la parte superior de los objetos AR, de todos modos - con y sin AR! Y, por ejemplo, ¿por qué querría tener una abstracción de "Objetos de entrada de registro"? El código lógico de la aplicación los escribe, pero ¿debería tener la capacidad de actualizarlos o eliminarlos? suena tonto, y App::Log("I am a log message") es algunas magnitudes más fácil de usar que le=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert();. Y por ejemplo: el uso de un "objeto de entrada de registro" en la vista de registro en su aplicación funcionará para 100, 1000 o incluso 10000 líneas de registro, pero tarde o temprano tendrá que optimizar, y apuesto a que en la mayoría de los casos, solo usará esa pequeña y hermosa sentencia SQL SELECT en la lógica de su aplicación (que rompe totalmente la idea de AR..), en lugar de envolver esa pequeña declaración en marcos rígidos de ideas AR fijas con mucho código envolviéndolo y ocultándolo. El tiempo perdido con la escritura y / o creación de código AR podría haber sido invertido en una interfaz mucho más inteligente para la lectura de listas de entradas de registro (muchas, muchas maneras, el el cielo es el límite). Los codificadores deben atreverse a inventar nuevas abstracciones para realizar su lógica de aplicación que se ajuste a la aplicación prevista, y no volver a implementar estúpidamente patrones tontos, que suenan bien a primera vista!

D) la lógica de la aplicación-implementa la lógica de la interacción de los objetos y la creación, eliminación y listado (!) de los objetos de lógica de la aplicación (NO, esas tareas rara vez deben estar ancladas en los objetos de lógica de la aplicación en sí: ¿ la hoja de papel en su ¿el escritorio te dice los nombres y ubicaciones de todas las demás hojas de tu oficina? olvídese de los métodos "estáticos" para listar objetos, eso es tonto, un mal compromiso creado para hacer que la forma humana de pensar encaje en [algunos-no-todos-AR-framework-like -] AR thinking)

E) la interfaz de usuario-bueno, lo que escribiré en las siguientes líneas es muy, muy, muy subjetivo, pero en mi experiencia, los proyectos que se basan en AR a menudo descuidaron la parte de la interfaz de usuario de una aplicación-el tiempo se perdió en la creación oscura abstracción. Al final, tales aplicaciones perdieron mucho tiempo codificadores y se sienten como aplicaciones de codificadores para codificadores, inclinados a la tecnología dentro y fuera. Los programadores se sienten bien (trabajo duro finalmente hecho, todo terminado y correcto, de acuerdo con el concepto en papel...), y los clientes "solo tienen que aprender que tiene que ser así", porque eso es "profesional".. ok, lo siento, estoy divagando; -)

Bueno, es cierto que todo esto es subjetivo, pero es mi experiencia (Ruby on Rails excluidos, puede ser diferente, y tengo cero experiencia práctica con ese enfoque).

En los proyectos pagados, a menudo escuché la demanda de comenzar con la creación de algunos objetos de "registro activo" como un bloque de construcción para la lógica de aplicación de nivel superior. En mi experiencia, esto a menudo era una especie de excusa para que el cliente (una empresa de desarrollo de software en la mayoría de los casos) no tenía un buen concepto, una gran visión, una visión general de lo que el producto finalmente debería ser. Aquellos los clientes piensan en marcos rígidos ("en el proyecto de hace diez años funcionó bien.."), pueden dar cuerpo a entidades, pueden definir relaciones de entidades, pueden descomponer relaciones de datos y definir la lógica básica de la aplicación, pero luego se detienen y se lo entregan a usted, y piensan que eso es todo lo que necesita... a menudo carecen de un concepto completo de la lógica de la aplicación, interfaz de usuario, usabilidad y así sucesivamente y así sucesivamente... les falta la vista grande y les falta amor por los detalles, y quieren que sigas que AR así son las cosas, porque.. bueno, ¿por qué, funcionó en ese proyecto hace años, mantiene a la gente ocupada y silenciosa? No sé. Pero los "detalles" separan a los hombres de los niños, o .. ¿cómo fue el eslogan publicitario original ? ;-)

Después de muchos años (diez años de experiencia en desarrollo activo), cada vez que un cliente menciona un "patrón de registro activo", suena mi alarma. Aprendí a tratar de llevarlos de vuelta a esa fase conceptual esencial, dejarlos pensar dos veces, tratar de mostrarles sus debilidades conceptuales o simplemente evitarlos en absoluto si son undiscerning (al final, ya sabes, un cliente que aún no sabe lo que quiere, tal vez incluso piensa que sabe pero no lo hace, o trata de externalizar el concepto de trabajo para MÍ de forma gratuita, me cuesta muchas horas preciosas, días, semanas y meses de mi tiempo, vivir es demasiado corto ... ).

Así que, finalmente: TODO ESTO es por lo que odio ese tonto "patrón de registro activo", y lo hago y lo evitaré siempre que sea posible.

EDITAR : I incluso llamaría a esto un No-Patrón. No resuelve ningún problema (los patrones no están destinados a crear azúcar sintáctica). Crea muchos problemas: la raíz de todos sus problemas (mencionados en muchas respuestas aquí..) es, que solo oculta el buen viejo SQL bien desarrollado y poderoso detrás de una interfaz que es por la definición de patrones extremadamente limitada.

Este patrón reemplaza la flexibilidad con azúcar sintáctica!

Piénselo, ¿qué problema resuelve AR para usted?

 7
Author: Frunsi,
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-11-27 09:28:43

Algunos mensajes me confunden. Algunas respuestas van a "SQL "vs" SQL " o algo así.

El hecho es que AR es solo un patrón de programación de simplificación donde se aprovecha de los objetos de dominio para escribir allí el código de acceso a la base de datos.

Estos objetos suelen tener atributos de negocio (propiedades del bean) y algún comportamiento (métodos que suelen funcionar en estas propiedades).

El AR solo dice "agregar algunos métodos a estos objetos de dominio" a tareas relacionadas con la base de datos.

Y tengo que decir, desde mi opinión y experiencia, que no me gusta el patrón.

A primera vista puede sonar bastante bien. Algunas herramientas Java modernas como Spring Roo utilizan este patrón.

Para mí, el verdadero problema es solo con la preocupación OOP. El patrón AR le obliga de alguna manera a agregar una dependencia de su objeto a los objetos de infraestructura. Estos objetos de infraestructura permiten al objeto domain consultar la base de datos a través de los métodos sugeridos por AR.

Siempre he dicho que dos capas son clave para el éxito de un proyecto. La capa de servicio (donde reside la lógica de negocios o se puede exportar a través de algún tipo de tecnología remota, como Servicios Web, por ejemplo) y la capa de dominio. En mi opinión, si agregamos algunas dependencias (no realmente necesarias) a los objetos de capa de dominio para resolver el patrón AR, nuestros objetos de dominio serán más difíciles de compartir con otras capas o aplicaciones externas (raras).

Primavera Roo la implementación de AR es interesante, porque no se basa en el objeto en sí, sino en algunos archivos AspectJ. Pero si más tarde no desea trabajar con Roo y tiene que refactorizar el proyecto, los métodos AR se implementarán directamente en sus objetos de dominio.

Otro punto de vista. Imagine que no usamos una Base de datos Relacional para almacenar nuestros objetos. Imagine que la aplicación almacena nuestros objetos de dominio en una base de datos NoSQL o simplemente en archivos XML, por ejemplo. ¿Implementaríamos los métodos ¿que hacen estas tareas en nuestros objetos de dominio? No lo creo (por ejemplo, en el caso de XM, añadiríamos dependencias relacionadas con XML a nuestros objetos de dominio...Verdaderamente triste, creo). ¿Por qué entonces tenemos que implementar los métodos relacionales DB en los objetos de dominio, como dice el patrón Ar?

En resumen, el patrón AR puede sonar más simple y bueno para aplicaciones pequeñas y simples. Pero, cuando tenemos aplicaciones complejas y grandes, creo que la arquitectura clásica por capas es un mejor enfoque.

 5
Author: Juanjo,
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
2011-08-25 09:42:35

La pregunta es sobre el Activo Patrón de diseño de grabación. No es un orm Herramienta.

La pregunta original está etiquetada con rails y se refiere a Twitter que está construido en Ruby on Rails. El framework ActiveRecord dentro de Rails es una implementación del patrón de diseño Active Record de Fowler.

 3
Author: John Topley,
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
2008-08-26 19:19:25

Lo principal que he visto con respecto a las quejas sobre Active Record es que cuando creas un modelo alrededor de una tabla, y seleccionas varias instancias del modelo, básicamente estás haciendo un "select * from"...". Esto está bien para editar un registro o mostrar un registro, pero si desea, por ejemplo, mostrar una lista de las ciudades para todos los contactos en su base de datos, puede hacer "seleccionar ciudad de ..."y solo las ciudades. Hacer esto con Active Record requeriría que estés seleccionar todas las columnas, pero solo usando City.

Por supuesto, las diferentes implementaciones manejarán esto de manera diferente. Sin embargo, es una cuestión.

Ahora, puedes evitar esto creando un nuevo modelo para lo específico que estás tratando de hacer, pero algunas personas argumentarían que es más esfuerzo que beneficio.

Yo, me gusta el Registro Activo. :-)

HTH

 2
Author: Tim Sullivan,
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
2008-08-11 16:22:52

Me encanta la forma en que SubSonic hace la única cosa de una columna.
O bien

DataBaseTable.GetList(DataBaseTable.Columns.ColumnYouWant)

O:

Query q = DataBaseTable.CreateQuery()
               .WHERE(DataBaseTable.Columns.ColumnToFilterOn,value);
q.SelectList = DataBaseTable.Columns.ColumnYouWant;
q.Load();

Pero Linq sigue siendo el rey cuando se trata de carga diferida.

 1
Author: Lars Mæhlum,
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
2008-08-11 19:50:26

@ BlaM: A veces simplemente implementé un registro activo para un resultado de una unión. No siempre tiene que ser la Tabla de relación Active Record. ¿Por qué no" Result of a Join statement " Active Record ?

 1
Author: Johannes,
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
2008-08-11 21:14:48

Voy a hablar de Registro Activo como un patrón de diseño, no he visto ROR.

Algunos desarrolladores odian Active Record, porque leen libros inteligentes sobre la escritura de código limpio y ordenado, y estos libros afirman que active record viola el principio de resposobilidad única, viola la regla DDD de que el objeto de dominio debe ser ignorante persistente, y muchas otras reglas de este tipo de libros.

La segunda cosa que los objetos de dominio en Active Record tienden a ser 1-a - 1 con base de datos, que puede ser considerado una limitación en algún tipo de sistemas (n-tier en su mayoría).

Eso es solo cosas abstractas, no he visto ruby on rails implementación real de este patrón.

 1
Author: Alex Burtsev,
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
2011-06-13 07:26:16

El problema que veo con los Registros activos es que siempre se trata de una tabla. Eso está bien, siempre y cuando realmente trabaje con solo esa tabla, pero cuando trabaje con datos en la mayoría de los casos, tendrá algún tipo de unión en algún lugar.

Sí, unirse por lo general es peor que no unirse en absoluto cuando se trata de rendimiento, pero unirse por lo general es mejor que "falso" unirse por primera lectura de toda la tabla A y luego utilizando el ganado información para leer y filtrar tabla B.

 0
Author: BlaM,
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
2008-08-11 17:21:06

El problema con ActiveRecord es que las consultas que genera automáticamente pueden causar problemas de rendimiento.

Terminas haciendo algunos trucos poco intuitivos para optimizar las consultas que te dejan preguntándote si hubiera sido más efectivo escribir la consulta a mano en primer lugar.

 0
Author: engtech,
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
2008-08-15 14:32:24

Aunque todos los demás comentarios sobre la optimización SQL son ciertamente válidos, mi principal queja con el patrón de active record es que generalmente conduce a desajuste de impedancia. Me gusta mantener mi dominio limpio y correctamente encapsulado, lo que el patrón de registro activo generalmente destruye toda esperanza de hacer.

 0
Author: Kevin Pang,
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
2008-08-26 18:31:26

Intenta hacer una relación polimórfica de muchos a muchos. No es tan fácil. Especialmente cuando no estás usando ITS.

 0
Author: Alexander Trauzzi,
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-20 13:39:29