lunes, 19 de noviembre de 2007

Desarrollar en 3 capas con .NET (parte 2) - La controladora y la presentación

Antes de mostrar el manejo de la capa de negocio, les aclaro el concepto de Viatico:
"es una retribución diaria que cobra un empleado cuando la empresa lo envía a realizar alguna tarea fuera de su ámbito normal de trabajo (normalmente a otra ciudad), este importe diario depende de la categoría del mismo. A esto se le suman todos los gastos extras que surjan por movilidad, inscripción a cursos, etc. Normalmente se genera una Solicitud que , en caso de ser aprobada por un superior genera el Viático correspondiente." (los sustantivos marcados en negrita corresponden a algunos de los objetos de negocio de nuestro sistema).

Aclarado este punto importante para poder entender mínimamente el objetivo del sistema, a continuación veremos como desarrollar nuestra capa de negocio.

La capa de negocio (ó dominio del problema) de nuestro sistema, estará compuesta de todos los procesos que fueron analizados como casos de uso, por lo que es común denominar a estas clases : controladora del caso de uso XX.

Capa de negocio (componente ViaticoProcesos)

En mi caso de ejemplo tengo una clase CSolicitud que posee la responsabilidad de controlar todos los procesos inherentes al manejo de Solicitudes de viáticos.
Si quisiéramos darle más flexibilidad (y en consecuencia mayor reutilización futura) al sistema, deberíamos desarrollar una interfaz que agrupe los métodos comunes a todos los posibles sistemas de Viáticos (IViaticoSolicitud) .
Logrando cierta abstracción defino los contratos de mi interfaz:

public interface IViaticoSolicitud
{
void CrearSolicitud(Solicitud nueva);
Viatico AutorizarSolicitud(Solicitud s);
void ImprimirSolicitud(Solicitud s);
List BuscarSolicitud(Solicitante s);
List
BuscarSolicitud(Solicitante s, DateTime Desde);
List BuscarSolicitud(Solicitante s, DateTime Desde, Boolean Aprobadas);
Solicitud BuscarSolicitud(int id);
}

Asi que entonces mi clase CSolicitud implementará mi IViaticoSolicitud:


public class CSolicitud:IViaticoSolicitud
{
// Declaro los DAOs con los que va a trabajar mi controladora

ISolicitudDao solicitudDao;

IViaticoDao viaticoDao;

IEmpleadoDao empleadoDao;
IMovilidadDao movilidadDao;


public CSolicitud()

{ // Creo la fabrica y los DAOs que usaré
NHibDAOFabrica fabricaDAO = new NHibDAOFabrica();

this.solicitudDao = fabricaDAO.GetSolicitudDao();

this.viaticoDao = fabricaDAO.GetViaticoDao();

this.empleadoDao=fabricaDAO.GetEmpleadoDao();

this.movilidadDao = fabricaDAO.GetMovilidadDao();

}


#region IViaticoSolicitud Members


public void CrearSolicitud(Solicitud nueva)

{
if (nueva.Solicitante != null)
{ try { this.solicitudDao.Save(nueva); }
catch { thro
w; }
}
}

public Viatico AutorizarSolicitud(Solicitud s)

{
try
{ // Marco como aprobada la Solicitud
s.Aprobada = true;
// Creo Viatico basado en la Solicitud aprobada
Viatico v = new Viatico();
v.Estado = Viatico.estado_viatico.Creado;
v.Solicitud = s;

v.Numero = this.viaticoDao.DarUltimoNro()+1; // Verificar concurrencia...
// Persisto cambios en Solicitud y Grabo nuevo viatico
this.solicitudDao.SaveOrUpdate(s);

this.viaticoDao.Save(v);

return v;
}
catch { throw ; }
}

public void ImprimirSolicitud(Solicitud s)
{
throw new Exception("The method or operation is not implemented."); }

public List BuscarSolicitud(Solicitante s)
{
try

{
List lista = this.solicitudDao.DarSolicitudesXSolicitante(s,null,null);

return lista;
}
catch { throw ; }

}


public List BuscarSolicitud(Solicitante s, DateTime Desde)
{
try
{
List lista = this.solicitudDao.DarSolicitudesXSolicitante(s, Desde, null);
return lista;
}
catch { throw ; }

}


// etc, etc ..............


#endregion

}

Como verán muchos de los métodos solicitan objetos a la capa de acceso a datos (clases DAO), por lo que termina siendo un llama
do a un método similar en el DAO. Aquí es donde mis alumnos dicen " porqué debemos hacer las cosas por duplicado?, y si llamamos desde la capa de presentación al método del DAO directamente ? ".
OK, el tema es que la Controladora posee métodos como
AutorizarSolicitud(Solicitud s), donde debemos desarrollar código que sólo es inherente al proceso de negocio y además trabajar en una transacción con varios objetos DAO.
En este ejemplo se cambia un atributo del objeto Solicitud, se crea un objeto Viatico, se pide un nro. de viático a un método estático de Viatico, y se persisten ambos objetos, vía llamado a los métodos de sus DAOs.

Capa de presentación Web (componente ViaticoWeb)

Por último, veamos un ejemplo de mi Capa de presentación Web, que permite la autorización de determinada solicitud de viático.
La página web muestra una lista de las solicitudes a autorizar por este usuario (jefe).
Selecciona una, y pulsa el botón "Autorizar".



Al pulsar este botón lo captura el evento "click" del mismo y este es el código:

protected void ButtonAutorizar_Click(object sender, EventArgs e)
{
Solicitud elegida=(Solicitud)Session["SolicitudBuscada"];
if (elegida!= null)
{
// creo controladora de Proceso de Solicitudes
CSolicitud cSolicitud = new CSolicitud();
try
{
Viatico v=cSolicitud.AutorizarSolicitud(elegida);
this.CargarGrid((Usuario)Session["Usuario"]);
this.LabelError.Text = "Viatico"+v.Numero.ToString()+" CREADO";
}
catch
{
Exception ex = new Exception();
this.LabelError.Text = ex.Message;
}
Response.Redirect("~/Admin/Autorizar.aspx");
}
else
this.LabelError.Text = "Seleccione una Solicitud para Autorizar...";
}

Como ven , se crea un objeto de tipo CSolicitud y se llama a su método AutorizarSolicitud pasándole como parámetro la solicitud seleccionada por el usuario. Cualquier error en el proceso llamado será capturado (catch), mostrándose el mensaje de error pertinente.

Capa de Objetos de negocio (componente ViaticoBO)

Mis objetos de negocio son clases independientes de la tecnología que se use para persistencia.
Ejemplo: mi BO Solicitud.

public class Solicitud
{
public Solicitud()
{ }

int id;

public int Id
{
get { return id; }
set { id = value; }
}
Solicitante solicitante;

public Solicitante Solicitante
{
get { return solicitante; }
set { solicitante = value; }
}
System.DateTime fecha_soli;

public System.DateTime Fecha_soli
{
get { return fecha_soli; }
set { fecha_soli = value; }
}
string motivo;

public string Motivo
{
get { return motivo; }
set { motivo = value; }
}

int numero;

public int Numero
{
get { return numero; }
set { numero = value; }
}
string origen;

public string Origen
{
get { return origen; }
set { origen = value; }
}
string destino;

public string Destino
{
get { return destino; }
set { destino = value; }
}
System.DateTime desde;

public System.DateTime Desde
{
get { return desde; }
set { desde = value; }
}
System.DateTime hasta;

public System.DateTime Hasta
{
get { return hasta; }
set { hasta = value; }
}
Movilidad ida;

public Movilidad Ida
{
get { return ida; }
set { ida = value; }
}
Movilidad vuelta;

public Movilidad Vuelta
{
get { return vuelta; }
set { vuelta = value; }
}

Boolean aprobada;

public Boolean Aprobada
{
get { return aprobada; }
set { aprobada = value; }
}

}

En este componente también poseo los archivos de mapeo XML que necesita NHibernate (1 por clase BO), ya que recomiendan que estén en la misma .dll que las clases que mapean.

Bueno, hasta aquí, esta breve explicación de mi desarrollo con la arquitectura 3 capas usando .NET y NHibernate; no quise profundizar en el uso de ninguna herramienta, sólo mostrar ejemplos del desarrollo a nivel de arquitectura y componentes de soft.
Quedo a las órdenes de cualquiera que desee aportar comentarios sobre mejoras, experiencias, ó preguntas sobre la forma de trabajo; que quizás no hayan quedado del todo claras en este post.


4 comentarios:

Jorge Infante Osorio dijo...

Hola tocayo, jejeje....muy interesante tu blog, ya lo puse en mi lector de feed pues bien vale la pena leer lo que escribes.

Sobre esta entrada me queda la curiosidad de comparar la forma en que trabajo con la tuya, y que me des tu criterio.
Yo en vez de usar data Mapper uso Active Record, y eso hace que no tenga objetos del negocio como tal si no clases de acceso a datos que encapsulan las responsabilidades de los mappers y de los objetos del negocio.
Lo otro que hago diferente es que separo mi lógica de proceso, en controladores, de mi lógica de negocio, en clases especificas de negocio, donde almaceno las reglas de negocio,validaciones, y todo lo que necesite para responder a las restricciones del negocio.

Como vez tambien mis alumnos me preguntan el porque no hago las cosas directamente y tengo que darles la misma respuesta que les das tú a los tuyos jejeje...

Esta separacion última lo hago pensando en que el proceso de negocio que automatizo me puede cambiar y con solo añadir una nueva clase que me lo implemente ya no tengo que hacer cambios en mi código, algo asi como usar patrones GOF.

Y el uso del Active Record creo que es por simplicidad mas que por ventajas que me reporte, que siempre es un fastidio que capas que no se deban de conocer sus tripas se las vean, ejemplo de negocio y acceso a datos.

Mi correo es jorgeio at uci dot cu, por si quisieras comentarme esto personalmente, que me interesaría pues nunca está de mas tener a alguien de expericiencia a quien poder consultar.

Gracias por la informacion que nos das.

Saludos.

Jorge Ercoli dijo...

Gracias Jorge por tu comentario.
Espero que tu email sea : jorgeio@uci.com, he visto que el dominio es de la Univ.de Cs.Informáticas de Cuba; te estoy escribiendo para comunicarnos porque hay algunas cuestiones que no he interpretado de tu comentario.
Saludos, Jorge Ercoli.

Anónimo dijo...

hola jorge, es muy interesante esta forma de trabajo (arquitectura), jorge quisiera comunicarme contigo para que me aclares algunas dudas que tengo sobre esta arquitectura, mi correo es cefloresjoseph@hotmail.com; soy de peru.

Anónimo dijo...

Hola jorge me parece bacan que te hayas lanzado a compartirnos tus conociimientos de arquitectura logica...yo por lo pronto n utilizo niguna herramineta ORM, pero pienso utilizar .nettiers para eso, recibi muy buenas sugerencias bobre .nettiers, bueno hasta la viat...