Generando aplicaciones con AjGenesis

En estos días, estuve escribiendo unos ejemplos de AjGenesis


http://www.ajlopez.com/ajgenesis


mi proyecto de código abierto de generación de código, para producir, desde un modelo, aplicaciones tanto en ASP.NET 1.x/2.x, como en JSP, tanto usando SQL Server en el primer caso, como con MySql en Java, y tanto con ADO.NET, como con NHibernate, Hibernate, ya sea con arquitectura de capas, como en capas a la Domain-Driven Design de Evans.


Sigo escribiendo esos ejemplos, pero ya hay algo publicado en


AjGenesisExamples3.zip


Usaremos la versión del proyecto que está actualmente en desarrollo:


AjGenesis-0.4.3.zip


Quisiera en este artículo, comentar cómo funciona y se construye, uno de esos ejemplos. Permítanme primero repasar algo sobre el proyecto. Primero, “the big picture”:



Está escrito en Visual Basic .NET 1.x, y es código abierto, pues viene con una licencia tipo BSD, que permite utilizarlo en cualquier proyecto que quieran. Se puede usar como librería, invocado desde nuestro proyecto, se puede invocar desde la línea de comando, o puede ser utilizado desde el poderoso NAnt (esto último me permitió organizar mejor las tareas que realiza el generador de código).


En la página del proyecto (y en el proyecto mismo) hay varios ejemplos, que generan código desde un modelo, usando plantillas. Los ejemplos generan código PHP, Java, JSP, VB.NET, C#, ASP.NET, y hasta scripts de base de datos y procedimientos almacenados. Quisiera destacar dos puntos:



- El modelo del que parte es totalmente definible por el usuario



- Las tareas y plantillas a aplicar son totalmente programables y controlables



Esto lo diferencia de otros generadores. Podemos construirnos nuestro propio modelo, y sus propias plantillas, para generar los artefactos de texto que prefiera. Otros sistemas parten de la base de datos, y sólo generan un grupo de artefactos de textos predefinidos (por ejemplo, POJOs, plain old java objects, o DAOs, Data Access Objects). Pero con AjGenesis puede generar el artefacto de texto que se nos ocurra.


Para comprender mejor que el modelo se puede construir y consumir como uno lo disponga, ver un artículo anterior:


Generando Código- Hello World con AjGenesis


Ahí se describen los pasos iniciales, y un modelo que es totalmente libre.


Creando una aplicación



Encaremos hoy algo más completo. Necesitamos armar una solución sencilla, con dos tablas, sobre una base de datos SQL Server, con código VB.NET 2.0, con interface web, capa de servicios, capa de datos, entidades y componentes de negocio a la Microsoft. Queremos generar la solución, los proyectos, los scripts de creación de la base, y procedimientos almacenados. Este ejemplo está incluido en los ejemplos AjGenesisExamples3.zip. Primer paso: escribir el modelo.


El proyecto


En un directorio de proyectos de los ejemplos que acompañan este artículo, hay un directorio Projects/AjFirstExample.



 


En ese directorio está el archivo Project.xml que contiene el modelo.


 


<Project>
  <Name>AjFirstExample</Name>
  <Description>First Example using AjGenesis</Description>
  <Prefix>AjFE</Prefix>
  <Domain>com.ajlopez</Domain>
  <CompanyName>ajlopez</CompanyName>
  <Model>
    <Entities>
      <Entity Source=”Entities/Customer.xml”/>
      <Entity Source=”Entities/Supplier.xml”/>
    </Entities>
  </Model>
</Project>

 


Recordemos: el modelo es libre. Acá definimos, para los templates que vamos a usar, las entidades de nuestro modelo: customers y suppliers.


Las entidades


Para que un archivo XML no resulte terriblemente largo, AjGenesis permite que cualquier nodo del modelo se especifique en un archivo aparte. Es un criterio que he usado para definir cómo se escribe el modelo: el XML resultante no debe herir a la vista, debe ser entendible y abarcable en una lectura.


En el Project.xml, eso aparece en el caso de las entidades, con el atributo Source. Examinemos una entidad, escrita en Entities/Customer.xml:



<Entity>
  <Name>Customer</Name>
  <Description>Customer Entity</Description>
  <SetName>Customers</SetName>
  <Descriptor>Customer</Descriptor>
  <SetDescriptor>Customers</SetDescriptor>
  <SqlTable>customers</SqlTable>
  <Properties>
    <Property>
      <Name>Id</Name>
      <Type>Id</Type>
    </Property>
    <Property>
      <Name>Name</Name>
      <Type>Text</Type>
      <SqlType>varchar(200)</SqlType>
    </Property>
    <Property>
      <Name>Address</Name>
      <Type>Text</Type>
      <SqlType>text</SqlType>
    </Property>
    <Property>
      <Name>Notes</Name>
      <Type>Text</Type>
      <SqlType>text</SqlType>
    </Property>
  </Properties>
