viernes, agosto 02, 2013

Sobre arquitectura en capas – parte 2

Desde hace algún tiempo, cuando un proyecto necesita acceder a una base de datos relacional, lo común es utilizar un ORM. El ORM se encarga de realizar la conversión desde objetos/colecciones en el lenguaje de OOP a registros/tablas en el modelo relacional y viceversa. En otros proyectos se ha optado incluso por usar una base de datos no relacional (NoSQL), donde no es siquiera necesario el mapeo entre clases y tablas. El uso de estas herramientas (ya sea ORM o NoSQL) nos ayuda a trabajar en un nivel más alto de abstracción. Ya no vemos SQL directamente, ahora trabajamos con objetos (entidades) como nuestros datos.

Si seguimos con el ejemplo del artículo anterior, donde se nos pide que un usuario pueda ingresar a un sistema usando su nombre y contraseña. Al usar herramientas como EntityFramework (un ORM) podemos cambiar nuestra capa de acceso a datos.La cual pudiera usar EF para realizar las consultas y actualizaciones. De esta forma no necesitamos escribir SQL y seguimos con nuestra misma arquitectura en 3 capas. Quedando el código algo así:

public User FindByUsername(string username)
{
  var db = new MySampleAppContext();
  return db.Users.SingleOrDefault(user => user.Username == username);
}

Pero nuestro objetivo no es tener 3 capas, ese era solo un medio para un fin. Recordemos para qué tenemos nuestra capa de acceso a datos. La capa de acceso a datos existe para que la capa de reglas de negocios no tenga que ”pensar” en detalles de tablas, registros y SQL. Es la misma razón por la que existen los ORMs, para abstraer el acceso a datos. Entonces… nuestra capa de acceso a datos es una abstracción del ORM que a su vez es una abstracción del acceso a datos, una abstracción redundante. Debemos de limitar nuestras abstracciones por lo que podemos utilizar el ORM directamente en nuestra capa de negocio. De ese modo podemos aprovechar mejor las características de la herramienta y nos ahorramos el trabajo de mantener una capa ya innecesaria. Comparemos el código de la capa de negocios con y sin una capa de acceso a datos.

//con capa de acceso a datos
public bool ValidateUser(string username, string password)
{
    var userRepository =  new Dal.UserRepository();
    var user = userRepository.FindByUsername(username);
    if (user == null) return false;
    var cryptoService = new CryptoService();
    return cryptoService.ComputeHash(password) == username.Password;
}
//Usando ORM en la capa de negocios
public bool ValidateUser(string username, string password)
{
    var db =  new MySampleAppContext();
    var user = db.Users.SingleOrDefault(x => x.Username == username);
    if (user == null) return false;
    var cryptoService = new CryptoService();
    return cryptoService.ComputeHash(password) == username.Password;
}

Podemos ver que el código es muy similar; pero usando EF ya nos ahorramos una capa (un proyecto). Podemos hacer uso de características de EF desde nuestra capa de reglas de negocio (como lazy loading, entre otras) sin tener que tratar de replicarlas en nuestra capa de acceso a datos.


En conclusión podemos decir que ahora, con el uso de herramientas como ORM (o una base de datos no relacional), ya no es necesario crear una capa de acceso a datos porque las herramientas cumplen con el propósito que tenia esa capa. La capa de acceso a datos tenia sentido cuando escribíamos SQL directamente en nuestra aplicación; pero ya no.


Artículo relacionado: Sobre arquitectura en capas – parte 1

