lunes, 20 de octubre de 2014

[Patrones] Implementando patrón repositorio - Repository pattern en C# Parte IV

En el artículo anterior observamos cómo crear los repositorios para MongoDB y para Asp.Net Web Api, ahora observaremos como consumir estos repositorios que hemos creado a lo largo de esta serie desde una aplicación, en este caso usaremos una aplicación web Asp.Net MVC, desde la cual consumiremos todos los repositorios. Entonces crearemos un módulo para administrar los empleados que recordemos están en una base de datos Sql Server, incluyendo sus datos de contacto que recordemos están expuestos en un servicios Web Api y un módulo para administrar los artículos escritos por estos empleados que recordemos están almacenados en una base de datos documental en MongoDB. Lo primero que vamos a hacer es crear un sitio Asp.Net MVC en Visual Studio, y una vez lo tengamos vamos a crear los controladores para el formulario de empleados y para el formulario de artículos, y obviamente vamos a crear sus vistas, teniendo una estructura como la siguiente:


Recordemos que nuestros controladores se comunicarán con nuestros repositorios, por lo cual los debemos referenciar a nuestro proyecto Asp.Net MVC, de esta forma vamos a utilizar todo el acceso a las diferentes fuentes de datos (Sql server, MongoDB y Web Api) y nuestra aplicación web no sabrá de donde provienen los datos y por ende estará desacoplada del acceso a datos. Visualmente tendremos un Crud bastante sencillo para los empleados y sus datos de contacto y un crud para los artículos como se aprecia en las siguientes imágenes:



Ahora veamos el código de nuestros controladores y como se comunican con los repositorios:

        private IEmpleadoRepository empleadoRepository;
 
        // GET: Empleado
        public ActionResult Index()
        {
            empleadoRepository = new EmpleadoRepository();
            var empleados = empleadoRepository.ObtenerEmpleados();
            return View(empleados);
        }
 
        // GET: Empleado/Details/5
        public ActionResult Details(string id)
        {
            empleadoRepository = new EmpleadoRepository();
            var empleado = empleadoRepository.ObtenEmpleadoPoId(id.ToString());
 
            var datosRepository = new DatosContactoRepository();
            empleado.DatosContacto = datosRepository.ObtenerDatosContactoEmpleado(id.ToString());
 
            return View(empleado);
        }
 
        // POST: Empleado/Create
        [HttpPost]
        public ActionResult Create(Empleado model)
        {
            try
            {
                empleadoRepository = new EmpleadoRepository();
                model.Id = DateTime.Now.Ticks.ToString();
                empleadoRepository.GuardarEmpleado(model);
 
                return RedirectToAction("Index");
            }
            catch
            {
                return View(model);
            }
        }

Como podemos ver, en nuestro controlador de Empleados, en la acción Index creamos una instancia de EmpleadoRepository y hacemos uso del método ObtenerEmpleados, el cual se encarga de obtener los empleados almacenados en la base de datos Sql Server, recordemos que dicho repositorio implementa una interface y adicional hereda de un repositorio base, lo cual nos brinda la oportunidad de cambiar por otra implementación concreta en caso de que sea necesario.

