¿Los ORM permiten la creación de modelos de dominio enriquecido?

21

Después de usar Hibernate en la mayoría de mis proyectos durante aproximadamente 8 años, llegué a una empresa que desalienta su uso y quiere que las aplicaciones solo interactúen con la base de datos a través de procedimientos almacenados.

Después de hacer esto durante un par de semanas, no he podido crear un modelo de dominio rico de la aplicación que estoy comenzando a compilar, y la aplicación solo parece un script transaccional (horrible).

Algunos de los problemas que he encontrado son:

  • No se puede navegar por el gráfico de objetos, ya que los procedimientos almacenados solo cargan la cantidad mínima de datos, lo que significa que a veces tenemos objetos similares con diferentes campos. Un ejemplo es: tenemos un procedimiento almacenado para recuperar todos los datos de un cliente, y otro para recuperar la información de la cuenta, además de algunos campos del cliente.
  • Gran parte de la lógica termina en clases auxiliares, por lo que el código se vuelve más estructurado (con entidades utilizadas como estructuras de C antiguas).
  • Código de andamio más aburrido, ya que no hay un marco que extraiga conjuntos de resultados de un procedimiento almacenado y lo coloque en una entidad.

Mis preguntas son:

  • ¿Alguien ha estado en una situación similar y no estuvo de acuerdo con el enfoque del procedimiento de la tienda? ¿Qué hiciste?
  • ¿Existe un beneficio real de usar procedimientos almacenados? aparte del punto tonto de "nadie puede emitir una tabla desplegable".
  • ¿Hay alguna manera de crear un dominio rico utilizando procedimientos almacenados? Sé que existe la posibilidad de utilizar AOP para inyectar DAO / repositorios en entidades para poder navegar por el gráfico de objetos. No me gusta esta opción, ya que está muy cerca del vudú.

Conclusión

Primero, gracias a todos por sus respuestas. La conclusión a la que he llegado es que los ORM no permiten la creación de modelos de dominio enriquecido (como algunas personas mencionaron), pero sí simplifica la cantidad de trabajo (a menudo repetitivo). La siguiente es una explicación más detallada de la conclusión, pero no se basa en ningún dato sólido.

La mayoría de las aplicaciones solicitan y envían información a otros sistemas. Para hacer esto, creamos una abstracción en los términos del modelo (por ejemplo, un evento de negocios) y el modelo de dominio envía o recibe el evento. El evento generalmente necesita un pequeño subconjunto de información del modelo, pero no todo el modelo. Por ejemplo, en una tienda en línea, una pasarela de pago solicita cierta información del usuario y el total para cobrar a un usuario, pero no requiere el historial de compras, los productos disponibles y toda la base de clientes. Por lo tanto, el evento tiene un conjunto pequeño y específico de datos.

Si tomamos la base de datos de una aplicación como un sistema externo, entonces necesitamos crear una abstracción que nos permita mapear las entidades del Modelo de Dominio a la base de datos ( como mencionó NimChimpsky , usando un mapeador de datos). La diferencia obvia es que ahora necesitamos crear una asignación para cada entidad modelo a la base de datos (ya sea un esquema heredado o procedimientos almacenados), con el dolor adicional de que, dado que los dos no están sincronizados, una entidad de dominio podría mapearse parcialmente a una entidad de base de datos (por ejemplo, una clase de UserCredentials que solo contiene nombre de usuario y contraseña se asigna a una tabla de Usuarios que tiene otras columnas), o una entidad de modelo de dominio podría asignar a más de una entidad de base de datos (por ejemplo, si hay una una asignación en la tabla, pero queremos todos los datos en una sola clase).