10 comentarios:

  1. Cabe mencionar que la capa de datos no se limita exclusivamente a manejadores de base de datos (MS SQL, MySQL, Oracle, y por el estilo) es buena práctica también poner los accesos a datos desde archivos (txt,excel, CSV,INI) , webservices y todo lo que el manejo externo de datos al programa, de esta forma la capa de negocios jamás será alterada si la fuente del mismo dato cambia digamos de un acceso directo a base de datos a un webservice.

    ResponderEliminar
  2. Muy cierto lo que dice "Proteo5", toda entrada de datos deberia ser abstraida :)

    ResponderEliminar
  3. @proteo: yo separo cada uno de esos componentes en su propio asembly, no como capa, sino como componentes colaterales (servicios).

    Con respecto a las capas, estas siguen estando ahi, solo que ahora son implicitas, en muchos casos generadas en tiempo de ejecucion, dependiendo de la libreria que se use.

    ResponderEliminar
  4. Entonces, todo se podría resumir en que "ya no es necesario escribir nuestro propio ORM cada vez que se desarrolla una aplicación porque ya hay muchos disponibles"?

    ResponderEliminar
  5. Black TigerX

    Creo que no hay que confundir Capas con Tiers. No es del todo buena practica poner cada capa en un tier.

    Normalmente en la forma que yo lo hago es si el proyecto solo es de un solo tipo de nodo pues pongo las tres capas en ese paquete.

    Solo si digamos un nodo es web y otro winforms porque asi lo requiere el usuario pues hago un paquete dll para la capa de negocios y la capa de datos y un paquete web para presentacion y el otro paquete winforms solo para presentacion.

    Si la fuente de datos es remota, entonces hago un paquete de servicio para datos.

    Hay muchas variantes, nada es rigido en cuestion de los tiers, siempre va de acuerdo a los problemas que se busca resolver.

    ResponderEliminar
  6. Octavio,

    Creo que si es cierto en parte, para los motores actuales de base de datos (mysql, MS Sql, oracle, y por el estilo) ya no es necesario.

    Sin embargo, una aplicacion se alimenta de datos de maneras adicionales, como comentaba antes, como archivos, webservices, otros dlls (.net, com, Windows API), Mensajes Queue, tcp/ip, webcams (si, las imagenes tambien son datos), lectores de huellas digitales, lectores rfid, por puertos seriales.

    Todos estos son entradas de datos que deben de interactuar unicamente con la capa de datos, salvo en los casos de que hay algunos dispositivos que los datos los mandan por la capa de presentacion como escaners, lectores de banda magnetica, lectores de codigo de barras.

    En otras palabras, lo que el sismtema consuma de datos y no intervenga el usuario va en la capa de datos.

    ResponderEliminar
  7. @proteo5 er... yo no hable de tiers, y por tu comentario ya no me queda claro si conoces la distincion entre capas y tiers, yo solo me referia a que separo cada acceso a *dependencia externa* en su propio assembly, esos no son tiers, en todo caso es una capa de servicios (no hablo de servicios web)

    ResponderEliminar
  8. siguiendo con el otro comentario de proteo5, yo me apego mas al principio de responsabilidad unica, no me gusta mezclar el acceso a LA base de datos con los accesos a otras dependencias externas en un solo assembly, para mi son componentes con responsabilidades totalmente diferentes, y reusables en diferentes contextos. Ahora, me estoy refiriendo meramente a la separacion logica del codigo en assemblies, la 'capa de datos' no tiene que ser un solo assembly.

    ResponderEliminar
  9. BackTigerX

    Bueno, mi error, pensé que si ponías cada assembly en una maquina diferente al considerarlos servicios, por eso lo del comentario de los tiers.

    Entiendo que separas en varios assemblies las capas y sobre todo la capa de datos descomponerlas en más assemblies de acuerdo al acceso que tengan de diferentes datos.

    Lo que yo comentaba es que salvo el caso en donde los datos a consumir no estén en el mismo datacenter (o sea que se necesitan consumir los datos de forma remota) yo dejaba en un solo assembly la capa de datos y la de negocios, ya que para la forma en que diseño las aplicaciones la reusabilidad entre sistemas la hago a nivel capa de negocio.

    Por ejemplo:

    En base de datos 1 tengo la tabla de usuarios.
    En base de datos 2 tengo el estatus de las personas en RH.
    La capa de daos tendrá 2 clase para manipular los datos del usuario (aka CRUD o ABC) una para cada base.
    De ahí tendré una clase de capa de negocio que consuma a las dos clases de la capa de datos para que haga por ejemplo Verificar login. Allí tendrá todo el código necesario para verificar el login haciendo lo que tenga que hacer (existe usuario y está activo empleado), pidiendo los datos que se necesiten y respondiendo si pasa o no pasa.
    Creo el assembly con estas dos capas juntas y será llamado seguridad de la empresa.
    En sistema X cuando necesite hacer login llamo a la capa de negocios que ya tiene programada esta funcionalidad, igual pasaría con una aplicación Y ya que es la parte de negocios la que me interesa reutilizar al tener todo lo habido y por haber relacionado con los usuarios.
    Si bien puedo separar cada capa y cada componente de la capa de datos en su propio assembly, corro el riesgo que algo que ya se programó en una capa de negocios en aplicación X lo reprograme en Aplicación “Y” creando una segunda capa de negocios y sin la política de RH.
    No se si me explico?

    ResponderEliminar
  10. En otras palabras solamente es una forma distinta de solucionar ciertos problemas de reusabilidad, claro esta que no esta exenta de introducir otros problemas.

    Mientras para ti la separacion de responsabilidades la haces a nivel assebly, yo la hago a nivel clases dentro del assembly.

    ResponderEliminar