AjModel, Modelo por Código (Parte 1) El Origen

Siguiente Post


Ya saben que soy un gran promotor de elevar el nivel de abstracción, separar los problemas de las soluciones, etc. Uno de mis proyectos, AjGenesis, está dedicado a usar modelos de libre definición que pueden producir, con generación de código, aplicaciones en diferentes plataformas y tecnologías. Ahora, quiero explorar otra manera de hacer las cosas: en lugar de generación de código, podemos armar el sistema en ejecución, habiendo enriquecido un modelo inicial.


Primeras Ideas


Es natural, en estos tiempos, tener un modelo (de dominio, de negocio, sea como lo hayan nombrado):



Podemos tener clases principales (entidades), asociaciones, colecciones, servicios con méetodos, etc. Y podemos armar esos artefactos usando TDD o no. Pero esos elementos SON el núcleo del sistema. Pero para poder usarlo en otros ámbitos (interfaz de usuario, persistencia), necesitamos enriquecer, extender el modelo. Un ejemplo de AjGenesis:


<Entity>
    <Name>Customer</Name>
    <Description>Customer</Description>
    <SetName>Customers</SetName>
    <Descriptor>Customer</Descriptor>
    <SetDescriptor>Customers</SetDescriptor>
    <SqlTable>customers</SqlTable>
    <Properties>
	<Property>
	    <Name>Id</Name>
	    <Description>Id</Description>
	    <Type>Id</Type>
	    <SqlType>int</SqlType>
	</Property>
	<Property>
	    <Name>Name</Name>
	    <Description>Name</Description>
	    <Type>Text</Type>
	    <SqlType>varchar(200)</SqlType>
	    <Required>true</Required>
	</Property>
    </Properties>
</Entity>

Este modelo abstracto tiene atributos como Name (el nombre de una entidad), SetName (el nombre de un conjunto de entidades) que podemos usar como nombres a usar en nuestro código a generar. Pero también tiene Descriptor, SetDescriptor, Description a ser usadas en la generación de la interfaz de usuario. Estos atributos adicionales son parte de lo que llamo “Modelo Enriquecido”: un modelo con propiedades, información adicional que nos ayudan a describir el modelo en términos humanos. Podría agregar datos adicionales (como tipos de SQL, longitud), a ser usado en persistencia, pero no es un objetivo en esta primera parte de mi experimiento:



Los Modelos de interfaz dependen de la tecnología a usar. El modelo extendido no depende de la tecnología. Una vez que tenemos el modelo extendido describiendo entidades, propiedades en detalle, entoncs los View Models pueden ser producidos en “runtime” (ejecución) para cada tecnología final. Y algo importante: si el “nombre humano” de una entidad o propiedad no está especificada, podemos usar el nombre de código. Esta es el dato “asumido” de un modelo extendido: usa el modelo, Luke! ;-)


En los próximos párrafos muestro código para extender el modelo. Pero siempre tengo como decisión clave de diseño: puedo generar toda la interfaz aún si no me indican nada adicional sobre el modelo inicial.


Algo de Código


Entonces, hace una semana comencé a codificar AjModel. Pueden ver mi progreso en mi AjCodeKatas Project en trunk/AjModel. Mis primeros modelos extendidos están compuestos de Entidades (los principales objetos del modelo) y Propiedades:



Estoy probando con una clase simple Customer:


public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Notes { get; set; }
}

Un test de ejemplo:


[TestMethod]
public void GetProperties()
{
    EntityModel model = new EntityModel(typeof(Customer));
    var properties = model.Properties;
    Assert.IsNotNull(properties);
    Assert.AreEqual(4, properties.Count);
    Assert.AreEqual("Id", properties.First().Name);
    Assert.AreEqual("Name", properties.Skip(1).First().Name);
    Assert.AreEqual("Address", properties.Skip(2).First().Name);
    Assert.AreEqual("Notes", properties.Skip(3).First().Name);
}

Lo que viene: ServiceModel (describiendo servicios), y Model (conteniento modelos de entidades y servicios). Más propiedades en EntityModel, como .Descriptor, .SetDescriptor, .Description, etc.


Cada modelo de interfaz depende de la tecnología. Como prueba de concepto, estoy usando ASP.NET MVC, en el proyecto AjModel.WebMvc. Hay un EntityController, no genérico aún, dedicado a Customer por ahora:


public ActionResult Index()
{
    var model = new EntityListViewModel();
    model.Entities = Domain.Instance.Customers;
    model.EntityModel = new EntityModel(typeof(Customer));
    return View(model);
}

Domain.Instance.Customers es una lista en memoria de clientes (“customers”). Algo de código en la vista:


<h2>
    <%= this.Model.Title %></h2>
<table>
    <tr>
        <% foreach (var property in this.Model.EntityModel.Properties)
           {
        %>
        <th>
            <%= Html.Encode(property.Name) %>
        </th>
        <%} %>
    </tr>
    <% foreach (var entity in this.Model.Entities)
       { %>
       <tr>
       <% foreach (var property in this.Model.EntityModel.Properties)
          { %>
          <td><%= property.GetValue(entity) %></td>
       <%} %>
       </tr>
    <%} %>
</table>

Próximos Pasos


Quiero tener una interfaz fluent como:


model.ForEntity<Customer>()
      .Descriptor("Customer")
      .SetDescriptor("Customers")

Y quiero usar Expression<Func…> para usar validación de código (define propiedades por código en vez de por un nombren en string como “Name”):


model.ForEntity<Customer>()
     .ForProperty(c => c.Name)
         .Description("Customer Name")
         .IsRequired()

O algo como:


model.ForEntity<Customer>()
     .Property(c => c.Name,
          pm => pm.Description("Customer Name")
                  .IsRequired()
        )
     .Property(c => c.Address,
          pm => pm.Description("Customer Address")
        )

donde pm es un PropertyModel. .ForProperty retorna un PropertyModel con interfaz fluent. Pero .Property retorna un fluent sobre EntityModel, así que puedo usar el segundo parámetro pm => … como una forma de indicar qué quiero hacer con la propiedad, sin perder el modelo fluent de la entidad.


Pero no hay código de esto en el repositorio, todavía. Ya me conocen, estoy haciendo esto en mi tiempo libre, tengo que ir a trabajar, todavía necesito trabajar, la fortuna de la familia quedó en las mesas de Montecarlo ;-)


Generación de Código vs En Runtime


Yo sigo prefiriendo la generación de código desde un modelo de libre definición: marca una clara separación del problema de su posible solución tecnológico. Y puede ser fácilmente adaptado a los cambios en tecnología y lenguajes de programación (Java vs Scala vs C# vs … y lo que venga en el futuro.. ;-). Pero muchos desarrolladores prefieren trabajar directamente con código. Por eso este experimento. Desde finales del os noventa, conozco morphic  en el mundo Smalltalk (gracias a las reuniones de SUGAR Smalltalk User Group Argentina, ahora disuelto). Ver History of Morphic. Y Naked Objects es otra forma de exponer el modelo al usuario/desarrolladore. Ver Naked Objects, Wikipedia. Leo ahí:


1. All business logic should be encapsulated onto the domain objects. This principle is not unique to naked objects: it is just a strong commitment to encapsulation.

2. The user interface should be a direct representation of the domain objects, with all user actions consisting, explicitly, of creating or retrieving domain objects and/or invoking methods on those objects. This principle is also not unique to naked objects: it is just a specific interpretation of an object-oriented user interface (OOUI).

The original idea in the naked objects pattern arises from the combination of these two, to form the third principle:

3. The user interface should be created 100% automatically from the definition of the domain objects. This may be done using several different technologies, including source code generation; implementations of the naked objects pattern to date have favoured the technology of reflection.

The naked objects pattern was first described formally in Richard Pawson’s PhD thesis[1] which includes a thorough investigation of various antecedents and inspirations for the pattern including, for example, the Morphic user interface.


Y en este siglo, también encontré Magritte.


Ok, es tiempo de explorar la forma runtime/reflection/lambdas en .NET. Pros: los desarrolladores pueden extender el modelo desde la IDE y escribiendo código. Cons: está limitado a una tecnología, en este caso .NET; nuevos modelos dinámicos de interfaz pueden ser difíciles de escribir; soportar extensiones “custom” o manuales puede no ser fácil.


Bien, suficiente por ahora, vuelvo a codificar (y al trabajo). Sigo teniendo el Efecto Coto ;-)


Nos leemos!


Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

This entry was posted in 1389, 15035, 16021, 3463. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>