Modelos experimentales de persistencia para aplicaciones web (II): Object Freezer-Relational

En el anterior post vimos un invento de Sebastian Bergmann llamado Object Freezer, que consiste en almacenar objetos PHP en CouchDB de forma automática. Es un buen método para no tener que definir esquemas de bases de datos, escribir sentencias SQL, hacer mapeo objeto-relacional… en definitiva, es una solución que nos puede ahorrar un montón de trabajo.

Pero, una vez aceptadas las ventajas de la congelación, quizá sea necesario guardar esos objetos en una base de datos diferente a CouchDB. Por ejemplo, en MySQL. Y de esto trata el presente artículo: de congelar objetos y guardarlos en MySQL.

Un brevísimo repaso a la congelación

Congelar un objeto (“freeze”) es convertirlo en un array manteniendo los objetos hijos y evitando duplicidad de los mismos. La operación inversa, la descongelación (“thaw”), sería recoger ese array y convertirlo de nuevo en el objeto original.

¿Por qué MySQL, si con NoSQL nos ahorramos todo el trabajo?

  1. A veces no es viable o rentable cambiar de servidor de bases de datos, por la inversión económica y de formación que requieren los sistemas tradicionales.
  2. Interoperabilidad: si en el futuro queremos migrar la aplicación, quizá poder manipular la información con SQL facilite la tarea.
  3. Rendimiento: las bases de datos relacionales se basan en algoritmos muy evolucionados y rápidos, y sistemas como MySQL-MyISAM han demostrado un rendimiento muy alto.

Entonces… ¿Se trata de guardar arrays en MySQL?

Exacto. Eso es lo que hace Object Freezer-Relational, una extensión de Object Freezer escrita por un servidor :-)

Lo primero que debemos pensar a la hora de guardar objetos en MySQL es: ¿cuál es el esquema de tablas? Porque si con CouchDB éramos NoSQL y guardábamos la información en arrays JSON, ahora tenemos que ceñirnos al álgebra relacional, las tablas, los índices y las claves primarias.

Así que este es el esquema que podemos usar:

  • Tabla objects
    • id: el ID único del objeto.
    • className: indica la clase de la cual es instancia el objeto almacenado.
    • isDirty y isRoot: dos atributos que utiliza Object Freezer internamente.
  • Tabla properties
    • name: nombre del atributo.
    • value: contiene el valor del atributo o bien un ID si el atributo hace referencia a otro objeto.
    • type: tipo de dato (int, float, string, boolean, array, objeto…).
    • object_id: ID del objeto al que pertenece el atributo. Hace referencia a objects.id.

Llegados a este punto uno podría pensar: “hey, te estás cargando el modelo relacional”. Efectivamente. Si queremos guardar objetos sin definir su esquema de BD, no nos queda más remedio que soluciones experimentales como la que te presento hoy. Sí, es una “des-normalización”, pero gracias a ella te ahorras escribir SQL.

Object Freezer-Relational se encarga de gestionar este esquema. De hecho, no es siquiera necesario crearlo, ya que lo hace automáticamente.

Vale, ya veo que mola. ¿cómo se usa?

Usar Object Freezer-Relational es tan sencillo como usar Object Freezer indicando los datos de acceso a MySQL:

$storage = new Object_Freezer_RelationalStorage(
    new Object_Freezer,
    NULL,
    FALSE,
    new MysqlStorage(
        "localhost",                    //Servidor MySQL
        "freezer",                      //Usuario
        "passw0rd",                     //Contraseña
        "freezer",                      //Base de datos
        3306,                           //Puerto
        MysqlStorage::ENGINE_INNODB));  //Motor MySQL

A partir de ahí, podemos usar el objeto $storage de la misma forma que el Object Freezer original, con CouchDB, ya que es la misma API.

Object Freezer-Relational está disponible en SourceForge. Puedes descargarlo y probarlo, echar un vistazo al código o leer la documentación si te interesa. Y por supuesto, comentar qué te parece la idea de guardar objetos en una base de datos MySQL :-)

Modelos experimentales de persistencia para aplicaciones web (I): congelación de objetos con Object Freezer

Hoy comienzo una serie de artículos dedicados a exponer diferentes patrones y arquitecturas de software para un problema tradicional: el almacenamiento de datos. Hacer consultas SQL a una base de datos relacional es una de las soluciones más extendidas, pero no es la única. Hay alternativas como las que te voy a presentar en este artículo y los siguientes.

Disclaimer

