code design – Principio Command Query Separation Caso practico

Diagrama Cqs
Diagrama Cqs

Artículos relacionados:

Introducción:

Para descargar el código haz clic acá.

1. Escenario: Mantenimiento de Productos

Para este ejemplo vamos a desarrollar un módulo básico de un mantenimiento de Productos donde se va a aplicar el principio CQS en el diseño del código. Para este ejercicio se necesita una pantalla que muestre un listado de productos y una opción que permita agregar nuevos productos a la lista. Este ejemplo fue desarrollado usando las siguientes tecnologías:

  • C#
  • Framework 4.5.1
  • MVC 5
  • Entity Framework 6.1
  • Sql Server Express
  • VS 2013

En la siguiente imagen podemos ver el diagrama de secuencia del mantenimiento que vamos a desarrollar, desde ya podemos ver que existe una separación a nivel clases en la capa de datos, tenemos una clase especializada para hacer Querys y otra para ejecutar Comandos.

Diagrama de secuencia Cqs
Diagrama de secuencia Cqs

2. Command

2.1 Modelo
public class Producto
{
    public int Id { get; set; }
    public string Nombre { get; set; }
    public string CategoriaNombre { get; set; }
    public string TipoNombre { get; set; }
}
2.2 Command DbContext
public class CommandDbContext : DbContext
{
    public CommandDbContext()
        : base("name=Almacen")
    {
        Database.SetInitializer<CommandDbContext>(
new CreateDatabaseIfNotExists<CommandDbContext>());
    }

    public virtual DbSet<Producto> Productos { get; set; }
}

Para este ejemplo el dominio del Command es sencillo, tenemos a la clase POCO Producto, que se comporta como un DTO, y tenemos la clase CommandDbContext que se encargara de ejecutar los comandos hacia la base de datos.

Modelo Command
Modelo Command

3. Query

3.1 Modelo
public class Producto
{
    public int Id { get; set; }
    public string Nombre { get; set; }
    public string CategoriaNombre { get; set; }
    public string TipoNombre { get; set; }
}
3.2 Query DbContext

A diferencia del CommandDbContext la clase QueryDbContext es interna, ya que no queremos exponerla directamente para evitar que se realicen comandos sobre esta clase.

internal class QueryDbContext : DbContext
{
    public QueryDbContext()
        : base("name=Almacen")
    {
        Database.SetInitializer<QueryDbContext>(
new CreateDatabaseIfNotExists<QueryDbContext>());
    }

    public virtual DbSet<Producto> Productos { get; set; }
}
3.3 Query Database

En este punto entra en juego la clase QueryDatabase que será usada para exponer solo lo que deseamos de la clase QueryDbContext, que en este caso es la ejecución de consultas sobre la base de datos.

public class QueryDatabase : IDisposable
{
    private readonly QueryDbContext context = new QueryDbContext();

    public IQueryable<Producto> Productos
    {
        get
        {
            return context.Productos;
        }
    }

    public void Dispose()
    {
        context.Dispose();
    }
}

El dominio del Query también es sencillo solo se encarga de exponer consultas a través de la clase QueryDatabase.

Modelo Query
Modelo Query

4. UI

4.1 Modelo
public class ProductoInputModel
{
    [Required(AllowEmptyStrings = false, 
              ErrorMessage = "El nombre es obligatorio")]
    public string Nombre { get; set; }
    [Required(AllowEmptyStrings = false, 
              ErrorMessage = "La categoria es obligatoria")]
    public string Categoria { get; set; }
    [Required(AllowEmptyStrings = false, 
              ErrorMessage = "El tipo es obligatorio")]
    public string Tipo { get; set; }
}

public class ProductoViewModel
{
    public IList<Producto> Productos { get; set; }
}
4.2 Servicio
public interface IProductoServicio
{
    void AgregarProducto(ProductoInputModel productoModel);
    ProductoViewModel ObtenerProductos();
}

public class ProductoServicio : IProductoServicio
{
    public void AgregarProducto(ProductoInputModel productoModel)
    {
        using (var db = new CommandDbContext())
        {
            db.Productos.Add(new Command.Producto
            {
                Nombre = productoModel.Nombre,
                CategoriaNombre = productoModel.Categoria,
                TipoNombre = productoModel.Tipo
            });
            db.SaveChanges();
        }
    }

    public ProductoViewModel ObtenerProductos()
    {
        var modelo = new ProductoViewModel();
        using (var db = new QueryDatabase())
        {
            var lista = db.Productos.ToList();
            modelo.Productos = lista;
        }

        return modelo;
    } 
}
4.3 Controladora
    public class ProductoController : Controller
    {
        private readonly IProductoServicio servicio;
        
        public ProductoController(IProductoServicio servicio)
        {
            this.servicio = servicio;
        }

        public ProductoController() :  this (new ProductoServicio())
        {
        }
        
        // GET: Producto
        public ActionResult Index()
        {
            var modelo = servicio.ObtenerProductos();
            return View(modelo);
        }

        public ActionResult Nuevo()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Nuevo(ProductoInputModel modelo)
        {
            if (!ModelState.IsValid) return View(modelo);
            
            servicio.AgregarProducto(modelo);
            return RedirectToAction("index");
        }
        
    }

En la parte de presentación tenemos la clase ProductoServicio, usada por la controladora ProductoController, encargada de coordinar las llamadas a CommandDbContext y QueryDatabase para realizar las acciones necesarias en cada una de ellas.

Modelo UI
Modelo UI

Para descargar el código haz clic acá.

Referencias:
Metal Tip:

Este artículo lo escribí escuchando la canción Tu eres su seguridad de la banda Kraken de Colombia, les comparto el enlace.

Happy coding and Stay Heavy lml

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s