En una aplicación con algunas entidades, la cantidad de trabajo adicional puede ser pequeña si no hay necesidad de atravesar las entidades, pero aumenta cuando hay una necesidad condicional de atravesar las entidades (y, por lo tanto, podríamos querer implementar algún tipo de 'perezoso' cargando'). A medida que una aplicación crece para tener más entidades, este trabajo solo aumenta (y tengo la sensación de que aumenta de forma no lineal). Mi suposición aquí es que no intentamos reinventar un ORM.

Una ventaja de tratar la base de datos como un sistema externo es que podemos codificar situaciones en las que queremos que se ejecuten 2 versiones diferentes de una aplicación, en las que cada aplicación tiene una asignación diferente. Esto se vuelve más interesante en el escenario de entregas continuas a la producción ... pero creo que esto también es posible con ORM en menor medida.

Voy a descartar el aspecto de seguridad, sobre la base de que un desarrollador, incluso si no tiene acceso a la base de datos, puede obtener la mayoría, si no toda, la información almacenada en un sistema, simplemente inyectando código malicioso (p. Ej. ¡No puedo creer que olvidé eliminar la línea que registra los datos de la tarjeta de crédito de los clientes, querido señor! ).


Pequeña actualización (6/6/2012)

Los procedimientos almacenados (al menos en Oracle) evitan hacer algo como la entrega continua con tiempo de inactividad cero, ya que cualquier cambio en la estructura de las tablas invalidará los procedimientos y los desencadenantes. Por lo tanto, durante el tiempo que se actualiza la base de datos, la aplicación también estará inactiva. Oracle proporciona una solución para esta denominada Redefinición basada en la edición , pero los pocos DBA que he preguntado sobre esta característica mencionaron que estaba mal implícita y no la incluirían en una base de datos de producción.

Augusto
fuente
Bueno, obviamente podrías hacer lo que Hibernate hace y usar la herencia para generar un objeto proxy dinámico, que te permite recuperar el gráfico del objeto. Sin embargo, eso es extremadamente hacky con SP: D
Max
Así que terminaría reinventando la mitad de la hibernación, sin los más de 10 años de experiencia que tiene el equipo de hibernación :).
Augusto
1
Cualquier DBA debe evitar la caída de tablas particulares por parte de ciertos usuarios. No debería importar cómo intentes hacerlo.
JeffO
1
Puede echar un vistazo a Mybatis : puede proporcionarle la función que necesita. Es menos un ORM que un marco de mapeo. Puede escribir SQL como quiera y decirle a Mybatis dónde colocarlo en su modelo de objetos. Manejará gráficos de objetos grandes con múltiples consultas, lo que suena como la situación que tiene (muchos procedimientos almacenados).
Michael K
1
@Augusto: He estado en una situación similar, no debido al uso de SP, sino debido al uso de un marco de mapeo patentado que no admitía relaciones de objeto. Pasamos días escribiendo código que podría escribirse en minutos usando un ORM adecuado. Nunca resolví ese problema.
Kevin Cline

Respuestas:

16

Su aplicación aún debe modelarse a partir de principios de diseño basados ​​en dominio. Ya sea que use un ORM, JDBC directo, llamar a SP (o lo que sea) no debería importar . Con suerte, una capa delgada que abstraiga su modelo de los SP debería ser la solución en este caso. Como dijo otro póster , debe ver los SP y sus resultados como un servicio y asignar los resultados a su modelo de dominio.

Martijn Verburg
fuente
Martijn, estoy de acuerdo en que la aplicación debe modelarse utilizando los principios DDD, pero el problema que estoy enfrentando (¡y díganme si hay una solución!) Es que algunos procesos almacenados devuelven muy poca información para instanciar una entidad DDD. Vea este comentario donde expliqué un poco más sobre la información que devuelven los procedimientos almacenados. Podría eludir esto, invocando más de un proceso almacenado y, por ejemplo, recuperando todos los detalles del usuario y luego invocar a otro para recuperar toda la información de la cuenta, pero se siente mal :).
Augusto
1
@Augusto Bueno ... eres desarrollador de aplicaciones, por lo que debes decidir si tiene sentido que exista un determinado objeto con ciertos campos establecidos en NULL. Si tiene sentido (para ciertas tareas, por ejemplo), déjalo ser. De lo contrario, solicite al autor de SP que proporcione más datos para poder crear sus objetos.
Jacek Prucia
Y agregando al comentario de Jacek: en realidad es perfectamente aceptable llamar a más de 2 procesos almacenados, nuevamente piense en ellos como dos servicios remotos a los que debe llamar para crear su modelo de dominio, no hay nada de malo en eso :-).
Martijn Verburg
@ Martijn: En mi experiencia, una capa delgada no es suficiente. El código de mapeo puede ser considerablemente más largo que la lógica empresarial subyacente.
Kevin Cline
@Kevin cline - Buen punto, he puesto "con suerte" en la respuesta :-)
Martijn Verburg
5

