Estructura de Capas


Controladores para recibir peticiones desde Front.

Residen en un proyecto "Api" añadido en una carpeta de solución "Web".

Su número depende de las unidades de negocio que implementa la API y las acciones constituyen toda la funcionalidad de ésta.

Securizados con decorador "[Authorize]" mediante configuración en WebApiConfig para usar la autenticación por bearer token .

La API originalmente fue creada sin autenticación. Posteriormente se añadió un proyecto MVC nombrado "Identity" a una carpeta de solución "Transversal" con autenticación "Individual account" y Code first, algo muy parecido se explica aquí, suprimiendo vistas y controladores para dejarlo como un proyecto base de OAuth2. El contexto asociado a la Base de Datos, ubicado en un proyecto "Entities" añadido en una carpeta de solución "Models", contiene en su OnModelCreating el mapeo a los modelos asociados con las tablas asociadas a Identity: AspNetRole, AspNetUser y AspNetUserClaim que se añadieron en BBDD en una migración. Por último la Api contiene una OWIN Startup Class Detection nombrada StartUp.cs que llama a la configuración de la autenticación en Identity.
Relacionado con ello y para la persistencia de la configuración del usuario logado se enriquece AspNetUser con una propiedad UiConfiguration generada como una columna más en tabla mediante migración donde persistimos un JSON con el estados de las columnas de varios grids.

Las acciones de los controladores realizan los SaveChanges de UnitOfwork habiendo instalado paquete URF - Unit of Work & (extensible/generic) Repositories Framework . Este paquete usa Entity FrameWork con repositorios genéricos y lo encapsula en una capa extra de servicio donde aglutinar las diferentes modificaciones sobre el contexto utilizado en un solo commit como se puede ver aquí.

Los servicios empleados en las acciones del controlador así como UnitOfWork son inyectados mediante Unity. De esta forma permitimos tests unitarios y de integración al estar desacoplados.

Algunos controladores permiten subidas de archivos.


Servicios que contienen la lógica y cálculos de la aplicación.

Residen en un proyecto "Servicios" añadido en una carpeta de solución "Backend".

Existe uno por cada unidad de negocio pudiendo llamar a varios repositorios o modelos dependiendo de la necesidad.

Gracias a URF - Unit of Work & (extensible/generic) Repositories Framework todos los servicios heredan de un service pattern. Esta clase abstracta es genérica y acepta cualquiera de nuestros repositorios de forma que agrega métodos virtuales de inserción, borrado, etc. que se traducen en acciones sobre el repositorio aceptado.

La dependencia de los repositorios es inyectada con Unity por cada uno de ellos.

Algunos de los servicios son decorados con un HandlerAttribute de forma que son interceptados por Unity. Así, despues de realizar el invoke dejaremos un log sobre una Storage Table en Azure habilitada para ello.

Otros servicios realizan inserciones, modificaciones y borrados de Blobs en Azure en containers habilitados para recibir archivos como imágenes, pdf o excel.

Para queries costosas desde los repositorios se generó una clase estática para ser accedida tanto por los servicios como por los Tests. Ésta contiene tantos métodos como llamadas a los procedimientos almacenados utilizados. Los procedimientos almacenados son generados en Base de Datos mediante Migraciones.


Repositorios

La idea general fue la de generar LINQ queries usando query expressions así como funciones Lambda (ver aquí). Éstas últimas fueron más utilizadas por su simplicidad cuando existían relaciones en BBDD traducidas a colecciones u objetos virtuales. Además, se intentó que los datos se obtuviesen en crudo siendo el servicio quien aplicara la lógica y tradución al DTO correspondiente para navegar transversalmente por las distintas capas.

En las queries cuyo resultado no es gestionado posteriormente se añade la extensión AsNoTracking para no generar un cambio en los modelos lo que evita un coste de tiempos cuando no van a ser usados. Si el coste persiste se utilizan procedimientos almacenados desde los servicios como ya se ha explicado.




Objetos entre capas

Los ViewModels recogidos como parametros en las acciones del controlador son validados y mapeados mediante automapper a DTOs como objeto transversal por las distintas capas.
Los DTOs de respuesta recibidos desde los repositorios son enviados a los Servicios quienes en la mayoría de los casos reenvían al controlador, en otros casos se mapean (tambien con automapper) en nuevos objetos DTOs más similares a la respuesta de controlador. En todos los casos se realiza un último mapeo de estos DTOs a ViewModels, quienes son envueltos en códigos de estado definidos para HTTP como respuestas de las distintas acciones del controlador. Las propiedades de estos ViewModels de respuesta fueron decorados con JsonProperty, una propiedad de Newtonsoft.Json acortando el nombre de éstas cuando recuperaban listas numerosas para optimizar el peso en la respuesta.