</Entity>



Hay atributos de la entidad, como su nombre y descripción, en singular y plural, que sirve para nombrarlas en las páginas resultantes, o dentro del código. Las propiedades son los campos a mantener en cada entidad. Vemos que en este ejemplo, no hay más que datos dentro de una entidad.


Aparte de las entidades, en otro directorio, Technologies, se especifica el modelo dependiente de la tecnología, como VbNet2:


<Technology>
  <Programming>
    <Dialect>VbNet2</Dialect>
  </Programming>
  <Database>
    <Dialect>MsSql</Dialect>
    <Name>AjFirstExample</Name>
    <Username>sa</Username>
    <Prefix>ajfe_</Prefix>
    <Host>(local)</Host>
  </Database>
</Technology>


Las plantillas


En el directorio Templates/VbNet2 encontramos



Son las plantillas para generación de código VB.Net 2.0. También encontraremos plantillas para C# 1.x y 2, Vb.NET 1, Java. Hay plantillas para usar Nhibernate, Hibernate, JSP, MySql, y conceptos de Domain-Driven Design. Todo desde el mismo modelo. Tomemos como muestra una plantilla, la que genera la entidad en Visual Basic, EntityVb.tpl:



<#
message “Generating Entity ${Entity.Name}”
include “Templates/VbNet2/VbFunctions.tpl”
include “Templates/VbNet2/Prologue.tpl”
#>

‘ Project ${Project.Name}
‘ ${Project.Description}
‘ Entity ${Entity.Name}
‘ ${Entity.Description}


Public Class ${Entity.Name}


‘ Private Fields

<# for each Property in Entity.Properties
  message “Procesando Campo ${Property.Name}”
#>
Private m${Property.Name} as ${VbType(Property)}
<#
  end for
#>
‘ Default Constructor

Public Sub New()
End Sub



‘ Public Properties


<#
  for each Property in Entity.Properties
    message “Procesando Propiedad ${Property.Name}”
#>
Public Property ${Property.Name}() as ${VbType(Property)}
  Get
    Return m${Property.Name}
  End Get
  Set(ByVal Value As ${VbType(Property)})
    m${Property.Name} = Value
  End Set
End Property
<#
end for
#>

End Class


Como antes, se usan estructuras de control, y recorrido de una entidad del modelo. No se maneja el XML. El formato XML es la forma de serialización del modelo. Durante el proceso de la plantilla, el modelo ya está en memoria, accesible desde variables dinámicas.


Los pasos


Ahora tenemos más archivos a generar: desde las páginas ASPX, y su código asociado, los proyectos de fachada de servicio, entidades, acceso a datos, el archivo de solución, y más. Para automatizar esta generación, el ejemplo tiene varios archivos de tareas, en el directorio Tasks, donde se describen los pasos a ejecutar. Hay dos grandes tareas: los pasos a ejecutar independientemente de la tecnología elegida, como completar el modelo, revisarlo, y las dependientes de la tecnología, como generar tal archivo JSP o ASPX, dependiendo de si queremos Java o .NET.


La tarea de completar el modelo está a cargo de Tasks\BuildProject.ajg, que comienza con:



‘ Build Project
‘ Complete the Project Data
‘ Project must be loaded in global variable Project

PrintLine “Completing Project ${Project.Name}”

include “Templates/EntityFunctions.tpl”
include “Templates/Utilities.tpl”

if not Project.Title then
  Project.Title = Project.Name
end if

if not Project.Version then
  Project.Version = “1.0.*”
end if

if not Project.SystemName then
  Project.SystemName = Project.Name
end if

Acá incluye algunas funciones auxiliares, y luego comienza a completar el modelo que reside en la variable Project. Ejemplo: si falta Project.Title le coloca como título el Project.Name. Prosigue:


 


for each Entity in Project.Model.Entities
  PrintLine “Entity ” + Entity.Name

  for each Property in Entity.Properties
    PrintLine “Property ” & Property.Name


   
if not Property.Description then
      Property.Description = Property.Name
    end if


    if not Property.Title then
      Property.Title = Property.Description
    end if


    if Property.Type=”Id” and not Property.SqlType then
      Property.SqlType=”int”
    end if


    if Property.SqlType and not Property.SqlColumn then
      Property.SqlColumn = Property.Name
    end if


    if Property.Type=”Id” and not Entity.IdProperty then
      Entity.IdProperty = Property
    end if


    if Property.Reference then