¿Existe un beneficio real de usar procedimientos almacenados?

En el mundo financiero (y lugares donde se requiere el cumplimiento de Sarbanes-Oxley ), debe poder auditar los sistemas para asegurarse de que hagan lo que se supone que deben hacer. En estos casos, es mucho más fácil garantizar el cumplimiento cuando todo el acceso a datos se realiza a través de procedimientos almacenados. Y cuando se elimina todo el SQL ad-hoc, es mucho más difícil ocultar cosas. Para ver un ejemplo de por qué esto sería algo "bueno", lo remito al artículo clásico de Ken Thompson, Reflexiones sobre confiar en la confianza .

Tangurena
fuente
si un millón de veces si! También debe asegurarse de que los usuarios no puedan hacer nada que se supone que no deben hacer, incluido el hecho de no tener derechos directos sobre las tablas y los procedimientos almacenados que ayudan enormemente.
HLGEM
1
Trabajo para una empresa pública y cumplimos con SOX. Puede ser mi escaso conocimiento de la auditoría, pero no veo la diferencia entre hacer la auditoría a nivel de base de datos (a través de procesos almacenados) o a nivel de aplicación. Cada aplicación debe tener su propio esquema de base de datos y ese esquema solo es accesible desde la aplicación, en lugar de ser compartido entre diferentes aplicaciones.
Augusto
enlace roto ...
Alex R
@AlexR, enlace fijo
Tangurena
2

Los procedimientos almacenados son mucho más eficientes que el código SQL del lado del cliente. Precompilan SQL en la base de datos, lo que también le permite realizar algunas optimizaciones.

Arquitectónicamente, un SP devolverá los datos mínimos necesarios para una tarea, lo cual es bueno ya que significa que se están transfiriendo menos datos. Si tiene esa arquitectura, debe pensar en la base de datos como un servicio (piense en ella como un servicio web y cada SP es un método para llamar). Trabajar de esta manera no debería ser un problema, mientras que un ORM lo guía a trabajar con datos remotos como si fuera local, por lo que lo engañará para que presente problemas de rendimiento si no tiene cuidado.

He estado en situaciones en las que usamos SP por completo, el DB proporcionó una API de datos y la usamos. Esa aplicación en particular era muy grande y funcionó increíblemente bien. ¡No tendré nada malo que decir sobre los SP después de eso!

Hay otra ventaja: los DBA escribirán todas sus consultas SQL por usted y manejarán con gusto toda la jerarquía relacional en la base de datos, por lo que no tiene que hacerlo.

gbjbaanb
fuente
3
gbjbaanb, la mayor parte de lo que dijiste es cierto para bases de datos antiguas. La mayoría de las bases de datos más recientes vuelven a compilar consultas con bastante frecuencia para decidir qué nuevas optimizaciones usar (incluso si están almacenadas). Estoy de acuerdo con lo que dijo sobre el uso de la base de datos como un sistema externo, pero también veo que es mucho trabajo, ya que la aplicación posee la base de datos y ambos deben estar sincronizados tanto como sea posible. Por ejemplo, con el nombre de tabla / clases y campos / columnas. Además, el enfoque de dejar que los DBA escriban los procedimientos huele a silos de desarrollo, en lugar de tener un equipo multidisciplinario.
Augusto
77
Los SP no siempre son necesariamente más eficientes y creo que pasar SQL a DBA es un mal camino a seguir. Como experto en dominios, el desarrollador debe saber qué datos desean obtener y cómo obtenerlos
Martijn Verburg
1
Esta es una buena respuesta, pero en mi experiencia, la mayoría de los clientes no necesitan las ganancias de rendimiento para controlar el acceso a los datos a través de procedimientos almacenados frente a los inconvenientes de hacer un uso completo de sus herramientas ORM en la capa de aplicación. La mayoría de las veces veo estas decisiones arquitectónicas tomadas en tiendas de software donde tienen la necesidad de justificar los salarios inflados de los programadores de procedimientos almacenados de "barba gris" que no tienen otras habilidades.
maple_shaft
1
@Augusto the approach of letting the DBAs write the procedures smells like development silos+100 te conecta por esta joya de la verdad. Siempre he visto esto como el caso en el que el acceso a los datos se controlaba mediante procedimientos almacenados.
maple_shaft
1
@maple_shaft: ¿por qué los DBA que escriben SP no son considerados parte del equipo de desarrolladores? Donde funciona, son codificadores especializados que conocen este aspecto del sistema realmente bien, mucho mejor que la mayoría de los desarrolladores de propósito general. Este podría ser el problema que ha dado lugar a la popularidad de ORM. Quiero decir, nadie se lo pensaría dos veces antes de que un diseñador haga la GUI, entonces, ¿por qué odio que un arquitecto de datos haga el esquema?
gbjbaanb
2

