Descomposición de objetos

Los aspectos

Esta aproximación se basa en un análisis conceptual de la información, a través de los siguientes problemas: ¿son las relaciones características esenciales de un objeto de negocio, como pueden serlo las propiedades escalares? Sin entrar en esta cuestión tan abstracta, podemos formular otra más genérica: ¿existen diferentes categorías de propiedades? No es una cuestión trivial, ya que en el día a día de muchos desarrolladores se plantea la necesidad de mantener diferentes versiones de los datos, o de gestionar versiones traducidas de algún contenido.

Pongamos como ejemplo un gestor de contenidos para un blog, en el que queremos versionar las modificaciones que se aplican a los artículos, así como traducciones y varios estados posibles de los posts (borrador, revisado, publicado).

Nuestro objeto de negocio es el Artículo, y sus atributos los podemos definir fácilmente: título, autor, cuerpo, categoría, fecha. Bien creemos un objeto en PHP o un esquema de bases de datos, afrontar los problemas del versionado, traducción y estado de los artículos no es fácil, ya que no podemos decir que sean atributos propios del elemento Artículo, pero sí podemos decir en un momento dado que el artículo A tiene la versión B, está en el idioma C y está en estado D. Podemos incluir esta información como atributos del esquema de base de datos o como propiedades de nuestro objeto Artículo, pero ¿seríamos fieles al modelo conceptual que representa?

La conclusión a este problema, al margen de diferentes implementaciones prácticas, es que los aspectos (idioma, versión, estado, etc.) no son características inherentes de nuestros objetos de negocio, definidos en un diagrama entidad-relación. Utilizando un símil gráfico, si representamos nuestro entidad-relación en un dibujo bidimensional, el versionado aportaría una tercera dimensión, la traducción una cuarta, etc.

Pero ¿por qué hablar de esta cuestión ahora y no antes, ya que estos problemas los encontramos en cualquier sistema de persistencia de la información? Sencillamente, porque ninguno de los sistemas analizados hasta el momento —incluidas las construcciones nativas de los casi todos lenguajes de programación— es capaz de mantener de forma nativa los aspectos, al menos los mencionados. Y si no se soporta de forma nativa, entonces es necesaria la descomposición.

La descomposición es, básicamente, reducir la información compleja a una estructura tan sencilla y manejable que podamos trabajar con ella en nuestro almacenamiento de datos.

Como plantea Kore Nordmann en "Why active record sucks", ninguna de las implementaciones de ActiveRecord y ORM soporta los aspectos mencionados. El autor además plantea un posible esquema relacional para descomponer y almacenar información con los aspectos de versionado, traducción y estado. Observando la estructura propuesta, y a la luz de ejemplos como eZ Publisher o Wikipedia, que realizan prácticas similares, la principal ventaja del mapeo objeto-relacional —mantener en la base de datos la estructura de la información— se desmorona. Por tanto, en un escenario así el mapeo objeto-relacional pierde mucha fuerza, en favor de otros sistemas de persistencia.

Llegados a este punto, el Object Freezer de Sebastian Bergmann comienza a tener sentido: los objetos —como unidad de información compleja pero fácilmente manejable desde la programación— son "congelados" en un momento dado, y su estado (es decir, el conjunto de sus propiedades y el valor que tienen en el momento de la congelación) es expresado en forma de array:

require_once 'Object/Freezer.php';

class A {
	protected $b;

	public function __construct() {
		$this->b = new B;
	}
}

class B {
	protected $foo = 'bar';
}

$freezer = new Object_Freezer;
$frozen_object = $freezer->freeze(new A);
var_dump($frozen_object);
    

Salida:


    array(2) {
      ["root"]=>
      string(36) "32246c35-f47b-4fbc-a2ad-ed14e520865e"
      ["objects"]=>
      array(2) {
        ["32246c35-f47b-4fbc-a2ad-ed14e520865e"]=>
        array(3) {
          ["className"]=>
          string(1) "A"
          ["isDirty"]=>
          bool(true)
          ["state"]=>
          array(2) {
            ["b"]=>
            string(57) "__php_object_freezer_3cd682bf-8eba-4fec-90e2-ebe98aa07ab7"
            ["__php_object_freezer_hash"]=>
            string(40) "8b80da9c38c0c41c829cbbefbca9b18aa67ff607"
          }
        }
        ["3cd682bf-8eba-4fec-90e2-ebe98aa07ab7"]=>
        array(3) {
          ["className"]=>
          string(1) "B"
          ["isDirty"]=>
          bool(true)
          ["state"]=>
          array(2) {
            ["foo"]=>
            string(3) "bar"
            ["__php_object_freezer_hash"]=>
            string(40) "e04e935f09f2d526258d8a16613c5bce31e84e87"
          }
        }
      }
    }

Object Freezer no soluciona la cuestión de los aspectos —que, por otra parte puede ser afrontada a través de la programación orientada a objetos y, por ende, almacenada a través de Object Freezer—, pero es una gran herramienta para simplificar la persistencia en la mayoría de entornos (sobre todo web) donde los aspectos no son necesarios o son tratados de otro modo.

En el desarrollo original de Sebastian Bergmann el objeto a persistir, una vez convertido en array, es almacenado en CouchDB, una base de datos orientada a documentos que maneja la información en forma de objetos anidados, expresados en JSON. Así que, una vez se ha congelado el objeto, basta con codificarlo en JSON y enviarlo a CouchDB a través de su API REST (y en caso de querer recuperar el objeto, el proceso contrario):

$this->send('POST', '/$database/_bulk_docs', json_encode($frozen_object));

Entendiendo que el objeto congelado es un array resultado de la descomposición del objeto, podemos trabajar con otros métodos de almacenamiento diferentes a CouchDB/JSON, por ejemplo, bases de datos relacionales.