De esta forma nuestros repositorios nos ayudan a aislar todas las operaciones de acceso a datos, por ejemplo en este caso, obtuvimos datos de Sql Server y adicional datos de un Web Api, y nuestra aplicación Asp.Net MVC no se dio por enterada de dónde salieron los datos. Ahora observemos el controlador para interactuar con los artículos escritos por los empleados que recordemos están en una base de datos No Sql como lo es MongoDB.


        private IArticuloRepository _articuloRepository;
 
        // GET: Articulo
        public ActionResult Index()
        {
            _articuloRepository = new ArticuloRepository();
            var articulos = _articuloRepository.ObtenerArticulos();
            return View(articulos);
        }
 
        // GET: Articulo/Create
        public ActionResult Create()
        {
            return View();
        }
 
        // POST: Articulo/Create
        [HttpPost]
        [ValidateInput(false)]
        public ActionResult Create(Articulo model)
        {
            try
            {
                _articuloRepository = new ArticuloRepository();
                _articuloRepository.GuardarArticulo(model);
 
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

Como podemos observar, de igual forma nuestro controlador de artículos se comunica solo con nuestro repositorio y en ningún momento sabe que los datos provienen o se llevan a una base de datos MongoDB.

Y bueno amigos, con esto damos por terminada esta mini serie sobre el patrón repositorio o repository pattern, espero que sea de utilidad para ustedes.

Artículos anteriores de la serie:

Implementando patrón repositorio - Repository pattern en C# Parte III
Implementando patrón repositorio - Repository pattern en C# Parte II
Implementando patrón repositorio - Repository pattern en C# Parte I

Este ejemplo lo puedes descargar de mi repositorio en GitHub

No olvides visitar mi página en Facebook para mantenerte actualizado de lo que pasa en el Tavo.Net https://www.facebook.com/eltavo.net
Saludos, y buena suerte!

16 comentarios:

  1. Excelente lo tuyo. Mil gracias, andaba buscando algo así. Aunque me lleve 1.000 años voy a tratar de hacerlo.
    Nuevamente, mil gracias a vos y a todos como vos que se toman las molestias para compartir e impartir conocimiento.
    Saludos desde Argentina

    JOA

    ResponderEliminar
    Respuestas
    1. Hola amigo, que bueno que te haya servido esta serie sobre el patrón repositorio, te recomiendo que sigas el paso a paso desde el capitulo 1, y que descargues el código fuente desde mi repositorio en GitHub para que te guíes, cualquier cosa que necesites estaré por acá para ayudarte.

      saludos desde Colombia!

      Eliminar
    2. hola gustavo muy bueno el post, de casulidad tienes alguno un poco mas complejo sobre Wcf o web api en una arquitectura n capas podria ser?

      Eliminar
    3. Hola Johan, la verdad no tengo un ejemplo a un nivel de complejidad como el que mencionas, pero se podría hacer, en este ejemplo podríamos crear una abstracción de los repositorios a través de una capa de servicios con Asp.Net web Api, la cual los exponga, y los clientes como en este caso una aplicación MVC no consumirían directamente los repositorios si no que consumirían las Apis y estás llamarían a los repos.

      Eliminar
    4. Hola Uyy genial sii porfavor :D,
      Gustavo una pregunta sobre este ejemplo, en la clase RepositoryBase en el metodo ObtenerTodos, porque retorna un IQueryable, el IQueryable es diferido osea cuando yo haga un cast a una lista en ese momento lanza la consulta a base de datos, pero esa consulta se estaría ejecutando por fuera del la clase repositorio base por el IQueryable no se si me estoy equivocando. ?

      Eliminar
    5. Hola Johan, es correcto lo que dices, IQueriable hace una carga diferida, ya que solo realiza la consulta cuando se recorre la colección o cuando se convierte a lista, el repositorio base, devuelve la consulta lista para ser ejecutada, pero esta se ejecuta solo cuando sea requerida, de esta forma evitamos mantener colecciones en memoria que no se están usando, y optimizamos el uso de la misma.

      Eliminar
  2. Hola Gustavo, tengo una duda, debo crear un test unitario donde los parametros provienen de una base de datos, que seria mejor ? usar un moq o un patron repository para aislar la recuperacion de parametros de la BD ?

    ResponderEliminar
    Respuestas
    1. Hola amigo, podrías usar el patrón repositorio de igual forma, me imagino que ya tienes el repositorio que implementa la funcionalidad, ahora podrías crear el repositorio para devolver el moc y usar inyección de dependencias para cambiar a través de configuración cual repositorio se debe usar dependiendo si es una prueba unitaria o el repo real.

      Saludos!

      Eliminar
    2. Hola Gustavo,

      Gracias por responder, revisando la solucion encontre que ya existe un proyecto con un repositorio que contiene los parametros que necesito. Pienso que podria invocarlos desde mis clases de tests, esto seria una buena practica ?

      Saludos cordiales?

      Arturo

      Eliminar
    3. Hola amigo, disculpa por mi tardía respuesta, dado que ya tienes el repositorio lo puedes hacer así, sin embargo para una próxima ocasión te sugiero que uses los conceptos de inyección de dependencias y mocks, es una buena práctica en estos casos.

      Saludos!

      Eliminar
  3. Excelente tu aporte, de verdad lo necesitada, tengo una duda, segun tu experiencia de que me sirve usar el patron repositorio con Entity Framework. no se supone que Entity framework es la implementacion del patron repositorio y unit of work pero solo para SQL? (Referencia: http://blog.sapiensworks.com/post/2012/04/15/The-Repository-Pattern-Vs-ORM.aspx/).

    ResponderEliminar
    Respuestas
    1. Hola Sebastian, gracias por tu comentario, EntityFramework al ser un ORM implementa una serie de patrones (como Unit of Work, data mapper, etc) que logran un optimo desempeño y conexión transparente a diferentes motores de base de datos (No solo SqlServer), por otro lado el patrón repositorio, nos ayuda a tener una abstracción del acceso y persistencia a datos en nuestras aplicaciones, en mi opinión no quiere decir que uno vaya en contra de otro, por ejemplo una de las problemáticas que nos ayuda a resolver este patrón es la persistencia poliglota, por ejemplo si en tu aplicación requieres acceso a una BD relacional, y adicional requieres interactuar con una base de datos NoSql, como podrás ver un ORM no se ha hecho para este tipo de base de datos, ya que el ORM esta enfocado netamente en el mapeo de relacional, entonces a través del patrón repositorio logras, tener acceso a estas fuentes de datos, a la relacional con un ORM por ejemplo, y a la NoSql con algún api o librería dependiendo del lenguaje y BD, lo interesante del patrón repositorio es que abstrae este acceso a datos a estas dos fuentes de datos diferentes, de modo que la capa de presentación por ejemplo sea agnóstica al acceso a datos logrando el desacoplamiento de las capas. Ahora piensa en este escenario, tu puedes crear tu modelo conceptual con EntityFramework en tu capa de presentación, en un proyecto Asp.Net MVC por ejemplo, ahora si decides por algún motivo o por exigencia del proyecto cambiar de ORM por algún otro, tu aplicación web se verá afectada y tendrás que cambiarla, apesar de que EF implementa una serie de patrones, ya que hay un fuerte acoplamiento, por el contrario si tienes una correcta implementación del patrón repositorio y cambias tu ORM tu aplicación web no se dará por enterada y no sufrirá cambios, dado que es agnóstica a la fuente de datos y ORM. Recuerda que con este tipo de patrones se busca crear soluciones mantenibles con bajo acoplamiento, no quiere decir que siempre deba ser así, tampoco se debe caer en sobre arquitectura de un proyecto, y como siempre en el desarrollo no hay balas de plata, todo dependerá del escenario

      Espero sea clara mi respuesta, Saludos!

      Eliminar
  4. Amigo como estas saludos desde medellin, me gustaría saber si puedes actualizar todo lo relacionado con la arquitectura de software n-capas, ya que pienso que quizás tu ya hayas cambiado algunos parámetros o métodos a la hora de implementar esta arquitectura y quizás la hayas modificado para un bien. Me gustaría saber si puedes actualizar de tal modo que se vea las buenas practicas y la forma mas profesional posible y code refactor. por favor muchas gracias, digo esto ya que he visto en algunas partes el uso de automapper, autofac, y otras buenas practicas que quizás tu conoces mejor que yo ya que soy un poco nuevo en esto muchas gracias amigo por escuchar mi petición ojala la tomes en cuenta por favor!

    ResponderEliminar
    Respuestas
    1. Hola Camilo gracias por tu comentario, creo que hablamos hace un tiempo por Inbox, espero te haya servido, cómo vas con las nuevas tecnologías que hablamos?

      Eliminar
  5. Hola Gustavo, te felicito por tu serie de entradas sobre el patrón repositorio. Se me vienen a la mente unas duda:
    1)¿colocas las reglas de negocio y validaciones en las mismas entidades del dominio mediante Data Annotations?

    2)Vengo de programar en 3 capas (DA-BL-UI) para winforms, usando entidades pero logicamente incluyendo mis validaciones y reglas de negocio en la capa de negocios, y me gustaria dar el salto a MVC, y poder aplicar algo de patrones, SOLID, DI. Tengo algunos proyectos que pienso reutilizar con MVC, consumiendo mis clases de negocio desde los controladores de MVC, ves esto correcto?

    3) ¿En este ejemplo estás aplicando algunos principios de DDD es así? ¿Aconsejas profundizar en DDD o dedicar tiempo a aprender arquitecturas mas recientes como Clean, Onion..?

    4) ¿Consideras útil desacoplar las vistas del negocio mediante el interfaces?

    Gracias!

    ResponderEliminar
    Respuestas
    1. Hola Matias, gracias por tu comentario.

      1) La verdad depende mucho de la arquitectura y la necesidad, en proyectos medianos y grandes no suelo usar las entidades de EF desde la vista, sobre todo si tengo una capa de servicios de por medio, por el tema de la serialización y referencias circulares de las entidades de EF, para esto suelo usar DTOs. los DataAnnotations suelo ponerlos en los ViewModels o modelos de MVC y las reglas de negocio en el core del proyecto no en la presentación.

      2) Es correcto lo que planteas, que bueno que estés considerando usar patrones y principios como SOLID te traerá muchas ventajas, es correcto que desde el controller de MVC invoques el core de tu proyecto.

      3) La verdad este ejemplo es sencillo y no incorporo una arquitectura DDD como tal, es muy válido que aprendas DDD si ves la necesidad de incorporar este tipo de arquitectura si tienes un dominio muy complejo, es importante que consideres también que onion y clean de igual forma fueron pensados para escenarios complejos, si se emplean en aplicaciones sencillas se podría llegar a una sobre arquitectura innecesaria.

      4) desacoplar es importante, para el tema de la modificabilidad, como en todo no se puede exagerar en abstracciones para no caer en complejidad innecesaria, es importante desacoplar la presentación del negocio no es bueno tener una arquitectura monolítica, pero es bueno que evalúes el nivel de abstracción que requieres, para no matar una mosca con un cañón, todo esto siempre dependerá del escenario en particular.

      Espero mis respuestas te sean de utilidad.

      Eliminar