Lo que sucede a menudo es que los desarrolladores usan incorrectamente sus objetos ORM como sus modelos de dominio.

Esto es incorrecto y vincula su dominio directamente a su esquema de base de datos.

Lo que realmente debería tener es modelos de dominio separados tan ricos como quieras y usar la capa ORM por separado.

Esto significa que necesitará un mapeo entre cada conjunto de objetos.

ozz
fuente
1
Esta es una buena idea, pero para proyectos más pequeños realmente comienza a sentirse como una exageración. Este enfoque también requiere una capa de traducción entre la capa de persistencia ORM y el modelo de dominio.
maple_shaft
@maple_shaft estuvo de acuerdo, y eso es lo que quise decir con "mapeo" :-)
ozz
@Ozz, la forma en que he trabajado es exactamente eso, las clases de entidad SON el modelo de dominio (y podría agregar con bastante éxito). Estoy de acuerdo en que vincula el modelo de dominio con el esquema, pero eso es exactamente lo que quiero, ya que uso la convención sobre la configuración, y el efecto secundario es que si veo un campo en una entidad, no necesito pensar mucho sobre el nombre de la tabla y columna donde se almacena esa información.
Augusto
@Augusto ¡Yo también lo he hecho! y como dice maple_shaft, está bien para pequeñas aplicaciones de estilo CRUD, pero hay muchos problemas a medida que el OP se está enterando. Un ejemplo podría ser donde tiene una tabla de mapeo de muchos a muchos, por ejemplo: StudentClasses, que mapea a los estudiantes a sus clases y solo contiene StudentID y classID, no necesariamente querrá mapear esto en su dominio. Eso es solo un ejemplo rápido de la parte superior de mi cabeza.
ozz
2
@Ozz: su comentario parece contradecir la idea misma de un ORM. Un ORM no "vincula su dominio directamente a su esquema de base de datos". El ORM asigna su dominio a un esquema de base de datos, sin la necesidad de una capa DAO separada. Ese es el objetivo de un ORM. Y la mayoría de los ORM manejan mapeos de muchos a muchos muy bien, sin necesidad de un modelo de dominio para la tabla de mapeo.
Kevin Cline
1

Los objetos de su dominio se pueden completar de la forma que desee, no es necesario utilizar Hibernate. Creo que el término apropiado es mapeador de datos . Es muy posible que sus datos persistentes tengan una estructura completamente diferente a los objetos de su dominio.

NimChimpsky
fuente
Actualmente estamos utilizando mapeadores de datos, pero el problema es que los procesos almacenados devuelven un conjunto mínimo de datos, que a veces no es suficiente para llenar un objeto (tal vez deberíamos permitir que los procedimientos almacenados devuelvan más información). Por ejemplo, el proceso de una tienda puede devolver un correo electrónico de usuario, nombre, apellido; mientras que otro agrega ID de usuario y una dirección. Dado que los datos son diferentes, estamos usando diferentes objetos para almacenar los datos, lo que significa que tenemos diferentes clases de 'Usuario'. Estoy tratando de evitar el uso de la herencia aquí, porque creo que es un uso incorrecto.
Augusto
@Augusto: ¿Interfaces?
Kramii reinstala a Monica el
@Karmii, no creo que las interfaces ayuden aquí, ya que entonces tendríamos que duplicar la lógica en diferentes clases. O podríamos usar interfaces y luego delegar el procesamiento a una clase auxiliar, pero eso no es realmente OO :(.
Augusto
1
@Augusto No entiendo el problema: "los procesos almacenados devuelven un conjunto mínimo de datos, que a veces no es suficiente para llenar un objeto" Entonces, altera el sproc o crea otro, y luego deja que el mapeador de datos haga el mapeo
NimChimpsky