Mas adelante, se ejecutan las tareas de tecnología. Como ejemplo, veamos un fragmento de Tasks\BuildVbNet2.ajg:


<#
include “Templates/Utilities.tpl”
include “Templates/VbNet2/UtilitiesVb.tpl”


message “Creating Directories…”

FileManager.CreateDirectory(Project.BuildDir)
FileManager.CreateDirectory(“${Project.BuildDir}/Sql”)
FileManager.CreateDirectory(“${Project.BuildDir}/Src/${Project.Name}.Entities”)
FileManager.CreateDirectory(“${Project.BuildDir}/Src/${Project.Name}.Entities/My Project”)
FileManager.CreateDirectory(“${Project.BuildDir}/Src/${Project.Name}.Data”)
FileManager.CreateDirectory(“${Project.BuildDir}/Src/${Project.Name}.Data/My Project”)
FileManager.CreateDirectory(“${Project.BuildDir}/Src/${Project.Name}.Services”)

En este fragmento, se crean los directorios necesarios para albergar la solución. El nombre del directorio se extrae del modelo desde ${Project.BuildDir}.

Generando la solución


Podríamos lanzar las tareas desde la línea de comando, pero tenemos un build armado para el Nant, uno para cada tecnología. Ejecutamos las tareas build, buildsql, y deploysql de AjFirstExampleVbNet2.build:


 



En el directorio Build/AjFirstExample/VbNet2/Sql quedan los scripts de creación de la base y procedimientos almacenados. Y en el directorio hermano Src, sorpresa, tenemos la solución ya preparada:



Encontramos varios proyectos armados. Si levantamos la solución en el Visual Studio 2005, aparece todo el código generado:




Con otro archive build AjFirstExampleCSharp2.build, generamos la misma solución en CSharp:




Encontrarán otros proyectos y ejemplos de .build, que usan NHibernate, Hibernate, JSP, y conceptos de DDD.


Reflexiones


Claro, no todo se puede generar automáticamente. Es importante tener siempre presente esto. Pero en el día a día, reconozcamos que tenemos cantidad de texto repetitivo, tareas que bien podemos encargar al propio software.

Recordemos siempre: el modelo es libre. Los ejemplos presentados son solamente ejemplos: podemos general el modelo que queremos, y escribir las plantillas que necesitamos. Es importante escribir las plantillas de forma que el código generado sea similar al que hubiéramos generado nosotros. Si no nos sentimos cómodos con el código generado, si no tiene nuestro estilo, nuestra experiencia, terminamos generando algo que no entendemos.

Otra reflexión: el modelo debe ser independiente de la tecnología. En el ejemplo final, hemos visto cómo, desde el mismo modelo, se puede generar la solución para VB.NET2, y para CSharp. Encontrarán las plantillas para generarla con Nhibernate, con DDD, con Hibernate, con Java y JSP, y podemos escribir la que necesitamos.

El software debe ayudarnos a generar software. Nuestra experiencia cuenta: lo que aprendimos de hacer aplicaciones, podemos volcarlo en esta especie de sistema experto, generador de código. Justamente, en el futuro, espero poder incorporar al proyecto, en las plantillas, más toma de decisiones: así como volcamos nuestra experiencia en escritura de aplicaciones, podemos incorporar nuestro conocimiento acumulado sobre patrones, arquitectura, estilos de programación.

Y al ser de código abierto, AjGenesis permite que lo extendamos, a nuestro gusto y necesidad.

Se aceptan sugerencias, historias de uso. Pueden escribir comentarios a este artículo, o escribirme. Desde ya, muchas gracias por cualquier “feedback”.

Nos leemos!

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

This entry was posted in 1389, 1392, 2643, 2884, 3463, 6145. Bookmark the permalink.