Las ideas que voy a presentar son experimentales. Son conceptos, con su discusón y su implementación (casi siempre parcial), pero no son estándares ampliamente probados y usados en el mundo real. El objetivo de estos artículos es reflexionar, discutir, probar y ver que algunas soluciones pueden ser útiles en ciertos escenarios, y otras quizá no valgan para nada. :-P

La congelación de objetos

La primera idea que voy a presentar es un concepto de Sebastian Bergmann. Los datos que se almacenan deben ser objetos de PHP. Se trata de “congelar” los objetos de una forma similar a como lo hacen las funciones de serialización. La diferencia es que el método de congelación entiende las relaciones entre objetos (es decir, que un atributo de un objeto sea, a su vez, otro objeto), de tal forma que si el objeto A tiene una referencia a B y el objeto C tiene una referencia a B también, al congelar los objetos A y C la referencia a B será un puntero al objeto congelado B, en lugar de congelar B dentro de A y B dentro de C, duplicando los datos, como hace serialize().

Pero ¿qué quiere decir congelar objetos? Pues sencillamente convertirlos en arrays para poder guardarlos en CouchDB, una base de datos orientada a documentos en la que los datos se almacenan en JSON.

Para comprender el funcionamiento de Object Freezer, nada mejor que un artículo de su autor: Freezing and Thawing PHP Objects.

¿Qué problemas soluciona Object Freezer?

  1. La definición y uso de esquemas relacionales. Ya no son necesarias las tablas, claves primarias, foreign keys, etc.
  2. El mapeo objeto-relacional. No hay que definir la conversión de tablas a objetos y viceversa.
  3. El almacenamiento sencillo de atributos vectoriales: es una forma fácil y automática de guardar arrays en la base de datos.
  4. ¡Tipado dinámico! En PHP se pueden añadir nuevos atributos a un objeto ya existente en tiempo de ejecución. No sería posible guardar este tipo de objetos en esquemas relacionales fijos, pero con Object Freezer es posible.

Además, tiene la ventaja de que, al guardar los objetos en CouchDB se maneja el versionado de los objetos, ya que CouchDB controla los cambios de todos sus registros.

Dame algo de código

//Una clase cualquiera
class Coche {
    private $color = 'rojo';
} 

//Una instancia de una clase cualquiera
$ferrari = new Coche();

require_once 'Object/Freezer/Storage/CouchDB.php'; 

//Instancia el freezer indicando los datos de conexión a CouchDB
$storage = new Object_Freezer_Storage_CouchDB('test', new Object_Freezer, TRUE, 'localhost', 5984); 

//Congela el objeto y lo almacena en CouchDB, devolviendo el ID auto-generado
$id = $storage->store($ferrari);

Así de sencillo. CouchDB asigna un ID único a cada nuevo registro. Para leer un registro:

//Devuelve el objeto de tipo Coche con $id
$objeto = $storage->fetch($id);

¿Es adecuado Object Freezer para mi proyecto?

La decisión de almacenar datos sin un esquema fijo es tan importante como la de usar una base de datos relacional o una NoSQL. ¿Es NoSQL bueno? No vamos a discutirlo aquí, pero sí citaré un tweet que leí una vez:

Usar NoSQL es como ir sin calzoncillos. Es cómodo, pero peligroso.

No obstante, hay una cuestión a tener en cuenta en la decisión: si usamos Object Freezer es cierto que la base de datos será schema-less, pero no es cierto que nuestros datos no estén estructurados, ya que ¡usamos objetos! Sería más correcto decir que los datos no están estructurados en la base de datos, pero sí en el código.

Si hemos hecho un buen diseño de las clases y sus relaciones, Object Freezer puede ser una solución rápida y fácil para guardar esos objetos en una base de datos muy interesante como CouchDB.

Sin embargo, no creo que sea buena idea para proyectos…

  • con fuerte necesidad de integridad referencial, o
  • que no estructuran toda la información con objetos, o
  • grandes, o
  • si crees que el modelo almacenamiento de datos cambiará, o
  • si no sabes usar las vistas de CouchDB para obtener listas de datos filtradas, indizadas, etc.

Creo que con esto es suficiente introducción. Puedes consultar la presentación que he citado arriba y el blog de Sebastian Bergmann para tener más información sobre Object Freezer, que por cierto es software libre :-)

Esto es todo por hoy. Ahora te toca a ti: ¿qué te parece el concepto de “congelar los objetos”? ¿Usarías Object Freezer? ¿Usarías CouchDB? ¿Conoces soluciones similares para otros lenguajes de programación?