8 Responses to Generando aplicaciones con AjGenesis

  1. Alejandro Gianazza says:

    Angel, generé el codigo pero al compilar en este modulo no encuentra a AjFramework y los objetos DataParameter, DataService, … no los reconoce.
    ¿De donde vinculo AjFramework al proyecto?


    ‘ File generated using AjGenesis
    http://www.ajlopez.com/ajgenesis
    http://www.ajlopez.net/ajgenesis
    ‘ Open Source Code Generation Engine


    ‘ Project AjFirstExample
    ‘ First Example using AjGenesis
    ‘ Entity Customer
    ‘ Customer Entity

    Imports System.Collections.Generic

    Imports AjFramework.Data

    Imports AjFirstExample.Entities

    Public Class CustomerData

    Public Sub Insert(entity as Customer)
    Dim dpid As New DataParameter

    dpid.Value = entity.Id

    DataService.ExecuteNonQuery(“CustomerInsert”, CommandType.StoredProcedure, _
    dpid, _
    entity.Name, _
    entity.Address, _
    entity.Notes _
    )

    entity.Id = dpid.Value
    End Sub

    Public Sub Update(entity as Customer)
    DataService.ExecuteNonQuery(“CustomerUpdate”, CommandType.StoredProcedure, _
    entity.Id, _
    entity.Name, _
    entity.Address, _
    entity.Notes _
    )
    End Sub

    Public Sub Delete(id as Integer)
    DataService.ExecuteNonQuery(“CustomerDelete”, CommandType.StoredProcedure, id)
    End Sub

    Public Function GetById(id as Integer) as Customer
    Dim reader as IDataReader = Nothing

    try
    reader = DataService.ExecuteReader(“CustomerGetById”, CommandType.StoredProcedure, id)

    if not reader.Read() then
    return Nothing
    end if

    Dim entity as Customer

    entity = Make(reader)

    return entity
    finally
    reader.Close()
    end try
    End Function

    Public Function GetAll() as List(of Customer)
    Dim reader as IDataReader
    Dim list as new List(of Customer)()

    reader = DataService.ExecuteReader(“CustomerGetAll”, CommandType.StoredProcedure )
    Dim entity as Customer

    while reader.Read()
    entity = Make(reader)
    list.Add(entity)
    end while

    reader.Close()

    return list
    End Function

    Public Function GetAllAsDs() as DataSet
    return DataService.ExecuteDataSet(“CustomerGetAll”, CommandType.StoredProcedure )
    End Function

    Private Function Make(reader as IDataReader) as Customer
    Dim entity as new Customer

    if reader(“Id”) is System.DbNull.Value then
    entity.Id = 0
    else
    entity.Id = CType(reader(“Id”),Integer)
    end if
    if reader(“Name”) is System.DbNull.Value then
    entity.Name = Nothing
    else
    entity.Name = CType(reader(“Name”),String)
    end if
    if reader(“Address”) is System.DbNull.Value then
    entity.Address = Nothing
    else
    entity.Address = CType(reader(“Address”),String)
    end if
    if reader(“Notes”) is System.DbNull.Value then
    entity.Notes = Nothing
    else
    entity.Notes = CType(reader(“Notes”),String)
    end if

    return entity
    End Function
    End Class

  2. alejandro nelis says:

    Angel:

    Mil gracias por darnos un poco de vos.

    Saludos cordiales.

    Alejandro Nelis

  3. Pablo says:

    A ver… para vos los archivos de configuraciones de Spring son un infierno… y los tuyos??? ademas no te compares con una BESTIA como Spring, lo unico q haces ahi es manipular texto…

    “vieron la luz” …

    Por favor. Solo en Argentina señoras y señores

  4. lopez says:

    Hola gente!

    Para Alejandro y otros que tengan ese problema: Tendría que incluir el ajframework dentro de los ejemplos. Es una dll y un código abierto, que uso como prueba de concepto, podrían usar Enterprise Library, si quisieran. Pueden obtener esa dll del ejemplo:

    http://www.ajlopez.com/downloads/CursoPuntoNet/CodeDotNetArch.zip

    El código de ese utilitario está en

    http://www.ajlopez.com/downloads/AjFramework-0.1.zip

    Nos leemos!

    Angel “Java” Lopez

  5. lopez says:

    Para Pablo:

    Ciertamente Spring es grandioso. Sigo las ideas de Rod Johnson desde antes de Spring, cuando tenía su propio framework en interface21.

    Pero no es la idea de AjGenesis renegar de frameworks. Al contrario: adoptarlos, si se necesitan, o cambiar de framework, plataforma, tecnología, o lo que sea, a partir de un modelo libre.

    Jeje… lo de “ver la luz” viene de las intentos de chistes, que hago en mis cursos. Una de las primeras apariciones de la frase fue hace años en:

    http://msmvps.com/blogs/lopez/archive/2007/01/20/evangelizando-net.aspx

    Nos leemos!

    Angel “Java” Lopez

  6. Eduardo says:

    Es verdad lo unico que haces es manipular texto,

    Como los poetas…

    Muchas gracias por compartir tu trabajo con nosotros.

    Saludos

  7. Luis says:

    Angel, todo esta esta genial, decime es posible mediante AjGenesis generar una aplicaion(NHibernate) pero a partir de un esquema de dataset?

  8. Espinete says:

    Para Oracle y C# 3.0 hay algo ? salu2

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>