Book - APIs Con CLean Architecture y .NET Core N3
Book - APIs Con CLean Architecture y .NET Core N3
¡Sin enredos!
Sigue tu desarrollo Paso a paso
haciendo uso de UML,
diagramas de secuencia
Deployment API
Despliegue On Premises
Despliegue OnCloud
PRESENTACIÓN
1. Una revisión a las Arquitecturas de software
2. Código limpio, principios y estilos
3. Patrones de diseño
4. Patrón CQRS: Segregación de responsabilidades
5. La Calidad y el patrón Clean Architecture
6. Creación de la estructura de un proyecto con el patrón Clean Architecture
13. Refactoring
14. Reglas de negocio
15. CRUD API
16. Excepciones
17. Filtros
18. El estándar Open API con Swagger
19. Implementado documentación API
20. Credenciales
21. Token JWT
22. Esquema de Autorización Bearer y creación de usuarios API
23. Aplique el Patrón Unit Of Work (UoW)
24. On Premises
25. On Cloud Azure Devops
Capa Carpeta/Servicios
Proyecto: Api.EscuelaNorte
Presentación
Tipo: .NET Core API
Proyecto: Core.EscuelaNorte
Core/Dominio
Tipo: Class Library .NET Standard.
Proyecto: Infraestructura.EscuelaNorte
Infraestructura
Tipo: Class Library .NET Standard.
Proyecto: Testing.EscuelaNorte
Testing Tipo: Carpeta de soluciones
Carpetas: xUnitTest.EscuelaNorte; IntegracionTest.EscuelaNorte
Vídeos Aplican
01. APIs CON C.A. - Creando la base de datos., 56 ● Intro a Arquitecturas, Principios y
02. APIs CON C.A. - Creando el proyecto, 60 Patrones
03. APIs CON C.A. - El Modelo, 99 ● Inyección de dependencia,
04. APIs CON C.A. - El Repositorio y DI, 99 ● Reglas de Negocio,
05. APIs Con C.A. - Dto Y Mapeo I, 106
● DTO,
06. APIs Con C.A. - Dto Y Mapeo II, 106
● Mapper,
07. APIs CON C.A. - Refactoring DbContext, 109
08. APIs CON C.A. - CRUD en el repositorio, 117 ● Filtros
09. APIs CON C.A. - Capa de negocio I, 119 ● Seguridad JWT,
10 APIs CON C.A. - Excepciones I, 133 ● Docs API con Swagger
11. APIs CON C.A. - Excepciones y Filtros II, 133 ● Deployment On Premises IIS y Cloud
12. APIs CON C.A. - Excepciones y filtros III, 133 Azure
13. APIs CON C.A. – Filtros: Servicio de filtrado, 140
14. APIs CON C.A. - Documentación API I, 158
15. APIs CON C.A. - Documentación API II, 158
16. APIs CON C.A. - Documentación API III, 158
17. APIs CON C.A. – JWT I, 178
18. APIs CON C.A. – JWT II, 178
19. APIs CON C.A. - Usuarios I, 192
20. APIs CON C.A. - Usuarios II, 192
21. APIs CON C.A. - Usuarios III, 192
22. APIs CON C.A. - Usuarios IV, 192
23. APIs CON C.A. - Usuarios V, 192
24. APIs CON C.A. - Repositorio Genérico I, 198
25. APIs CON C.A. - Repositorio Genérico UoW - II, 200
26. APIs CON C.A. - Deploy On Premises, 213
27. APIs CON C.A. - Deploy OnCloud I, 213
28. APIs CON C.A. - Deploy OnCloud II, 213
29 Caso #1: Principio SOLID SRP, 215
30 Caso #2: Principio SOLID OCP, 215
31 Caso #3: Principio SOLID LSP, 215
32 Caso #3: Principio SOLID LSP variante, 215
33 Caso #4: Principio SOLID ISP, 215
34 Caso #5: Principio SOLID IoD, 215
35: Prácticando con los ciclos de vida., 216
CONTENIDO
PRESENTACIÓN 5
ESTRUCTURA GENERAL 7
ÍNDICE DE VÍDEOS TUTORIALES: LABORATORIOS ¡APLICA YA! 8
BENEFICIOS Y LÍNEA CURRICULAR 9
CONTENIDO 10
PARTE I: ARQUITECTURAS, PRINCIPIOS Y PATRONES 18
VÍDEO N° 26 214
26. APIs CON C.A. - Deploy On Premises 214
LABORATORIO ¡APLICA YA! 214
Parte II: OnCloud deployement 214
VÍDEO N° 27, 28 214
27. APIs CON C.A. - Deploy OnCloud I 214
28. APIs CON C.A. - Deploy OnCloud II 214
APÉNDICE A 216
APÉNDICE B 217
Plus
Como parte del aprendizaje es común hacer uso de metáforas. Para tener una buena visual de conceptos como
Arquitecturas, Modelos, Patrones, entre otros, haremos uso a través de los siguientes apartados de la metáfora del
carguero.
En TIC 's equivale a un arreglo de clusters administrado por una aplicación globalizada.
Ejemplo:Kubernetes.
Arquitectura de software
Como observó en la parábola anterior, u
unn arquitecto de software se encarga de encajar cada
uno de los componentes,, módulos, proyectos de una solución, aplicación o sistema de
información respetando siempre las buenas prácticas y estándares y de manera optimizada.
Así, la arquitectura de software se encarga de crear los planos que rigen el diseño del software.
Es por esto que programador no es necesariamente un arquitecto de software.
Arquitecturas deficientes
Existen varias características propias de un mal diseño arquitectónico de software. Dentro de las más significativas
encontraremos:
Arquitecturas eficientes
Ejemplo:
// La interfaz
public interface IRelojMaquina
{
DateTimeOffset Now { get; }
}
//Implementando…
public class RelojMaquina : IRelojMaquina
{
public DateTimeOffset Now { get { return System.DateTimeOffset.Now; } }
}
1
Ver parte II, SOLID… ABSTRACCIÓN
¡No se preocupe!
El objetivo de este curso es dejar claros los conceptos a medida que va practicando.
Todo irá siendo más claro con el paso de las lecciones.
6. Manejo eficiente de los constructores:: Modelar como equipo de desarrollo clases que
se instancian en estados válidos. Eso significa tener una plantilla de programadores con
buenas bases en POO (Programación Orientada a Objetos).
7. DI (Inyección de Dependencias): pasar las dependencias al método constructor como
un parámetro. Esto elimina llamadas estáticas por el uso de la cláusula new. Así, evitamos
el acoplamiento.
8. Contenedores de servicios
servicios: La IoC o Inversión de Control
ontrol permite que grandes
aplicaciones compuestas por varios proyectos no sufran dependencias, gracias a que
cada servicio se declara de modo independiente par paraa cada proyecto. Así cada uno hace
uso de lo que necesita.
9. Respetar el Principio de segregación LS LS: Es dejar de depender de lo que no se utiliza.
No debe implementarse código en, por ejemplo, los controladores API para acceder por
instanciación a la capa de datos o DbContext ni de los repositorios.. Haga uso de la
abstracción que se especializa en su implementación encargada de la tarea para acceder
a los repositorios. En sus controladores se realizará la instanciación pero a los servicios
manejados por los mecanismos de abstracción.
10. IEnumerable: Este tipo de ActionResult es idóneo para ser incluido en el mecanismo de
abstracción. Úselo cada vez que vaya a generar una lista de datos.
Ejemplo:
//Mecanismo de abstracción
public interface IEstudianteRe
IEstudianteRepo
{
IEnumerable<Estudiante>
<Estudiante> ListaEstudiantes( );
}
Nota: Addscope nos indica que se creará una instancia independiente por cada
solicitud. Esto es uno a uno, 1:1
Por otra parte, con Singleton se podrán crear instancias para ser utilizadas en todas las
solicitudes de ese tipo. Esto es uno a muchos 1:M, una instancia para muchas
solicitudes. Esto lo estudiaremos en lecciones posteriores y lo usaremos en los
laboratorios ¡Aplica ya!.
11. Lógica empresarial: NO debe involucrarla en los controladores. Estos deben estar
dedicados a sus propios asuntos (SoC, o separación de preocupaciones). Use las
interfaces e implemente los servicios o aplique otras técnicas de implementación.
12. POO: nos ayuda a crear bases sólidas en la arquitectura de software al diseñar
complementos flexibles además de crear modelos de dominio robustos y estables.
Es ya obligado hacer uso de este paradigma de programación. Utilice a fondo sus cuatro
pilares, a saber:
- Abstracción.
- Encapsulamiento.
- Polimorfismo.
- Herencia.
Principios de diseño
Usted debe tener claro esto:
Las soluciones de software se deben diseñar y crear con el mantenimiento en mente.
Existen otros principios que se dejan como para de la investigación del estudiante
autodidacta.
2
Estilos de arquitectura
Los estilos arquitectónicos para el diseño de software conforman un equipo con características
muy especiales que no requieren de tecnologías concretas. Tenemos:
2
Microsoft, extracto.
Estilo N niveles
Fuente: Microsoft.
Ideal para aplicaciones empresariales. Toda dependencia se administra haciendo uso de capas
como veremos en los laboratorios ¡Aplica ya! con la implementación de Clean Architecture. Es
muy usada en soluciones de infraestructura como IaaS (Infrastructures as a Service) que
ofrece servicios de informática en la nube para procesos esenciales, almacenamiento y redes
entre otros. Observe las capas de servicios Web, mensajería, Data y servicios remotos
independientes. Las capas más externas pueden llamar a las capas debajo de estas.
Web-Cola-Trabajo
Fuente: Microsoft.
Se utiliza para tareas de uso intensivo de la CPU, operaciones de larga duración, solicitudes
HTTP controladas por un front-end web. Ideal para modelos cloud tipo PaaS (Platform as a
Service que proveen hardware, software e infraestructura
Microservicios
Fuente: Microsoft.
Basada en eventos
Fuente: Microsoft.
Ideal para productores que publican eventos-suscripciones y los clientes se suscriben, todos
independientes entre sí. Utilizado para software especializado en el manejo de grandes
volúmenes de datos muy generalizados en soluciones tipo IoT3 de uso masivo.
Big Data
Fuente: Microsoft.
Modelamiento de la arquitectura
Antes de escribir código, es fundamental identificar cada uno de los conceptos expuestos hasta
aquí. Con .Net será posible crear modelos para reconocer comportamientos del software,
identificar patrones repetitivos, analizar cambios, reduciendo la ambigüedad y las
interpretaciones, apoyando al equipo de trabajo para obtener una visual clara del diseño a
emprender.
4
Diseño de alto nivel
Se describen los componentes y las interacciones entre componentes principales del sistema y
el modo en que interactúan entre sí para lograr los objetivos del diseño.
3
Internet de las cosas.
4
Microsoft.
Por ejemplo, como parte de las necesidades del usuario tenemos las reglas de negocio.
negocio
Fuente: Microsoft.
Patrones de diseño
Seguidamente se deben elegir los patrones arquitectónicos.
Estos nos ayudan a explicar en detalle la implementación de los estilos arquitectónicos
Esto implica las tecnologías y elementos arquitectónicos básicos del sistema. Tenemos entre
otros:
1. DDD – Domain Driven Design: El diseño basado en dominios se usa para dominios
complejos donde la arquitectura por capas facilita el trabajo
trabajo.
Arquitecturas Onion
Este tipo de patrón de diseño para implementar la arquitectura de una aplicación o sistema de
información TIC’s, es le utilizado durante el curso.
Finalmente como parte de la arquitectura, se define el modelo de datos relacionados con los
componentes e interfaces. En este punto es ideal el uso de los diagramas de clases que
facilitan la descripción de la información a través de cada componente y su almacenamiento. En
.Net por ejemplo, se hace uso de Entity Framework (EF) con todas sus utilidades para el
modelamiento de acceso y gestión de los datos (Model, SQL Server, IIS, etc.).
En cualquier caso, debe empezar por concebir y codificar las características principales de los
requisitos y el diseño. Ocúpese de los detalles en posteriores iteraciones del proyecto.
Los patrones aunque son ‘plantillas’ no pueden simplemente replicarse o ser usados de manera
indiscriminada. Su utilidad básica es guiarnos en el diseño ideal y óptimo de la lógica, los
algoritmos y los servicios dependiendo del tipo de sistema, políticas de negocio y
requerimientos del usuario. Por ejemplo: un sistema de información financiero requerirá
módulos, arquitectura, algoritmos y rutinas distintas al sistema de información de una fábrica
que produce infinidad de componentes a escala, y además los agrupa acorde a los pedidos de
sus clientes multinacionales.
Existen varios libros muy populares dentro de la comunicad de desarrolladores, dedicados a este
tema. Por ejemplo, uno de los libros de ingeniería de software más populares Design Patterns
de los GoF (Gang of Four) o banda de los 4 ingenieros, define un total de 23 patrones que nos
orientan a aplicar los principios del diseño de software como reutilización de código,
extensibilidad o el cambio y evolución del software. Echemos una mirada y resumen a los
siguientes patrones de diseño y sus categorías:
Creacionales
● Abstract Factory: Familias de objetos de productos.
● Builder: Cómo se crea un objeto compuesto.
● Factory Method: Subclase de un objeto instanciado.
● Prototype: clase de un objeto que es instanciado.
Estructurales
● Adapter: Interfaz de un objeto.
● Bridge: Implementación de un objeto.
● Composite: Estructura y composición de un objeto.
● Decorator: Responsabilidades de un objeto sin subclases.
● Facade: Interfaz de un subsistema.
● Flyweight: Coste de almacenamiento en objetos.
● Proxy: Como un objeto es accedido. Su localización.
Comportamiento
● Chain of Responsibility: Objeto rellenado en una petición.
● Command: Cuándo y cómo una petición es rellenada.
● Interpreter: Gramática e interpretación de un lenguaje.
● Iterator: Como un elemento agregado es accedido, transversalmente.
● Mediator: Cómo y qué objetos interactúan entre sí.
● Memento: Qué información privada se almacena fuera del objeto y cuando.
● Observer: Número de objetos que se relacionan, y cómo mantener actualizadas las
relaciones.
● State: Estado de un objeto.
● Strategy: Un algoritmo.
● Template Method: Pasos para un algoritmo.
● Visitor: Operaciones que pueden ser aplicadas a un objeto sin cambiar su clase.
Builder
Facilita la construcción paso a paso de objetos haciendo uso de Constructores sin necesidad de
invocar los métodos innecesarios para el diseño de los objetos. Así, los constructores realizan la
misma tarea pero de forma distinta. Aquí se establece una clase directora o Base (clase
Abstract en .Net) que organiza la secuencia ordenada de pasos necesarios para el objeto o
servicio solicitado. Todo lo anterior evita el uso excesivo de parámetros en los constructores. Por
supuesto en la medida que existan nuevos parámetros se deberá refactorizar o especializar las
clases derivadas.
Ventajas:
1. Ideal para diseños con procedimientos similares que requieren pocas variaciones.
Desventajas:
1. La exigencia de nuevas clases aumenta la complejidad del modelo
Aplique Builder
Como ejemplo, este patrón lo encontrará como parte de la implementación UoW o repositorio genérico
genéric
más adelante en este curso. Su funcionalidad centraliza en una clase abstracta propiedades genéricas
como se observa en el código siguiente:
namespace Core.EscuelaNorte.Entidades
{
public abstract class EntidadBase
{
public int ID { get
get; set; }
}
}
Prototype, clonación
Los objetos que admiten ser clonados se conocen como prototipos.
La metáfora de la división celular es un ejemplo de este patrón.
Este patrón de diseño facilita copiar o duplicar objetos con código independiente de las clases.
En la realidad intentar clonar objetos impli
implica
ca terminar conociendo sus miembros privados u
ocultos. Este impedimento se supera con el patrón Prototype al declarar una interfaz común que
contendrá un método universal clonar()
clonar().. En el caso de .Net Core, se implementa llamando a la
interfaz IClonable. Esta
sta interfaz facilitará la copia incluso de los campos encapsulados de objetos
de la misma clase en dos clasificaciones de este patrón: clonación superficial y clonación
profunda.
Ventajas:
1. Clonación sin necesidad de crear objetos desde cero.
2. Facilita la creación de objetos complejos.
Desventajas:
1. Se puede presentar referencia cruzada.
Con este patrón creamos una ‘fábrica de servicios’ centralizada. Esto permitirá dos cosas muy
importantes:
1. Refactorización.
2. Evitar el acoplamiento.
Por ejemplo, la cláusula New genera acoplamiento de por vida. Como se observa en el siguiente
código. Por supuesto se entiende que el servicio está fuertemente desacoplado por carencia de
los mecanismos de desacople como veremos más adelante:
Observa ahora el siguiente ejemplo en C#, el lenguaje a utilizar en todos los laboratorios:
Ventajas:
1. Disminuye el acoplamiento.
2. Aplica el principio de responsabilidad única.5
3. Incluir nuevos tipos sin afectar el código existente.
Desventajas:
1. Multitud de subclases.
namespace Core.EscuelaNorte.Interfaces
{
public interface IUoW : IDisposable
{
//RELACIÓN DE REPOSITORIO GENÉRICOS...
IRepoGenerico<TblEstudiante> EstudianteRepoGenerico { get;
; }
}
}
Abstract Factory
Este patrón se caracteriza por generar objetos de categorías similares sin especificación de sus
clases abstractas.
Ventajas:
1. Compatibilidad de objetos.
2. Disminución de acoplamiento de código y dependencias.
5
Los principios SOLID se tratan más adelante.
Desventajas:
El código puede ser más complejo por la gran variedad de interfaces.
Singleton
Facilita una única instanciación de clase con un punto de acceso global donde los clientes
trabajarán con el mismo objeto de modo transparente. Lo anterior implica que se deshabilita la
alternativa de crear objetos de otras maneras… Utiliza el objeto creado o crea uno nuevo,
nuevo es
decir: nada puede
de reemplazar la instancia actual en caché
caché,, por lo que se deben crear
constructores de clase de tipo privado.
En el caso de .Net C#, el uso de la palabra reservada new solo puede ser utilizada una sola vez.
En aplicaciones que hacen uso de la DI (inyecció
(inyecciónn de dependencia) y abstracción, Singleton es
ideal para inicializar el acceso a los repositorios así:
Aplique Singleton
//PARA AUTOMAPEO... DI
private readonly IMapper _mapeo;
public EstudianteController
EstudianteController(
IMapper mapeo,
IEstudianteBLL estudiante)
{
_estuBLL = estudiante;
_mapeo = mapeo;
}
Tome un screen y envíe sus respuestas a vuelta de correo para retroalimentación
¡Pierda cuidado!
Variantes Singleton
Características comunes: Para todas la variante encontraremos los siguientes aspectos comunes:
Variante lazy
Recuerde que la instanciación solo se da por solicitud directa. La primera referencia al miembro
estático ocurre en el método estático Instance, y las clases anidadas tienen acceso a los
miembros privados de la clase primaria. A continuación el algoritmo original de esta variante:
//ALTERNATIVA 1
public sealed class clsSingletonLazy1
{
private clsSingletonLazy1
clsSingletonLazy1()
{
}
}
}
//ALTERNATIVA 2
public sealed class clsSingletonLazy2
{
private static readonly SingletonLazy2<clsSingletonLazy2> oLazy =
new oLazy<clsSingletonLazy2>(() => new clsSingletonLazy2());
private clsSingletonLazy2(){}
}
El patrón a utilizar
Singletón, variante No Lazy será el patrón utilizado en los laboratorios de este curso para el desacople y la
implementación del mecanismo de abstracción
abstracción.
El Aprendizaje Hecho Fácil!
El uso de propiedades y campos deben declararse estáticos. Todo lo anterior permite que el
servicio basado en este patrón se ejecute una sola vez de por vida. Así, el constructor y servicio
de uno de nuestro endpoint se declara
declararía así:
public EstudianteController(IEstudianteRepo
IEstudianteRepo estudianteRepo)
{ _estudianteRepo = estudianteRepo; } <
<- Mecanismos para acceso a encapsulados;
Constructor
//El servicio
Singleton.Instance.MiembrosClase
MiembrosClase() //Implementación tipo No Lazy:
La instancia se crea al ejecutar la aplicación utilícese o no.
clsSingletonProceso()
{
}
{
instance = new clsSingletonProceso();
}
return instance;
}
}
}
}
El anterior algoritmo se ha catalogado como de gran rendimiento cuando se hace uso del
bloqueo (look) debido a que hace uso de un solo bloque y verificación inmediata.
public ConstructorServicio1 (
Interfaz1 dependencia1,
Interfaz2 dependencia2,
Interfaz3 dependencia3)
{
...
}
public ConstructorServicio2 (
Interfaz1 dependencia1,
Interfaz2 dependencia2)
{
...
}
public ConstructorServicio3 (
Interfaz1 dependencia1,
IDependencia4 dependencia4)
{
...
}
Ventajas:
1. Control de las variables globalizadas.
2. Seguridad al tener una sola instancia.
3. Inicialización de objetos solo en el arranque de la solicitud.
Desventajas:
1. Se viola el principio de responsabilidad.
Este patrón es ideal para tablas que tienen varios campos con muchas variantes o alternativas,
similares a los combos de selección. Así, permite refactorizar algoritmos con muchos
condicionales extensos o switches para dichos campos, ubicando la lógica en clases
especializadas pero con una sola interfaz. Analicemos el siguiente caso:
Caso
En la Escuela Norte (laboratorio de este curso) , los estudiantes realizan muchos tipos de
solicitudes como certificado de notas, certificado de asistencia, formas de pago, cambio de
jornada (nocturna, diurna), clases personalizadas o a distancia, entre otras.
case TipoSolicitud.CertificadoAsistencia:
return GestionarCertificadoAsistencia(oSolicitud);
case TipoSolicitud.ClasesPersonalizadas:
return GestionarClasesPersonalizadas(oSolicitud);
case TipoSolicitud.CertificadoNotas:
return GestionaCertificadoNotas(oSolicitud);
.
.
.
n cases
default:
return false;
}
}
Si los tipos de solicitudes continúan creciendo por igual crecerá la cantidad de métodos para
gestionar cada solicitud. Tener una clase con muchos métodos es un indicador para atender la
refactorización de la clase. Ahora apliquemos el patrón Strategy:
VerificarMatriculasJornadas
VerificarMatriculasJornadas(oSolicitud.IDMatricula);
}
}
Recomendaciones generales
1. Para acceder a la misma instancia con un mismo parámetro, se recomienda el uso del patrón Factory
Method.
2. Se sugiere la implementación Lazy para cargar la instancia solo en el momento que se requiera.
3. Dependiendo del tipo de aplicación se pueden omitir con constructores estáticos para incrementar el
rendimiento debido a que el compil
compilador JIT realizará una y solo una verificación.
El Aprendizaje Hecho Fácil!
Otros patrones
Encontrará otro tipo de patrones de acuerdo a su clasificación. Estos se dejan como parte de su investigación. Por
ejemplo:
1. Patrones estructurales:
Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy.
2. Patrones de comportamiento:
Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template
Method, Visitor.
El Aprendizaje Hecho Fácil!
Aplicando UoW
De esa manera, cuando una UoW está completa, puede llamar al método transaccional en esa
instancia del contexto y estar seguro de que todos los cambios relacionados se coordinarán.
Todo lo que la clase necesita es un método especializado para la transacción y una propiedad
para cada repositorio.6
En resumen: todas las transacciones (CRUD) en una unidad UoW se realizan en una sola
transacción haciendo uso de una única instancia del DbContext como se observa en la
siguiente imagen.
6
Microsoft
Segregar responsabilidades en el acceso a los datos implica hacer uso del correcto enfoque para
el manejo de las consultas o queries y los comandos. CQRS facilita la separación de
responsabilidades o preocupaciones tanto en la lectura o consulta como en el registro y
actualización de los datos, maximizando el rendimiento, seguridad y
escalabilidad controlando los posibles conflictos en el dominio de la aplicación.
Es: cada método debe ser un comando que realice una acción, o debe ser una consulta que
recupere datos … pero no ambos. O es un método void o no. Por ejemplo, un método Post
agergaría nuevos estudiantes a la lista (comando). Y si además retorna el total de estudiantes a
la fecha, una vez actualizado el nuevo registro (count), entonces estamos ejecutando tambien
una consulta en el mismo método. Esto es lo contrario de CQSR.
Arquitectura tradicional
Tradicionalmente el diseño de una apl aplicación utiliza el Model o representación esquemática de
la fuente de datos para todas las operaciones relacionadas en un CRUD. El mapeo y los DTO se
pueden tornar complejos en sistemas de información más avanzados y escalables, incluso en las
tareas de validación y lógica de negocio. Lo complejo termina impactando los indicadores de
gestión, eficiencia y eficacia. Así, el enfoque tradicional en sistemas más avanzados impacta el
rendimiento por el incremento en la complcomplejidad
ejidad y carga de datos con las consultas
consu y la capa de
datos, además del control de la seguridad, roles, permisos, tokens, etc.
Por otro lado, el enfoque de los comandos se concentra en las tareas no en los datos y se
procesan asincrónicamente. No es m modificar
odificar o actualizar los datos, es gestionar la tarea base.
Esto facilita aislar los datos de lectura y los datos de escritura. Esto implica la posibilidad de
tener “dos bases de datos” y esquemas: Una tipo documento y otra relacional por supuesto
debidamente
ente sincronizadas. Gran parte de la lógica empresarial formará parte del lado de la
escritura (comandos).
Scaffold
El patrón CQRS no permite el uso de Scaffold 7 para la generación automatizada del modelo.
7
Scaffold: Framework o utilidad para generar código de modo automatizado. Usado especialmente con MVC.
MVC
Separar los datos de escritura y de lectura “… los datos de lectura pueden estar obsoletos. El almacén de modelos
de lectura debe actualizarse para reflejar los cambios en el almacén de modelos de escritura y puede ser difícil
8
detectar cuándo un usuario ha emitido una solicitud basada en datos de lectura obsoletos.”
8
Microsoft.
9
Microsoft
La migración
ión y refactorización de sistemas antiguos debe realizarse de
modo manual para minimizar tiempo y costos.
Verifique su comprensión
1. Factory simply.
2. Strategy.
3. CQRS.
4. Builder.
5. DDD.
6. Singleton
Compruebe su aprendizaje
Veinte años después muchos de ellos han estado incluyendo en su portafolio de herramientas,
más tecnologías necesarias para ser competitivos.
Calidad vs Arquitectura
Debe saber que existen millones de programadores en todos los continentes, escribiendo miles
de líneas de código, o en el campo de batalla atendiendo incidente
incidentes,
s, requerimientos y mejoras.
Desde el preescolar hasta la universidad, niños, adolescentes y adultos profesionales o
aficionados disfrutan desarrollando sus aplicaciones, con o sin estándares, con o sin calidad…. y
¡funcionan!
Arquitectura vs Calidad
10
Martin C. Robert.
Arquitectura vs Calidad
De acuerdo a amplios estudios corporativos, la productividad se hace más baja con cada nueva
versión del mismo producto como se observa en la imagen.
Todo su esfuerzo se ha desviado de las características y ahora se consumen con la gestión del
desorden. Su trabajo, tal como es, ha cambiado a mover el desorden de un lugar a otro,
otro y al
siguiente, y al siguiente, para que puedan agregar una pequeña característica más escasa…” 11
11
Ibid.
La comunicación con estas es limpia gracias a conceptos como la abstracción, los principios
SOLID entre otros el IoC —Inversión de Dependencia y la especialización de tareas en el
sitio que corresponde.
En este curso crearemos proyectos que representen cada una de las capas del modelo C.A.
En resumen tenemos inicialmente las siguientes:
1. Capa de dominio: debe representar todas las clases Core núcleo del negocio que
representan:
a. Las entidades de dominio,
b. Los DTOs,
c. Enumeraciones,
d. Excepciones,
e. Interfaces ,
f. Las reglas de negocio o servicios entre otras, dependiendo de las necesidades de
la organización o proyectos TI.
Beneficios de C.A
De acuerdo a Robert C. Martin encontramos los siguientes beneficios:
Llene los espacios con el concepto correcto. Pueden sobrar términos o frases:
Verifique su comprensión
API, Frontend
CQRS, Validaciones
Paso 1:
Vaya al Panel de control > programas y características. Seleccione > Activar o desactivar la
características de Windows como se observa en la imagen:
Paso 2
Ubique la opción que aparece en la figura y marque todas las listas alusivas al Administrador de
IIS, como observa en la imagen:
12
https://visualstudio.microsoft.com/es/thank
https://visualstudio.microsoft.com/es/thank-you-downloading-visual-studio/?sku=Community&rel=17
studio/?sku=Community&rel=17
13
https://www.microsoft.com/es-es/sql
es/sql-server/sql-server-download
14
https://docs.microsoft.com/en-us/sql/ssms/download
us/sql/ssms/download-sql-server-management-studio-ssms?view=sql
ssms?view=sql-server-
ver15
1. Modelar una base de datos modelo ‘Escuela Norte’ haciendo uso de SQL Server.
Los vídeo tutoriales los encontrará en la lista o sección LABORATORIOS ¡APLICA YA!
VÍDEO N° 1
En la siguiente imagen
visualizamos la estructura de
nuestro proyecto Escuela Norte,
aplicando los conceptos
bosquejados anteriormente.
Podrán existir algunas variantes
dentro de cada capa:
Estructura
En la imagen se ilustra la
estructura inicial de un proyecto
aplicando los conceptos
de Clean Architecture (C.A.)
Proyectos base
Haremos uso de las Bibliotecas de clase Estándar que nos ofrecen compatibilidad para otros
proyectos como .NET Framework.. En resumen, crearemos las siguientes capas relacionadas con
los proyectos .Net Core:
Capa Carpeta/Servicios
Proyecto: Api.EscuelaNorte
Presentación
Tipo: .NET Core API
Proyecto: Core.EscuelaNorte
Core/Dominio
Tipo: Class Library .NET Standard.
Proyecto: Infraestructura.EscuelaNorte
Infraestructura
Tipo: Class Library .NET Standard.
Proyecto: Testing.EscuelaNorte
Testing Tipo: Carpeta de soluciones
Carpetas: xUnitTest.EscuelaNorte; IntegracionTest.EscuelaNorte
Recuerde que para cada uno de los laboratorios, usted tendrá a mano:
El siguiente diagrama muestra la relación que debe configurarse entre los proyectos tipo C.A. El
diagrama nos permite visualizar las referencias entre las capas con Clean Architecture.
Observe que la capa Core no referencia a las capas externas por ser el núcleo de la aplicación.
Las capas más externas deben referenciarse entre ella y apuntan al corazón del sistema, es decir,
la capa Core.
El Core del negocio se encuentra centralizado en la capa más interna e independiente a las
capas interfaces y el modelo entre otros. El contenido de las capas externas se desarrolla en los
siguientes pasos a paso y vídeos tutoriales.
A continuación encontrará, al igual que para cada lección, la lista Paso a paso.
VÍDEO N° 2
ADVERTENCIA
Para las siguientes lecciones debes tener diseñada la base de datos modelo referenciada en lecciones anteriores, o
un diseño de su preferencia.
Abstracción
Este concepto forma parte de uno de los pilares de la Programación Orientada a Objetos (POO), e indica lo
siguiente:
Abstracción: “Una
Una abstracción es un tipo que describe un contrato, pero no proporciona una implementación
completa del contrato. Las abstracciones se suelen implementar como clases abstractas o interfaces,
interfaces e incluyen un
conjunto bien definido de documentac
documentación
ión de referencia que describe la semántica necesaria de los tipos que
15
implementan el contrato.”
Y como lo aprendí en mis años de pregrado: la abstracción es difícil de concebir y diseñar, especialmente cuando
de calidad y significancia se trata,, básicamente porque debemos acertar con los miembros que la conformarán
durante el ciclo de vida de un proyecto
proyecto,, ni más ni menos. Una abastracción con muchos miembros es complicada
de implementar. Pero si tiene pocos será de baja utilidad.
Su diseño es crítico a la hora de escoger el camino de la arquitectura y demás estructuras en un proyecto, porque
es imperioso que la abstracción proporcione una estupenda extensibilidad que brinde gran eficacia, como núcleo
precisamente de muchos patrones arquitectónicos, complementos, IoC, canalizaciones, etc.
Reconoceremos una buena abstracción cuando eliminamos dependencias y facilitamos las pruebas de
frameworks y testing, como es el caso de las pruebas unitarias. Y sobre todo, porque su mantenimiento y
actualización es mínima.
15
Microsoft.
Por ejemplo: Una clase que realiza registro de matriculas, no debería implementar envíos de
correo institucionales. Para atender esto se crearía una clase especializada en realizar mailing o
servicios de mensajería. A esto se le conoce como SoC —Separation Of Concerns.
Deben comportarse como una Unidad Autónoma, atómica … que cambien los demás!
Por ejemplo si nuestra clase ‘Profesor’ tiene implementado un ActionResult con dos
condicionales que determinan si el profesor es de tiempo completo o parcial…. qué
sucedería si se crearán nuevas modalidades de liquidación como tiempo por horas? Así,
la clase Profesor no debe volver a ‘abrirse para rediseña o agregar nuevas funciones si
aplicásemos el principio Open/Closed. Por el contrario, esta funcionalidad debe ponerse
en clases especializadas aparte de los ActionResult y evitar la apertura de la clase
debidamente cerrada (close), como por ejemplo, los métodos o firmas de una API.
Esto es: Si alguna de las subclases —clases hijas, no pueden implementar un método de la clase
Padre (abstracta, base) ….. entonces se viola el principio de sustitución que sería el de la hija! O
de otra manera: La clase base NO debe ‘comprender’ o conocer nada de las clases
hijas/derivadas.
Por ejemplo: La clase padre ‘cursos’ relaciona solo los cursos presenciales en una de sus firmas.
En la clase ‘registro matrícula’ —hija— se registran estudiantes a los cursos presenciales: son
estudiantes presenciales. ¿Qué sucede si aparece un estudiante de tipo corporativo que desea
matricularse pero en modalidad virtual? Esto implicaría modificar la clase base. Usted dirá: Ah!
diseño una nueva firma en la clase derivada. Es posible, pero… esa tarea le ha sido asignada a la
clase base! El método de registro heredado no sería válido para estudiantes que no pueden
asistir de manera virtual. Si se intenta el registro el sistema lanzará un Exception. Las clases hijas
no deben afectar o modificar la clase base.
Por ejemplo para una interfaz tipo CRUD que maneja todos los métodos para realizar gestión
con datos, quedaría por fuera de su diseño un proceso de conexión, envío de datos, tipos de
impresión, etc. Debemos dividir las tareas en distintas interfaces, cada una con su contrato
especializado y además a ser utilizado en el 100%. Se violaría este principio si usted creará en
una interfaz firmas que nunca se utilizarán.
Este principio es fundamental para el diseño con Clean Architecture y lo verá aplicado en el
laboratorio de casi todas las lecciones del curso. Indica que las clases de niveles internos (Core)
en arquitecturas bien definidas no deben depender de las clases de niveles externos del
modelo —Infraestructura, Presentación, etc. Ambas deben aplicar el principio de POO:
la abstracción…. Gracias a la Inversión de Control (IoC). Ampliaremos más el principio anterior,
comprendiendo la Inyección de Dependencia (DI) en los laboratorio SOLID y apartados a
continuación.
Ahora desarrollemos una serie de laboratorios prácticos con una aplicación de consola. Net, C#,
un par de capas básicas de la arquitectura Onion y los cinco casos prácticos con sus respectivas
soluciones, aplicando los principios SOLID.
La siguiente serie de laboratorios desarrolla la lógica suficiente para poder aplicar una solución
a cada caso, haciendo uso de los principio SOLID. Se implementará código C# en una aplicación
tipo consola sin implementación de EF.
Caso de estudio
Requerimiento N° 1: El cliente solicita un módulo adicional para su sistema de información “Escuela Norte” que
permita validar el proceso de las matriculas así:
1. El usuario autorizado para la gestión y validación de la matricula podrá ingresar por consola el código
del estudiante.
2. El backend de la aplicación debe atender los siguientes procesos:
a. Verificar que efectivamente el estudiante existe en la base de datos.
b. Una vez verificado el estudiante, se debe validar que existe el registro del pago de su
matricula semestral. (será simulado).
c. Confirmado el pago por el departamento contable, se procede a activar y asentar la matrícula
16
con todos los datos propios que formarán parte del Invoice.
d. Finalmente el servidor de comunicación deberá generar un correo electrónico adjuntando el
Invoice al correo del estudiante.
3. En cada proceso se debe informar al usuario autorizado del éxito de la transacción.
Código
/// <summary>
/// CASO: SOLID #1: SINGLE RESPONSIBILITY PRINCIPLE
/// </summary>
public class CasoSRP
{
public async Task<bool
bool> ValidarEstudiante(Estudiante oEStudiante)
{
Console.WriteLine(
Console.WriteLine("\n");
Console.WriteLine(
Console.WriteLine("PROCESANDO...Validando estudiante\n");
);
return true;
}
16
Estándar de un documento
o o compromiso entre las partes (factura, recibo de caja, etc.)
Aplique ahora el principio SOLID SRP como solución al caso anterior, en el vídeo Caso#1.
Una vez realizada la refactorización y aplicación del principio SOLID SRP, el resultado final será
el siguiente: (Capa Core)
Código
Código
public SolucionSRP(IGestionMatricula
(IGestionMatricula gestionMatricula) <- Constructor
{
_GestionMatricula
GestionMatricula = gestionMatricula;
}
NOTA:
Aunque se sigue la lógica similar a la implementación de servicios que usaremos en el diseño
API del curso, usted podrá crear un servicio independiente por cada proceso y una clase
solución. (ValidarEstudiante, ValidarPago, etc.)
En el código se ha hecho uso de una técnica que podrá profundizar en el siguiente apartado y
practicar ámpliamente. Se le conoce como DI o Inyección de dependencia. Aprenderá además
sus variantes.
Es el contrato entre clases que obliga a cualquier clase del modelo a implementar precisamente TODO el contrato
17
declarado en dicha interfaz y así crear el mecanísmo de Abstracción: los detalles van en los niveles inferiores de
la arquitectura, para nuestro caso de tipo Onion
Onion.
Recuerde que la capa Presentación —para nuestro caso es el proyecto SOLID de tipo Console —
contiene el archivo > ProgramSolutionSRP.cs
ProgramSolutionSRP.cs, y el Void Main— con el punto to de inicio de la
aplicación y que requiere acceso
cceso o instanciación a la clase original refactorizada,, haciendo uso
de la línea: CasoSRP casoSRP = new CasoSRP(). Ha sido ajusta por la creación de un objeto de tipo
Interfaz que facilita el uso del
el servicio especializado para la clase principal, como se observa en
el siguiente código. A la técnica anterior se le conoce como patrón Singleton y lo verá más
adelante:
17
Validaciones, reglas y lógica de negocio, acceso a datos, Queries, etc.
Código
namespace SOLID.Principio_1.SRP
{
public class ProgramSolutionSRP
{
static async Task Main(
Main(string[] arg)
{
. . .
Estamos creando un objeto gestionMatricula
gestionMatricula, de tipo interfaz IGestionMatricula y por supuesto
un constructor para acceder a los servicios implementados .
De acuerdo a la definición de este principio una clase debe poder extender el comportamiento
de otra clase sin modificar la original
original. Para este caso, el requerimiento es el siguiente:
siguiente
Caso de estudio
El cliente requiere un informe de las matriculas procesadas para cada día en formato tipo tabla electrónica y
Word.
El reto se presenta cuando el cliente interno o externo comience a requerir reportes en distintos formatos (Word,
PDF, JSON, XML, TXT, CVS, etc.). Esto implica abrir reiteradamente la clase para agregar nuevos métodos y
condicionales que determinen el reporte a procesar.
Lo anterior va en contra del principio SOLID OCP. Una vez diseñada una clase con base en la abstracción,
abstracción está no
debería sufrir
ufrir modificaciones o ‘abrirse (open). Desarrollemos el código inicial SIN aplicar el principio OCP, como
se observa en el siguiente bloque:
El Aprendizaje Hecho Fácil!
Código
else if(ID == 1)
{ InformeWord(); }
return oMatricula;
}
Nos encontramos con dos procesos dentro de la clase CasoOCP.. E igual funciona!. Pero
recuerde: desarrollamos e implementamos pensando en el mantenimiento y extensibilidad.
Por otra parte, observe el incremento de líneas por los if condicionales y swith.. case.
case Lo anterior
no coincide con las Buenas Prácticas y calidad de software. Respecto al estilo de codificación,
codifi en
el apartado relacionado con los patrones existen algunos casos para refactorizar este tipo de
casos comunes de codificación.
Código
1. Aplicamos primero el principio N°1 SRP (ver caso anterior).
2. Seguidamente haremos uso del
el principio N°2 OCP. Crearemos una interfaz Genérica para la que
el formato
ato del informe es transparente. Esto es: Las capas internas deben desconocer los
detalles!
3. Y finalmente, ajustaremos
justaremos la capa Presentación (UI).
public SolucionOCP(IGenerarInformes
(IGenerarInformes generarInformes) <- Constructor
{
_generarInformes = generarInformes;
}
}
}
El Aprendizaje Hecho Fácil!
Código
Código
await oExcel.NuevoInforme();
NOTA:
Para efectos de simplificación no se ha incluído, por ejemplo, un parámetro con el objeto
complejo ‘matricula’ que traería el registro del conci
conciliado
liado de las matrículas del día de la
base de datos.
18
Programación Orientada a Objetos.
B). Una vez cumplido el ciclo de vida del objeto A, no tendremos en la escena (memoria
principal) un objeto B aún con vida, deambulando por ahí.
Agregación: una clase hace uso de otra para cumplir ciertas tareas pero su existencia no
depende de esta. El objeto A que contiene la referencia al objeto B, solicita la
agregación. El objeto B NO se destruirá si el objeto A finaliza su ciclo de vida. E
igualmente en sentido inverso.
Delegación: transferir la responsabilidad o tarea a otra clase. El objeto A delega la tarea
al objeto B, y ambos son independientes en su ciclo de vida.
Este mecanísmo es usado cuando el objeto A no cuenta con recursos para realizar cierta
tarea obligatorias y que el objeto B sí puede.
Es precisamente este mecanísmo el que facilita el principio de sustitución para nuestro
caso de estudio.
Revisemos los requerimientos para este caso donde aplicaremos el principio LSP:
Caso de estudio
Código
//IMPLEMENTACIÓN
public class ModalidadDistancia : ModalidadEstudio <- heredando…
{
public async Task ValidarDistancia(
ValidarDistancia(int id, string Nombre)
{
IDModalidad = id;
Modalidad = Nombre;
}
Aunque en este nivel usted debe conocer este concepto resumamos como plus adicional lo siguiente:
1. Las clases abstractas no permiten instanciación y son usadas como Base en la jerarquía de clases y
herencia. Puede definir métodos, a diferencia de las interfaces que solo pueden declarar las firmas a
implementar. Además podremos utilizar los modificadores de acceso (public, private
private,, etc.)
etc.
2. Las clases abstratas
ratas son ideales para:
a. La generación de objetos con un gran propósito común,
b. Grandes unidades funcionales a implementar por igual en todos los componentes derivados,
c. Generar múltiples versiones del componente y funcionalidad cambiante.
19
3. Son ideales para implementar Hooks
Para resolver lo anterior podríamos tomar varias vías. En nuestro caso haremos uso de una
interfaz y conservaremos la clase abstracta así:
19
Patrón HTTP ligero dedicados a la detección de eventos y envíos de Post
Código
Debido a que no todas las clases derivadas pueden ser sustituidas, se traslada el
método que calcula el descuento a una interfaz así:
//INTERFAZ
public interface IModalidad
{
public string DescuentoModalidad(); <- Viene de la clase base (abstracta)
}
//IMPLEMENTACIÓN
public class ModalidadDistancia : BaseModalidad, Imodalidad <- Puede heredar de varias clases
{
public async Task ValidarDistancia(
ValidarDistancia(int id, string Nombre)
{
IDModalidad = id;
Modalidad = Nombre;
Console.WriteLine(
Console.WriteLine("EN PROCESO... Verificando bono\n");
);
}
Observe que podremos heredar tanto de la clase base abstracta como de la interfaz.
Ahora la implementación cumple el principio porque la clase base puede sustituirse por sus
clases derivadas que logran el 100% del contrato (para el ejemplo corresponde a los miembros
privados y el método abstrácto
abstrácto), e independientes del método para calcular el descuento que es
ahora parte de la interfaz.
Por supuesto, usted podrá crear una y solo una clase especializada por cada modalidad con su
respectiva abstracción como segunda variante a la implementación del principio N°3 así:
Código
// Ahora implementemos esta variant
variante,
, creando una interfaz por cada tipo de
modalidad y su respectivo servicio: Caso aplicando la solución con LSP.
LSP
//SERVICIO.
public class ModalidadDistancia :BaseModalidad, IModalidadDistancia
{
public async Task ValidarDistancia(int id, string Nombre)
{
IDModalidad = id;
Modalidad = Nombre;
Console.WriteLine("\n");
Console.WriteLine("EN PROCESO... Verificando bono\n");
}
Observe que una clase puede implementar una o más interfaces. En nuestro caso se implementan
los miembros de la clase abstracta y la respectiva interfaz. Además se hace uso de las firmas
completas. Notará que para la UI solo se requiere modificar el using por using
Core.SOLID.Servicios.
Queda como ejercicio para el estudiante aplicar el principio LSP para las modalidades
‘Semipresencial’ y ‘Presencial’.
El principio de Interface Segregation es muy parecido al primer principio SOLID. Cada quien con
su tarea. Es mejor una interfaz que muchas interfaces cuando de procesos similares se trata. O
en otro sentido: una interfaz debe especializarse en su labor. Como se da cuenta en los
anteriores ejercicios, romper una principio SOLID impacta otros más.
Entonces una clase no está obligada a declarar métodos o funciones que no requiere o necesita,
y que forman parte del contrato de la interfaz a usar. Si sobran firmas entonces se está violando
el principio N° 4 ISP, y deben crearse las interfaces especializadas en dicha labor para trasladar
las firmas respectivas o sobrantes. Así, crear firmas en una interfaz para las clases que la
implementan y nunca las van a utilizar, es una violación al principio ISP, ¡Se cumple el 100% del
contrato! Este caso es muy parecido al caso anterior de la clase abstracta. Igualmente aplica
para las interfaces: el contrato debe implementarse en su totalidad. Recuerde que las interfaces
definen el comportamiento pero no la implementación.
Ahora revisemos los requerimientos para este caso donde aplicaremos el principio ISP:
Caso de estudio
El anterior requerimiento hace muy tentadora la alternativa de crear un servicio global que
proporcione las tres tareas. El caso de estudio sin aplicar el principio N°4 ISP quedaría así:
Código
//INTERFACE BASE
Task GestionPrestamos(
GestionPrestamos(int id, string ISBN,
DateTime FechaIni, DateTime FechaFin);
Esta es una interface típica que va en contra del principio ISP.. Al igual que casos anteriores, su
especialización es difusa. Hace de todo un poco. Y usted ya comprende que esto afectará el
testing y mantenimiento futuro. Para su implementación tendremos:
Código
Y uno piensa: Genial! Todos los servicios centralizados. Pero ¿cómo podría por ejemplo
aplicarse una prueba xUnit a esta clase? Es inviable! Además estamos violando el principio SRP.
Como solución apliquemos el principio N° 4, Interfaces Segregation Principles así:
Código
//IMPLEMENTACIÓN DE INTERFACES
S SEGREGADAS: SERVICIO DE SALONES
namespace Core.SOLID.Interfaces
{
public interface IServicioSalones
{
Task GestionSalones(int
int id, string salon,
DateTime FechaReserva);
}
}
namespace Core.SOLID.Interfaces
{
namespace Core.SOLID.Interfaces
{
public interface IServiciosTutorias
{
Task Tutorias(int id, int CodTutor, DateTime FechaTutoria);
}
}
Estupendo diseño! Hemos aplicado el principio de segregación para especializar las interfaces
en cada una de sus responsabilidades. Ahora el usuario podrá hacer uso a través de la UI así:
Código
await oServicioSalones.GestionSalones
(IDEstu, "Salón A",
, DateTime.Today);
Console.WriteLine($"Salón
$"Salón reservado para el estudiante: \n " +
$"Carnet: {RegEstudiante.IDEstudiante}
{RegEstudiante.IDEstudiante}\n " +
$"Nombre: {RegEstudiante.NombreEstudiante}
{RegEstudiante.NombreEstudiante}\n");
¿Notó algo?. La refactorización de la UI fue mínima. Solo se modificó nuestra línea de código
que referencia el objeto oServicioSalones Y eso es todo lo necesario para aplicar este principio.
Este principio nos ayuda a evitar que las clases internas o Core de la app, se vean afectadas por
nuevas implementaciones en las capas externas de la arquitectura. Esto equivale a una Alta
abstracción y baja cohesión o acople
acople. ¿Y entonces, cuál es el puente entre ambas capas?
Precisamente el mecanismo de abstracción
abstracción, haciendo uso de las interfaces y clases abstractas.
El chef y la cocina
Los componentes que conforman las capas exteriores y su carpintería, deben estar diseñados para encajar a
perfección en los mecanísmos de abstracción.
Un CHEF Corporativo Internacional se encarga de diseñar, establecer y programar las compras, el menú y la
innovación gastronómica. Son sus auxiliares de cocina los que se entenderán con los ingredientes, la sazón, la
limpieza y la preparación. Son los auxiliares quienes reciben las instrucciones, condic
condiciones
iones y estándares.
Sería un desperdicio que un CHEF internacional tenga que cocinar unos huevos o sacarle brillo a la estufa para
que las cosas de su organización marchen bien.
La carpintería
tería en el diseño de aplicaciones corresponde a las capas externas
externas.. Y estás entenderán y se comunicarán
con el Core a través del mecanísmo de abstracción.
Entonces ¿quién suministrará la dependencia? Como se estudio en el caso del principio SRP,
hacemos uso de técnicas como la Inyección de Dependencia (DI) que es una de las más
populares para poner a punto la IoC y así facilitar la comunicación entre el Core y las capas
externas dedicadas a la implementación y detalles.
Como observación final, aplicar TDD (Test Driven Development) no es posible si no existe IoD. Y
es precisamente este principio la base del patrón Factory.
Caso de estudio
La administración académica requiere un módulo que registre la asistencia a clase con la lectura
biométrica del estudiante.
Código
Observe la instanciación de una clase que gestiona el registro del ingreso a la clase
RegistroIngresos. Esto ocasiona fuerte acomplamiento de class ProgramCasoIoC. Previamente
hemos desarrollado la entidad ControlIngresos con propiedades para simular el almacenamiento
de datos como se observa en el siguiente código:
Código
namespace Core.SOLID.Entidades
{
public class ControlIngresos
{
public int IDIngreso { get; set; }
public int IDEstudiante { get; set; }
public DateTime FechaReg { get; set; }
}
}
Y por otra parte, en la capa > Infraestructura creamos la simulación de un patrón tipo Repository
RegistroIngresos haciendo uso del método void RegistrarIngreso
Código
using Core.SOLID.Entidades;
namespace Infraestructura.Repositorio
{
public class RegistroIngresos
{
public void RegistrarIngreso(
RegistrarIngreso(int idEstu,
DateTime FechaReg)
{
var RegIngresos = new ControlIngresos()
{
IDIngreso = 1,
IDEstudiante = idEstu,
FechaReg = FechaReg
};
}
}
}
Usted aprenderá a implementar este patrón y el genérico UoW en laboratorios más adelantados
del curso. Por el momento vamos a aplicar como solución el principio SOLID N°5 Inversión de
Control.. ¿Qué código cree que debe reemplazar la línea RegistroIngresos oRegistroIngresos = new
RegistroIngresos(); ? Observe el siguiente proceso de refactorización del caso:
Código
namespace Core.SOLID.Interfaces
{
public interface IServicioRegistroBio
{
void RegistrosBIO(int idEstu,
DateTime FechaReg);
}
}
El Aprendizaje Hecho Fácil!
¡Exacto! Usted lo previó. Ya comprende entonces el concepto de abstracción haciendo uso del
mecanismo de programación como se observa en este caso con la interfaz:
interface IServicioRegistroBio
Ahora implementemos la interfaz anterior creando el servicio:
Código
namespace Core.SOLID.Servicios
{
public class ServicioRegistroBio : IServicioRegistroBio
{
Así, podremos acceder al servicio aplicando el principio IoD por el uso del mecanísmo ID
Código
oBIO.RegistrosBIO(IDEstu,FechaIngreso
IDEstu,FechaIngreso);
Finalmente hemos logrado desacoplar el servicio para el registro de los datos cuando un
estudiante asiste a clase.
Continúe ahora con la serie de laboratorios ¡Aplica ya! practicando son SOLID.
Todos los vídeos de las prácticas con cada principio los encontrará relacionados
en el apendice A al final del curso.
El Aprendizaje Hecho Fácil!
La mayoría de las aplicaciones están escritas de tal manera que la dependencia en tiempo de
compilación fluye en la dirección de la implementación de detalles durante la ejecución. Es decir,
si la clase Z llama a un método de clase Y y la clase Y llama a un método de clase X, entonces,
en el tiempo de compilación, la clase Z dependerá de la clase Y y X y la clase Y dependerá de la
clase X. Esto equivale a un fuerte acoplamiento y dependencia en todo el modelo.
Aplicando en tonces el principio SOLID #5, observe el siguiente gráfico que facilita la
abstracción para el modelado de la lógica de una aplicación:
El principio IoC es una parte clave, diríamos crítica, para la construcción de aplicaciones poco
acopladas, ya que los detalles de implementación se pueden escribir para depender e
implementar abstracciones de nivel superior, en lugar de lo contrario. Como resultado, las
aplicaciones resultantes son más testeables, modulares y fáciles de mantener. Y este principio y
técnica lo aplicaremos en los laboratorios ¡Aplica ya! del curso.
DI en pocas palabras
Inyección de Dependencia es la técnica que nos permite acceder a servicios configurados en una ubicación
central.
Para el caso de Blazor y sus componentes, una vez que se han agregado los serv servicios
icios a la colección de servicios —
contenedor— se insertan en los componentes mediante la directiva @inject de Razor. Esto lo puede practicar en
el desarrollo de los laboratorio de este curso y del siguiente curso,, nivel avanzado II durante el desarrollo de una
Tienda Online con Blazor.
El Aprendizaje Hecho Fácil!
Técnicas para DI
Aquí un resumen de las distintas técnicas para aplicar Inyección de Dependencia.
Inserción de constructores
Los controladores de nuestra aplicación solicitan las dependencias de forma explícita a través de
constructores. ASP.NET Core tiene compatibilidad integrada con la inserción de dependencias.
Los servicios se agregan como un parámetro de constructor y el runtime
ntime resuelve el servicio
desde el contenedor de servicios. Normalmente, los servicios se definen mediante interfaces.
Esta técnica la usaremos ámpliamente en los vídeo tutoriales.
Código
[HttpGet]
public List<Estudiantes> GetProduct
GetProduct([FromServices] IEstudiantesService EstudianteService)
{
return EstudianteService.
EstudianteService.Value;
}
El endpoint inicial
Para efectos de simplificar y lograr avanzar hasta llegar al modelo completo aplicando todos los conceptos del
curso, haremos uso del endpoint Estudiante y Usuario. El diseño de los demás tienen la mísma lógica y servirá
para que el estudiante practique lo aprendido.
El Aprendizaje Hecho Fácil!
20
Un repositorio está destinado a crear una capa de abstracción entre la capa de acceso a datos y la capa
de lógica empresarial de una aplicación. La implementación de estos patrones puede ayudar a aislar la
aplicación de los cambios en el almacén de datos y puede facilitar las pruebas unitarias automatizadas o
el desarrollo basado en pruebas (TDD) —Microsoft.
21
Combinación de patrones Unit Of Work y Repository. Corresponde a la clase especializada para interactuar
con los datos implementando un objeto único de tipo context.
Código
CASO 1:
[HttpGet]
public IActionResult GetListEstu()
{
var ObjEstudiante = new EstuRepositorio.GetEstuAll(); <- Acoplado
return Ok(ObjEstudiante);
(ObjEstudiante);
}
CASO 2:
[HttpGet]
public async Task<IActionResult> GetAsynEstuAll()
{
Explicación
En un escenario haciendo uso de un patrón como Repository,, en la capa API NO debería existir
una implementación como en el ejemplo anterior. El tipo de instanciación anterior
implica acoplamiento, tanto en el primer caso con la cláusula new por la dependencia con la
clase EstuRepositorio,, como en el segundo caso por la dependencia directa con la
clase DbContext instanciada a través de la variable
variable-objeto _context. Algo que debe ser
transparente en la codificación.
Ambos casos implican que para futuros cambios o migraciones, el desacoplamiento es muy alto
debido a que la clase (tipo repositorio
repositorio) que contiene el método GetEstuAll( ) está dependiendo
de una implementación
mplementación concreta y no una abstracta —de nivel superior,, que para este caso
depende de la implementación del repositorio
repositorio-clase EstuRespositorio,, ocasionando
ocasionan alto
acoplamiento y baja adhesión..
Tendríamos que crear un nuevo repositorio especializado en la gestión de objetos para el nuevo
proveedor de datos que es lo correcto. Eso significa que… tendremos que modificar TODO
nuestro proyecto para ajustar cada new EstuRespositorio! y realizar los ajustes uno a uno. El
Así que en el siguiente vídeo ¡Aplica ya! haremos uso de la abstracción e implementaremos
código para aplicar el principio SOLID N°5, creando un Contenedor de e Servicios DI como el
que aparece a continuación donde ¡¡la abstracción toma el control!
Código
public EstudianteController
EstudianteController( IEstudianteRepo estudianteRepo ) <- Constructor, DI
{ _estuRepo = estudianteRepo; }
[HttpGet]
public async Task<IActionResult> GetAsynEstuAll()
{
var ObjEstuAll = await _estuRepo.GetEstudiantes(); <- No existe acoplamiento.
Existe abstracción (Interfaz)
return Ok(ObjEstuAll);
(ObjEstuAll);
.
.
//EL CONTENEDOR DE SERVICIO DI
public void ConfigureServices
ConfigureServices(IServicesCollection services)
{
services.AddControllers();
();
services.AddTransient<IGetEstuRepositorio
IGetEstuRepositorio, GetEstuRepositorio>();
}
Explicación
En el contenedor de servicios ConfigureServices(IServicesCollection services) hemos inyectado
dependencia a través del servicio de este que referencia el repositorio particular
GetEstuRepositorio (todo lo trataremos pasos a paso en los vídeos), para nuestro caso haciendo
uso de la fuente de datos SQL Server para los datos. ¿Y si se necesitase implementar un
proveedor de datos como Oracle? Crearíamos todo el repositorio para Oracle aparte,
independiente y sencillamente referenciaríamos dicho repositorio cambiando el nombre en el
contenedor por otro como GetEstuRepoOracle
GetEstuRepoOracle…. ¡Esto es genial,
enial, un solo cambio! Gracias a
la abstracción representada en la Interface IGetEstuRepositorio. Ahora tome en cuenta los
siguientes conceptos resumidos:
Inyección de dependencia
1. Dependencia: Un Objeto depende de otro Objeto. Si una clase se modifica -> > todas las que dependan de
ésta por igual. El código de configuración se dispersa por todo el proyecto de software. Las pruebas
Unitarias no funcionan bien en este escenario.
Inyección: hace referencia
cia a ‘inyectar’ o aplicar el servicio en el constructor de la clase donde se usa. El
marco asume la responsabilidad de crear una instancia de la dependencia y deshacerse de ella cuando
ya no sea necesaria.22
Instanciación: Se crea por el servicio suministr
suministrado
ado en el contenedor. ¡Las instancias no suceden en la
clase!
2. Interfaz: Se usa el tipo concreto de la clase. Se utiliza la interfaz que lo implementa. Esta corresponde a
la abstracción. Lo anterior facilita cambiar la implementación que usa el controlador sin modificar el
controlador como lo veremos en el vídeo de la lección.
Plus
23
Spring.NET - Application Framework (springframework.net)
24
Ninject - Open source dependency injector for .NET
Transitorio
Los servicios de duración transitoria se crean cada vez que el contenedor del servicio los solicita.
Esta duración funciona mejor para servi
servicios
cios sin estado ligeros. Registre los servicios transitorios
con AddTransient. En las aplicaciones que procesan solicitudes, los servicios transitorios se
eliminan al final de la solicitud. “Always different”
Con ámbito
En el caso de las aplicaciones web, una duración con ámbito indica que los servicios se crean
una vez por solicitud de cliente (conexión). Registre los servicios con ámbito con AddScoped.
En las aplicaciones que procesan solicitudes, los servicios con ámbito se eliminan al final de la
solicitud. “Changes
Changes only with scope
scope”
Cada solicitud siguiente de la implementación del servicio desde el contenedor de inserción de dependencias
utiliza la misma instancia. Si la aplicación requiere un comportamie
comportamiento
nto de singleton, permita que el contenedor de
servicios administre la duración del servicio. No implemente el modelo de diseño singleton y proporcione el código
para desechar el singleton. Si un tipo o fábrica se registra como singleton, el contenedor eli
elimina
mina el singleton de
manera automática. Registre los servicios singleton con AddSingleton.
Los servicios singleton deben ser seguros para los subprocesos y se suelen usar en servicios sin estado. En las
aplicaciones que procesan solicitudes, los servicios singleton se eliminan cuando ServiceProvider se elimina al
cerrarse la aplicación. Como no se libera memoria hasta que se apaga la aplicación, se debe tener en cuenta el
uso de memoria con un servicio singleton.
“Always the same”
El Aprendizaje Hecho Fácil!
25
Microsoft
El procesador realiza una serie de tareas para que una instrucción se ejecute, y adicionalmente
podrá hacer otras simultáneamente sin tener que esperar la finalización de estas. Es realizar
tareas, iniciando algunas, dejandolas en desarrollo pero bajo vig
vigilancia
ilancia mientras se concluyen
otras. Todo se maneja a través de hilos. En programación en paralelo, se necesitarán varios hilos,
y unos bloquearán a otros hasta que no se completa la tarea de estos.
La relación de los vídeos de este tema los encontrará en el apéndice B al final del libro.
Material adjunto
Cadena de conexión
"ConnectionStrings": { "EscuelaNorte": "Server=. SQLExpress;Database=cursos; Tru
sted_Connection=True;"
El servicio
builder.Services.AddDbContext<cursosContext>(options => options.UseSqlServer("na
me=ConnectionStrings:EscuelaNorte"));
Explicación:
El diagrama visualiza el resultado final al aplicar la Checklist paso a paso de los vídeos 3 y 4.
El actor realiza una solicitud request haciendo uso de la interfaz API de usuario. La aplicación ya
ha implementado:
1. Como se observa en el diagrama el contenedor de servicios donde se generan los
objetos creados y cuyo ciclo de vida depende del tipo de servicio implementado como se
estudió en el apartado Ciclo de vida de un servicio
servicio; evalúa la solicitud request del cliente
en tiempo de ejecución o runtime. Se hace uso de los mecanismos de abstracción a
través de las interfaces que ofrecen los servicios y su implementación obligatoria
relacionadaa con las firmas registradas en estas.
2. Como el request se hace a través de la API y un Endpoint que para efectos del laboratorio
de los vídeos 3 y 4, corresponde a una lista de estudiantes a través de HttpGet,
HttpGet el
contenedor de servicios llama el objeto que presta dicho servicio en la interfaz<Repo>
3. Ahora la implementación de la interfaz solicita al objeto repositorio el acceso a los datos
a través de la clase DbContext
DbContext.
Observe que se regresa el control a la interfaz y esta finaliza su ciclo de vida pasando
pasand el
control al controlador API quien recibió inicialmente el request.
4. En el controlador API se realiza la DI a través del constructor. La variable única generada
se utiliza para la instanciación de la interfaz que presta el servicio solicitado a través del
de
Endpoint que forma parte del CRUD. Observe que se accede al repositorio para ejecutar
Proceso estándar
Se entenderá cada vez que se replique en posteriores laboratorios, que usted podrá repasar o referenciar
refe la
anterior explicación que es similar.
Para esta lección haremos un gran avance y desarrollaremos dos (2) vídeos tutoriales ¡Aplica ya!
En los tres vídeos ¡Aplica ya! de esta lección desarrollaremos los siguientes pasos:
Vídeo 3
1. Crear el MODELO con la arquitectura Database First utilizando el comando Scaffold-DbContext.
DbContext.
Capa -> Infraestructura ->
> DAL
2. Configurar la conexión.
3. Agregar los servicios necesarios.
4. Migrar o pasar las Entidades generadas por el Scaffold en la capa anterior, para la capa –> Core -> DAL.
Y ajustar namespace respectivos.
5. Agregar un controlador tipo API vacío:
Capa -> Presentacion, API
Vídeo 4
6. Diseñar nuestro primer Repositorio -> EstudianteRepo.
7. Crear los mecanismos de implementación de Inyección de Dependencias —DI.
8. Registrar el servicio.
Capa: Presentación(API), Program.cs (Startup)
El Aprendizaje Hecho Fácil!
VÍDEO N° 3 y 4
Los DTO
ASUNTOS A RESOLVER
En este punto de su progreso habrá notado que el despliegue de los datos y acceso a nuestro
almacén de datos subyacente muestra partes del código implementado
implementado.. Ahora encontramos
algunos asuntos a resolver.
Referencias circulares:: Este mensaje de error en aplicaciones API se genera debido a la relación
de nuestras entidades anidadas..
Fundamentalmente se debe a que nuestra API envía un response serializado que generalmente
es en formato JSON —de de código C# a JSON
JSON— ejecuta un escaneo o mapeo de TODAS las
entidades relacionadass en el modelo de datos. Esto incluye además todas las llaves foráneas —
FK— realizando el mapeo en ambas direcciones. Lo anterior ocasiona una Referencia Circular.
Circular
Recuerde que el modelo en nuestro proyecto se relaciona gracias a las entidades de
navegación para asociar la cardinalidad entre entidades (1:M, M:M, etc).
Código
public TblEstudiante()
{
TblMatriculas = new HashSet<TblMatricula>();
}
Código
[
{
"estudianteId": 1,
"nombreEstudiante": "Veronica"
"Veronica",
"apellidosEstudiante": "Brown"
"Brown",
"estadoEstudiante": "activo"
"activo",
"fechaReg": "2023-12-31T00:00:00"
31T00:00:00",
"tblMatriculas": []
},
LA SEGURIDAD Y EL REPOSTEO
¿Qué información estamos desplegando al cliente? Toda la estructura de nuestro modelo: la
propiedad -> "tblMatriculas": [ ] está siendo expuesta y brinda pistas del diseño de la aplicación.
Un programador experimentado podría hacer uso del reposteo para insertar datos no n
autorizados….
…. registrando una nueva matrícula
matrícula, por ejemplo. Entonces, debemos evitar la
exposición delel diseño de una entidad con sus respectivas propiedades
propiedades, refactorizando
refactoriza código
para ajustar:
Bugs
Modelos Relacionales
Los modelos relacionales tienen muchas propiedades y beneficios para el diseño de nuestros almacenes de datos
subyacentes. Uno de tantos es la actualización en cascada de los datos entre las entidades. Esto en una API es de
fácil manejo por parte de un cliente
ente avanzado con intenciones distintas y con conocimientos para generar desde
su aplicación código que actualice datos en cascada sin ninguna autorización.
Los Objetos de Transferencia de Datos (DTO) corresponden a objetos que definen CÓMO se
enviarán los datos a través de una red.
Todo lo anterior nos lleva a crear estructuras de datos independientes de las entidades y el
modelo.. Y no menos importante es el impacto en el costo de la operación de transferencia de
ida y vuelta, debido a que con un objeto DTO será posible entregar muchos datos en una sola
llamada.
Los DTO
Los DTO son objetos de clases especializadas simples que no deben contener ninguna regla de negocio ni
requieren ser testeados mediante aplicación de pruebas como xUnit.
Ventajas
Un DTO permite:
1. Que el objeto remoto devuelva los datos completos al cliente en una sola llamada
remota.
2. Hacerlo reduciría la cantidad de llamadas de cuatro a una. En lugar de realizar varias
llamadas remotas, el cliente realiza una única llamada y luego interactúa con el DTO
localmente.
Un DTO es un contenedor simple para un conjunto de datos agregados que deben transferirse a
través de un proceso o un límite de red. No debe contener lógica empresarial y limitar su
comportamiento a actividades como la verificación de coherencia interna y la validación
validació básica.
Durante su diseño debemos tener cuidado de no hacer que el DTO dependa de nuevas
clases como resultado de la implementación de estos métodos. 26 Aplicaremos DTO en los
siguientes vídeos.
En nuestro proyecto esto implica mapear las propiedades que representan los campos en
nuestro almacén de datos subyacente de tal manera que simplificamos código
código.. Utilizaremos los
servicios
os ofrecidos en el paquete:
NuGet AutoMapper.Extensions.Microsoft.DependencyInjection.
26
Microsoft.
Advertencia
Para proyectos existentes o para realización de mejoras corporativas donde hallan cientos de entidades y los
nombres de los campos del almacén de datos subyacente NO EQUIVALEN o no son iguales a las propiedades
establecidas en el modelo del proyecto tipos .NET Core o MVC, NO se recomienda la técnica de automapeo
automape debido
a lo dispendioso de referenciar y hacer equivaler cada una de estas.
Explicación
El anterior diagrama implementa dos nuevos objetos/capas:
1. DTO, 2. MAPPING.
2. Una vez implementado el objeto DTO, observe que en la capa repositorio se debe
primero ejecutar el mapeo previamente implementado.
3. Y además referenciado en el controlador API al refactorizar nuestro código.
4. Aplicando el patrón Singleton se accede al servicio registrado previamente IMapping en
el contenedor de servicios
servicios.
5. En este punto, el proceso continúa similar a lo explicado en lecciones anteriores hasta el
retorno de los datos como objeto complejo.
6. Es ahorara que los datos recibidos anteriormente son convertidos a objeto tipo DTO para
ser desplegados de manera transparente al usuario, evitando las inconveniencias
descritas en esta lección.
Parte I
1. Crear nuestros Objetos DTO, replicando las entidades implicadas del modelo.
2. Definir las propiedades que estarán disponibles a la vista de los clientes que consumen los servicios API,
estableciéndose como propiedades planas.
3. Ajustar los métodos API implicados, en la capa -> Presentación.
4. Utilizar el Automapeo instalando el Nuget respectivo en:
Capa -> Infraestructura.
Capa -> Presentación.
5. Registrar las conversiones por automapeo en la carpeta respectiva:
Capa -> Infraestructura, DTO.
6. Registrar el servicio de automapeo y ubicar en el ensamblado cuáles son los profiles registrados para
mapear.
7. Implementar el automapeo en nuestros Controllers:
Capa -> Presentación.
Parte II
1. Implementar el control de las Referencias circulares haciendo uso del Nuget respectivo en la capa -> Api.
2. Habilitar el servicio para el control de Referencia circular, configurando el
método ReferenceLoopHandling.
VÍDEOS N° 5 y 6
5. Con este
ste principio, , las clases y Módulos deben
especializarse en una y solo una funcionalidad.
¿Refactorizar cuándo?
El estándar general para el total máximo de líneas de código en un método C# está como
promedio entre 20-4040 líneas. De lo contrario el método se debe refactorizar en clases
especializadas acorde al servicio que presta cada método. ¡Divide y vencerás, aplicando SOLID!
1. Refactorizar el DBContext:
Capa ->
> Infraestructura, DAL.
2. Crear la carpeta y clases especializadas por cada entidad del modelo.
Capa ->
> Infraestructura, DAL > Configuraciones.
3. Crear la interfaz genérica IBuilderTypeConfiguration para implementar la nueva configuración general
de nuestras entidades.
Capa ->
> Infraestructura, DAL > Configuraciones,
Interfaz: IEntityTypeConfiguration<TblEntidad>
4. Migrar cada uno de los esquemas de nuestras entidades desde el DbContext:
6. Ajustar el DbContext para crear la instancia a la configuración autónoma de cada entidad en el modelo:
VÍDEO N° 7
Material adjunto
Inicialmente la empresa considera esencial implementar los siguientes requerimientos del negocio para el sano
funcionamiento de request y responses con los clientes, en este caso cliente interno que consumen los servicios de
su API:
1. Regla 1:Un estudiante NO podrá crearse con estado 'Activo'. Esto solo es posible si ha legalizado la
matrícula de su semestre. Es decir, la funcionaria encargada de las m
matrículas
atrículas es la persona autorizada
para modificar su estado a 'Activo'.
Solución:
1. Controlar el Request del cliente: endpoint > Post, crear nuevo estudiante.
2. Validación:
Capa > Presentación , API, Controller > Estudiante
Solicitar el servicio
io para aplicar la regla establecida
en la capa > Core, Servicio
Las reglas de negocio se deben implementar en una capa autónoma para facilitar la abstracción
o desacoplamiento, haciendo uso de los conceptos aplicados en lecciones anteriores como la DI,
el refactoring y testing.
La capacidad de crear reglas de negocio que aseguren una lógica empresarial consistente
independientemente de que la aplicación acceda a ese conjunto de datos, es imprescindible
para una operación comercial exitosa.
3. Ámbito: las reglas de negocio pueden tener un ámbito local de la aplicación o global. La
regla puede ser aplicada por áreas o a toda la organización. Por ejemplo, si un curso
obtiene como promedio de rendimiento académico un 4.0 o superior, la regla de
negocio podría ser: se otorgará un descuento en la matrícula del 10%. El ámbito aplica a
los cursos de la ciudad X.
Observe que no existe una capa de negocios. Esto significa que si se establece una regla de
negocio, de seguro deberá ser implementada, dentro de los ActionResult o incluso en el peor de
los casos, dentro de la base de datos como lo conocí en un software empresarial de una
multinacional. Hacerlo así, generará un fuerte acoplamiento y alta adhesión. Esto es algo muy
generalizado debido a la baja o nula aplicación de Buenas Prácticas de Desarrollo de Software.
Las reglas de negocio se deben validar contra los repositorios. Esto puede conllevar a que se
afecte el modelo planteado por Clean Architecture. ¿Por qué?
Advertencia
Como se afirmó antes, las reglas de negocio NO deben generar dependencia con las capas externas a la capa
Core.
La capa Core debe ser el eje central de un sistema de información basado en Buenas Prácticas o arquitecturas
limpias que se concentran en:
A este nivel se debe trabajar con ¡abstracciones! como lo veremos en el laboratorio vídeo tutorial de esta
lección.
Desde > Capa Api > Controlador > endpoint > Repositorio > Almacén de datos.
Ahora será: Capa Api > Controlador > endpoint > Servicio (BLL) > Repositorio > Almacén de
datos.
Explicación
Si existe una regla de validación personalizada —métodos
métodos distintos a los Verbos HTTP, se realiza
la verificación. Si el resultado es TRUE, implica que la regla es verdadera o está realizando su
papel para cumplir el requerimiento de negocio exigido. Esto devolverá un mensaje Bad
Request,500.
De lo contrario, si fue FALSE, indica que la regla se cumple porque el compilador la valida y
continúa el proceso solicitado. Es decir, es falso que no se cumple. En este caso, para nuestro
ejemplo, el usuario está intentando crear un nuevo usuario con Estado = “inactivo”,
“inactivo” se realiza el
registro y se envía el resultado o response al cliente Ok, o código 200 Success!.
Ahora manos a la obra… ¡Aplica ya!. Vamos a crear nuestra primera regla de negocios adjunta
en la caja “Material adjunto”, anterior. Diseñaremos nuestro nuevo nivel o capa BLL aplicando
aplica el
concepto de Abstracción visto hasta ahora.
Capa BLL
Para nuestra implementación, la BLL la realizaremos como un servicio en la respectiva carpeta de servicios del
mismo nombre, en la capa Core. Pero también podrá hacer uso de la carpeta BLL creada previamente para esto.
Advertencia
No debemos confundir o mezclar estos dos conceptos: Las reglas de validación hacen referencia a los formatos
de datos, tamaño, tipo, sin son null, obligatorios, etc., y están fuertemente vinculadas en las validaciones contra
nuestro almacén de datos subyacente. Como ejemplos: verificar el número telefónico dependiendo del país, el
formato general introducido
ntroducido en una caja de texto para correos electrónicos, validar la edad, etc.
Las Reglas de negocio hacen referencia a las condiciones COMERCIALES de la empresa o cliente. Por ejemplo,
la regla de negocio N°1 indica que solo usuarios autorizados pueden modificar el estado de un estudiante y que el
usuario que registra nuevos estudiantes NO puede o no está autorizado para asignar el estado ‘activo’.
Parte I
Crear el resto de métodos CRUD:
pasos accederemos a nuestro almacén de datos subyacente instanciando la interfaz (nuestro mecanismo
Estas conforman los criterios de aceptación que equivalen a las validaciones implicadas en la reglas.
VÍDEO N° 8
Parte II
Crearemos el nivel de la BLL que contendrá los métodos CRUD API necesarios para llamar las firmas
del repositorio relacionado, aludiendo a la entidad implicada en la regla de negocio.
1. Crear la carpeta o folder que contendrá las reglas de negocio que conforman la BLL. Podrá hacer uso del
folder ‘BLL’ o crear la interfaz de la regla de negocio dentro de la carpeta > ‘Servicios’.
Capa -> Core, Servicios.
2. Agregar siempre una clase por cada entidad implicada que requiere la prestación del servicio para
aplicar las reglas de negocio.
Capa -> Core, Servicios —BLL.
BLL.
3. Crear la nueva interfaz del servicio de la capa BLL:
Capa: -> Core, Servicios.
4. Trasladar la interfaz a la carpeta y capa indicada:
Capa: Core, Interfaces.
Actualizar el namespace.
5. Instanciar desde la capa BLL el repositorio haciendo uso del servicio prestado por su interfaz. Recuerde
que accedemos a los repositorios por abstracción gracias a nuestros mecanismos de desacople:
Capa > Core, Interfaces, IRepo.cs
En Capa > Core, Servicios. ??
6. Generar los métodos CRUD definidos en el contrato de la interfa
interfaz,
z, o en su caso los métodos
personalizados para validar la regla de negocio:
Capa: Core, Servicios (o BLL).
7. Modificar el mecanismo para DI y las referencias en el controlador a la capa BLL:
Capa: Presentación, Controller.
8. Implementar los métodos CRUD neces
necesarios para validar la regla de negocio:
Capa: Presentación, Constructor.
9. Registrar el o los servicios relacionados con las reglas de negocio, de la capa BLL —Interface.
Interface.
10. Definir con el cliente las reglas de negocio requeridas por la empresa. Para nuestro caso,
ca la regla N°1.
Estas conforman los criterios de aceptación que equivalen a las validaciones implicadas en la reglas.
11. Implementar la regla de negocio N°1.
VÍDEO N° 9
Verifique su aprendizaje
Llene los espacios con el concepto correcto. Pueden sobrar términos o frases:
.
10. Las reglas de negocio se encuentran entre los objetos
objetos-capas
capas
y el .
11. Como Buenas Prácticas la capa de negocio generalmente se incluye en la
Códigos de estado
Los códigos de estado se definen en la sección 10 de RFC 2616.27 Puede obtener las
especificaciones actualizadas en RFC 7231.
27
Estándares para los códigos HTTP. Véase el link https://www.rfc-editor.org/rfc/rfc2616
editor.org/rfc/rfc2616
● 200 OK
La solicitud ha tenido éxito. El significado de un éxito varía dependiendo del método
HTTP
● 201 Created
La solicitud ha tenido éxito y se ha creado un nuevo recurso como resultado de ello. Ésta
es típicamente la respuesta enviada después de una petición PUT.
● 202 Accepted
La solicitud se ha recibido, pero aún no se ha actuado. Es una petición “sin compromiso”,
lo que significa que no hay manera en HTTP que permite enviar una respuesta asíncrona
que indique el resultado del procesamiento de la solicitud. Está pensado para los casos
en que otro proceso o servidor maneja la solicitud, o para el procesamiento por lotes.
● 203 Non-Authoritative Information
La petición se ha completado con éxito, pero su contenido no se ha obtenido de la
fuente originalmente solicitada, sino que se recoge de una copia local o de un tercero.
Excepto esta condición, se debe preferir una respuesta de 200 OK en lugar de esta
respuesta.
● 204 No Content (en-US)
La petición se ha completado con éxito pero su respuesta no tiene ningún contenido,
aunque los encabezados pueden ser útiles. El agente de usuario puede actualizar sus
encabezados en caché para este recurso con los nuevos valores.
● 205 Reset Content (en-US)
La petición se ha completado con éxito, pero su respuesta no tiene contenidos y además,
el agente de usuario tiene que inicializar la página desde la que se realizó la petición,
este código es útil por ejemplo para páginas con formularios cuyo contenido debe
borrarse después de que el usuario lo envíe.
● 206 Partial Content
La petición servirá parcialmente el contenido solicitado. Esta característica es utilizada
por herramientas de descarga como wget para continuar la transferencia de descargas
anteriormente interrumpidas, o para dividir una descarga y procesar las partes
simultáneamente.
● 207 Multi-Status (WebDAV (en-US))
Una respuesta Multi-Estado transmite información sobre varios recursos en situaciones
en las que varios códigos de estado podrían ser apropiados. El cuerpo de la petición es
un mensaje XML.
usuario no debe cambiar el método HTTP usado: si un POST fue usado en la primera
petición, otro POST debe ser usado en la segunda petición.
A la página anterior se le conoce como página de excepción del desarrollador que despliega
el seguimientos de pila detallado para errores del servidor. En entornos que son de desarrollo se
puede usar middleware de control de excepciones para producir una carga de error. Pero en el
entorno de producción, el cliente necesita claridad.
Código
Responses legibles
Como parte de su implementación, enviaremos una respuesta personalizada en formato JSON,
con información clara de la excepción al cliente. Un sistema de información debe ser amigable.
El contenido de la respuesta se puede modificar desde fuera del controlador utilizando una
excepción personalizada y haciendo uso del filtro d de acción. Una vez implementado el código
en el vídeo ¡Aplica ya!, el resultado para verificar la regla de negocio N° 1 debe ser similar a la
siguiente respuesta personalizada:
Código
Plus
Como programadores podríamos implementar código para que una vez identificada la creación de un nuevo
estudiante, se asigne el estado como ‘inactivo’ y facilitar el proceso al cliente. La regla de negocio N° 1 indica que
esta asignación sólo la podrá realizar
zar el personal encargado de registrar la matrícula no el de registro de
estudiantes.
Así, este tipo de plus debe ser acordado entre ambas partes y manejado de modo transparente en la
documentación. De esta manera las implementaciones saldrán bien soport
soportadas
adas en futuras auditorías.
Por otra parte, para cada error tipo HTTP o para el que sea necesario, podremos realizar una implementación
genérica, facilitando al cliente la comprensión del mensaje y sugerencia o solución, como lo verá en el vídeo
tutorial de la lección.
Para excepciones que impliquen validaciones, desplegaremos la información al cliente igualmente de modo
personalizado como se observa en la siguiente imagen y que podremos ajustar:
Tipos de registro
Existen diversas alternativas para filtrar excepciones:
1. En el ActionResult.
2. En el Controller.
3. Globalmente.
Registro en el ActionResult
Permite aplicar una excepción a una acción específica haciendo uso del filtro como atributo.
Sin el filtro tendríamos el siguiente Response: 204 como se observa en la figura. Al revisar en
nuestra tabla de códigos al inicio de la lección encontramos que significa: ”…La petición se ha
completado con éxito pero su respuesta no tiene ningún contenido…”.
Con el filtro obtendremos un Response mucho más informativo o guiador para el cliente como
se observa en la siguiente implementación para un código 404, que hace uso del atributo
ProduceResponseType:
Código
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetAsynEstuId(int id)
{
...
}
Código
namespace API.EscuelaNorte.CA.Controllers
{
[Route("api/[controller]")]
)]
[ApiController]
[ProducesResponseType(StatusCodes.Status404NotFound)]
(StatusCodes.Status404NotFound)]
public class EstudianteController : ControllerBase
{ ...
Globalizado
Para aplicar el filtro globalmente a todos los controladores de API web, agregue una instancia
del filtro a la colección de filtros. Los filtros de excepción de la colección se aplican a cualquier
acción del controlador de API web, como lo veremos en el laboratorio
boratorio de la lección.
Realicemos ahora un recorrido visual incluyendo nuestro módulo centralizado para el control de
Excepciones
VÍDEO N° 10 , 11 y 12
Verifique su comprensión
500’s
Filtros personalizados
También puede crear sus propios filtros de acción personalizados. Por ejemplo, es posible que
desee crear un filtro de acción personalizado para implementar un sistema de autenticación
personalizado. O bien, es posible que desee crear un filtro de acción que modifique los datos
del request devueltos por una acción de controlador. En la lección aplicaremos filtros para
datos. Los demás los desarrollaremos en lecciones posteriores.
28
Microsoft
Sintaxis de la
Nombre del método Descripción expresión de Más información
consulta de C#
Realiza una
ordenación Enumerable.ThenBy
ThenBy orderby …, …
secundaria en orden Queryable.ThenBy
ascendente.
Realiza una
ordenación orderby …, … Enumerable.ThenByDescending
ThenByDescending
secundaria en orden descending Queryable.ThenByDescending
descendente.
Invierte el orden de
Enumerable.Reverse
Reverse los elementos de una No es aplicable.
Queryable.Reverse
colección.
Por ejemplo, utilizando la cláusula where para filtrar de una matriz aquellas cadenas que tienen
una longitud específica, utilizando comando
comandos de LINQ, que podrá profundizar en el link de la
29
nota.
Código
IEnumerable<string>
> ObjQuery = from palabra in strPalabras
where palabra.Length == 3
select palabra;
29
https://docs.microsoft.com/es-es/dotnet/csharp/programming
es/dotnet/csharp/programming-guide/concepts/linq/linq-and
and-strings
Para el envío de requests con filtros, el cliente decide esto, si se implementa o es es opcional. Así, debemos
establecerlos como Null para que no retornen dato alguno si el cliente no los solicita o utiliza.
Para cantidades de parámetros mayores a cuatro —ver el código siguiente— como estándar y buenas prácticas se
recomienda crear objetos para recibir los parámetros en el método del controlador, como lo aplicaremos en el
laboratorio.
La anterior recomendación centraliza su manejo y evita modificar las firmas de todo el proyecto,
proyecto además de lo
¡extenso que podría ser la refactorización de cada una de estas!
Código
¡CON OBJETOS!
[HttpGet]
public async Task<IActionResult> GetAsynEstuAll(
EstuFiltrosQuery filtroQuery)
{ . . .
Observe que los parámetros son manejados haciendo uso de un objeto tipo EstuFiltrosQuery
especializado en realizar la tarea de manera centralizada. Lo verá en el laboratorio.
Binding de datos
El parámetro EstuFiltrosQuery es un Objeto complejo. Configurando
ando los parámetros como
aparecen en el código supracitado, el request irá como cadena query adjunta a la URL. Para
gestionar esto además de los errores de formato reportados por el cliente API que estemos
utilizando, haremos uso de atributos como [FromBody] que se encargará de obtener los
parámetros de la consulta desde la URL. Usted podrá implementarlos en el laboratorio de la
lección. Lo anterior dará como resultado y haciendo uso de la UI de Swagger:
Este requerimiento fue realizado por el cliente en una de sus reglas de negocio
implementadas y equivale a un Or lógico. La cadena de consulta —QueryString— como aparece
en la imagen, la gestionaremos tomándola desde la URL que apunta al recurso al hacer uso de
la propiedad [FromBody]:
1. Filtros de autorización:
Implementa el atributo IAuthorizationFilter.
Se utilizan para implementar la autenticación y la autorización para las acciones del
controlador.
2. Filtros de acción:
Implementa el atributo IActionFilter.
Contienen lógica que se usa antes y después de que se ejecute una acción de
controlador. Puede usar un filtro de acción, por ejemplo, para modificar los datos que
30
Microsoft.
3. Filtros de resultados:
Implementa el atributo IResultFilter.
Contienen lógica que se ejecuta antes y después de ejecutar un resultado. Por ejemplo,
es posible que desee modificar un resultado justo antes de que se represente al cliente.
4. Filtros de excepción:
Implementa el atributo IExceptionFilter.
Es el último tipo de filtro que se ejecuta. Puede usar un filtro de excepciones para
controlar los errores generados por las acciones o resultados del controlador. También
puede usar filtros de excepción para registrar errores.
1. Agregar una nueva carpeta para crear una clase especializada en gestionar los filtros:
Capa: Core > Queries > EstuQuery.
2. Definir en la clase anterior las Propiedades requeridas a encapsular para los filtros necesarios, acorde al
requerimiento o regla.
3. Adicionar el parámetro de tipo objeto complejo del método GetAll( ).
Capa: Presentación, Controller, método GetAll ([FromQuery] Objeto Complejo).
4. Ajustar el parámetro en el resto de las referencias del método GetAll( Objeto Complejo) del proyecto.
Capa: Core, Interfaces, IEstudianteBLL(Objeto Complejo).
Capa: Core, Servicios > EstudianteBLL(Objeto Complejo).
Capa: Presentación, Controller, método GetAll, parámetro en línea await.
5. Implementar los condicionales para atender el request del cliente. Esto es: si el cliente va a filtrar por 1:M
de los parámetros establecidos.
VÍDEO N° 13
Llene los espacios con el concepto correcto. Pueden sobrar términos o frases:
Frases: IExceptionFilter,
, Reverse, Filtrado, Allow, FromBody.
Los Endpoint
Los Endpoint son los puntos de contacto de la comunicación entre una API y un servidor. O
también, los puntos de entrada que forman parte de la URI. Por ejemplo, hemos creado una API
para la escuela, observe la siguiente URL base:
easytomake.io
easytomake.io/api/Escuela
> Escuela
Así en la documentación de una API se deben definir de modo claro y preciso los Endpoint. En la
siguiente imagen encontramos el endpoint > Estudiante que despliega los métodos HTTP que
alguno por igual les llaman endpoints:
Observe que la API debe llevar en la URI base la palabra api/ seguida del Endpoint Estudiante
que corresponden a la ubicación exacta del recurso solicitado, con o sin parámetros. Las
herramientas modernas para la creación de APIs con Swagger permiten configurar los Endpoints
de acuerdo al manejo que necesitemos.
esitemos. Claro, se utilizará o no la palabra api acorde al
enrutamiento seleccionado por los desarrolladores.
Advertencia
Acorde al estándar Open API, la productividad y el rendimiento entre dos sistemas de información que se
comunican mediante una a API, dependen de la claridad y calidad del diseño de los Endpoints.
Plus
Es de gran utilidad para el diseño de APIs con calidad. Como comentamos en lecciones
anteriores, una API debe tener una descripción de su interfaz. En la ilustración anterior de la
API Estudiantes la descripción debe ir al frente de cada servicio API, GET, POST, etc.
En particular, OpenAPI puede utilizarse para describir, desarrollar, probar y documentar las API
compatibles con REST, o describirse de mmanera estandarizada o uniforme.
Las propiedades
OpenApi define propiedades que se usan para el diseño de APIs propias. Las propiedades son
agrupadas en objetos. Los siguientes son algunos de los objetos de este estándar: 31
31
OpenAPi
32
Diagrama de la estructura OpenAPI
En general y como lo indica la documentación de OpenApi, podríamos decir que este estándar,
conocido también como OAS Open Api Specification, define una interfaz estándar,
independiente del idioma para las API HTTP facilitando que tanto los humanos como las
computadoras descubran y comprendan las funcionalidades del servicio sin acceder a código
fuente, documentación o mediante la inspección del tráfico de la red. Cuando nuestra API se
define correctamente, un consumidor puede comprender e interactuar con el servicio remoto y
esto ¡usando una cantidad mínima de lógica de implementación!
32
https://www.openapis.org/
Código
openapi: 3.1.0
info:
description: Especificación de API para proyectos académicos.
version: 1.0.0
title: API proyecto de servicios académicos
servers:
- url: 'https://easytomake.io'
- description: 'Ambiente de
e producción de apis Easy To Make'
paths:
#aquí nuestros paths
components:
#componentes reutilizables
security:
#seguridad
En el ejemplo son secciones: openapi, info, servers, paths, components, security entre otras.
Campo de rutas
Corresponde a las expresiones de plantilla
plantilla,, delimitadas por llaves ({}), para marcar una sección
de una ruta URL como reemplazable mediante parámetros de ruta. Las rutas definen el acceso a
los endpoint de los recursos URI mediante el uso de los verbos HTTP descritos en la lección
anterior para APIs con RESTful. En el siguiente ejemplo la ruta nos lleva al endpoint Estudiantes:
Código
//LA RUTA:
Estudiantes / {Id}
GET api/Estudiantes/{id}
Código
{
"/Estudiantes": {
"get": {
"description": "Acceso a la lista de todos los estudiantes",
"responses": {
"200": {
"description": "Toda la lista de estudiantes.",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Estudiantes"
}
.
.
.
La operación anterior puede tener además la sección de parámetros que son pasados a través
de la ruta así:
Código
paths:
/Estudiantes/{ID}:
get:
summary: Retorna un estudiante por su ID
parameters:
- name: ID
in: path
required: true
description: Parameter description in CommonMark or HTML.
schema:
type : integer
format: int64
minimum: 1
responses:
'200':
description: OK
Código
components:
# Reusable schemas (data models)
schemas:
...
# Reusable path, query, header and cookie parameters
parameters:
...
# Security scheme definitions (see Authentication)
securitySchemes:
...
# Reusable request bodies
requestBodies:
...
# Reusable responses, such as 401 Unauthorized or 400 Bad Request
responses:
...
# Reusable response headers
headers:
...
# Reusable examples
examples:
...
# Reusable links
links:
...
# Reusable callbacks
callbacks:
...
Objeto Paths
Contiene las rutas relativas a los puntos finales o endpoints individuales con sus operaciones.
La ruta se agrega a la URL desde el Server Object para construir la URL completa.
A las notificaciones anteriores se les conoce en el mundo de las API como devolución de
llamada o callback.
WebHooks (ganchos)
Mensajería automatizada que se dispara cuando ocurre un evento. Es el guardián de cualquier evento en
Internet…. y lo notifica!
A diferencia de las APIs que requieren solicitud/respuesta, los WebHooks no requieren solicitud.
Con este mecanismo en las especificaciones de OpenAPI 3.x en adelante, se puede definir devoluciones de llamada
—solicitudes
solicitudes asincrónicas y fuera de banda
banda— que nuestros servicios enviarán a algún otro servicio en respuesta a
ciertos eventos. Esto nos ayuda a mmejorar
ejorar el flujo de trabajo que ofrece el diseño de la API a los clientes.
WebHook técnicamente: solicitudes HTTP entrantes que responden a algún evento o estímulo externo.
Primero puede verificar si el sistema admite ese evento yendo a la operación “Listar eventos”
(GET / webhooks / events). Si hay un evento ““create”” para el recurso “Estudiante”, puede ir a la
operación “Crear un webhook” (POST / webhooks) y crearlo con el rrecurso,
ecurso, evento y URL
deseados donde la solicitud de devolución de llamada POST se enviará cada vez que se cree el
usuario. Los parámetros opcionales para los webhooks son secretos y activos y se explican en el
esquema del cuerpo de la solicitud en la opera
operación “Crear un webhook”. La siguiente sería la
operación registrada dentro de nuestra API:
Código
paths:
/registros:
post:
summary: Suscrito a un webhook
requestBody:
…
callbacks: # Definición de Callback
myEvent: # Nombre del evento
'{$request.body#/callbackUrl}'
'{$request.body#/callbackUrl}': # La URL para mostrar
# información del evento
post:
requestBody: # Contenido del mensaje
required: true
content:
application/json:
schema:
type:
: object
properties:
message:
type
type: string
Solicitudes y respuestas
O también Request Body y Responses
Responses,
Si una operación envía un cuerpo de solicitud, use la palabra clave requestBody para describir el
contenido del cuerpo y el tipo de medio así:
Código
paths:
/Estudiantes:
post:
summary: Crear un estudiante
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
nombreEstudiante:
type: string
responses:
'201':
description: ¡Estudiante Creado!
Para cada operación, puede definir posibles códigos de estado, como 200 OK o 404 Not Found,
y el cuerpo de la respuesta schema. El formato OpenAPI para las respuestas sería:
Código
paths:
/Estudiantes/{ID}:
get:
summary: Retorna un estudiante por su ID.
parameters:
- name: ID
in: path
required: true
description: ID del estudiante a retornar.
schema:
type: integer
format: int64
minimum: 1
responses:
'200':
description: Un objeto estudiante.
content:
application/json:
schema:
type: object
properties:
ID:
type:
: integer
format: int64
example: 4
nombreEstudiante:
type: string
example: Veronica Brown
'400':
description: El ID no es válido (no es un número).
'404':
description: El Id solicitado no se encontró.
default:
description: Error inesperado. Comuníquese con el administrador.
En resumen
La documentación API debe tener:
1. Explicación de ejecución de la API.
2. Información para autenticación de usuarios.
3. Instrucciones para el uso de todos los endpoint.
4. Ejemplos ilustrativos de su uso. Sería estupendo hacer uso de vídeo tutoriales cortos y
claros.
5. Algo de código ilustrativo.
Iniciaremos con la documentación básica o mínima para una API sencilla. Generalmente generar
genera
la documentación se hace:
1. De modo manual, vídeo parte I.
2. De modo automatizado, vídeos II, III.
Documentación automatizada
Plus
Existen muchas empresas que prestan el servicio de documentación automatizada. Esta pequeña inversión facilita
grandemente minimizar el desgaste del equipo de trabajo y el ROI en especial para grandes proyectos
comerciales. Dejamos algunas como referencia:
1. StopLight
Proporcione una experiencia de desarrollad
desarrollador
or de primer nivel para consumidores internos y externos
generada automáticamente a partir de sus archivos OpenAPI.
2. Postman
Utilice la herramienta de documentación de la API de Postman p para
ara generar documentación hermosa y
legible por máquina, para su API, y mantenerla actualizada automáticamente.
3. ApiDoc
Una característica útil proporcionada por apiDoc es la capacidad de mantener la documentación de
todas
odas las versiones anteriores y la última versión de la API. Esto hace posible comparar la versión de un
método con su predecesor. El desarrollador de frontend puede simplemente ver qué ha cambiado y
actualizar su código en consecuencia.
4. Slate
Permita que sus usuarios actualicen su documentación por usted : de forma predeterminada, su
documentación generada por Slate se aloja en un repositorio público de GitHub. Esto no solo significa
que obtiene alojamiento to gratuito para sus documentos con GitHub Pages, sino que también facilita que
otros desarrolladores realicen solicitudes de extracción a sus documentos si encuentran errores
tipográficos u otros problemas. Por supuesto, si no desea utilizar GitHub, tambiénén puede alojar sus
documentos en otro lugar.
5. Swagger
Swagger elimina el trabajo manual de la documentación de API, con una variedad de soluciones para
generar, visualizar y mantener document
documentos de API.
33
Documentación en .NET Core
Los archivos de origen de C# pueden tener comentarios estructurados que generan
documentación de API para los tipos definidos en esos archivos. El compilador de C# genera un
archivo XML que contiene datos estructurados que representan los comentarios y las firmas de
33
Microsoft.
API. Otras herramientas pueden procesar esa salida XML para crear documentación legible en
forma de páginas web o archivos PDF, por ejemplo.
Este proceso proporciona muchas ventajas para agregar documentación de API en el código:
● El compilador de C# combina la estructura del código de C# con el texto de los
comentarios en un único documento XML.
● El compilador de C# comprueba que los comentarios coinciden con las firmas de API
para las etiquetas pertinentes.
● Las herramientas que procesan los archivos de documentación XML pueden definir
elementos y atributos XML específicos de esas herramientas.
Como parte de las herramientas para crear salida XML en .NET Core tenemos:
● DocFX: DocFX es un generador de documentación de API para .NET, que actualmente
admite C#, Visual Basic y F#. También permite personalizar la documentación de
referencia generada. DocFX compila un sitio web HTML estático a partir del código
fuente y los archivos Markdown. Además, DocFX proporciona la flexibilidad para
personalizar el diseño y el estilo de su sitio web a través de plantillas. También puede
crear plantillas personalizadas.
● Sandcastle: las herramientas de Sandcastle crean archivos de ayuda para bibliotecas de
clases administradas que contienen páginas de referencia tanto conceptuales como de
API. Las herramientas de Sandcastle se basan en la línea de comandos y no tienen front-
end de GUI, características de administración de proyectos ni proceso de compilación
automatizado. El Generador de archivos de ayuda de Sandcastle proporciona
herramientas independientes basadas en GUI y la línea de comandos para crear un
archivo de ayuda de forma automatizada. También está disponible un paquete de
integración de Visual Studio para que los proyectos de ayuda se puedan crear y
administrar completamente desde Visual Studio.
● Doxygen: Doxygen genera un explorador de documentación en línea (en HTML) o un
manual de referencia fuera de línea (en LaTeX) a partir de un conjunto de archivos de
código fuente documentados. También se admite la generación de resultados en RTF
(MS Word), PostScript, PDF con hipervínculo, HTML comprimido, DocBook y páginas
man de Unix. Puede configurar Doxygen para extraer la estructura de código de los
archivos de código fuente no documentados.
Saliendo a producción
En este punto la API funciona en nuestra máquina pero cliente requiere muestras, pruebas o
adelanto del desarrollo. Para hacerlo posible debemos como mínimo hacer entrega de
la Documentación API. Lo anterior implica que el cliente debe conocer el contrato de su API.
Esto lo diseñan algunos programadores haciendo
iendo uso de editores de texto populares, pero la
industria estandarizó la documentación para hacer su entrega al cliente o los desarrolladores
como lo veremos en el siguiente vídeo.
Plus
Hay que trabajar duro para implementar una API. Por muy funcional que resulte si los clientes o desarrolladores
no entienden cómo hacer uso de sus servicios de nada vale el esfuerzo.
La documentación API es el manual de usuario, la brújula para que nuestros clientes se familiaricen
familia con sus
servicios.
Aspecto final de la
documentación básica
API en .NET Core
1. Instalar el paquete NuGet (ya viene cargado en la plantilla .Net Core API) > Swashbuckle.AspNetCore:
Capa: Presentación, Dependencias
2. Verificar el registro del servicio:
Capa: Presentación, Program.cs.
Configurar el pipeline HTTP Request
3. Implementación del código y registro del servicio de documentación:
Capa: Presentación, Program.cs.
4. Habilitar en Compilación del proyecto API, la salida de documentación XML.
Capa: Presentación > Propiedades > Compilar.
5. Validar el despliegue inicial de la documentación:
https://localhost:1234/swagger/v1/swagger.json
6. Guardar como, Descargar o exportar el archivo JSON de la documentación con Swagger.
Material adjunto
El esquema de la documentación API podrá descargarlo una vez finalice los siguientes laboratorios desde el link
superior de us API ‘Swagger.json’
VÍDEOS N° 14, 15 y 16
Debemos agregar el nombre del recurso en la URI de una API como por
ejemplo: easytomake.io/api/
easytomake.io/api/Escuela.aspx
Los campos de rutas y WebHook son lo mínimo para describir los elementos
de una API con el estándar OpenAPI
¿Qué es autenticar?
Haremos uso de la siguiente definición:
Autenticación
“Es el acto o proceso de confirmar que algo (o alguien) es quien dice ser.
Para ilustrarlo:
El probador es nuestro cliente-usuario
usuario API que necesita acceder a un endpoint de la API Escuela
Norte. El verificador es nuestro servicio implementado para realizar la autenticación
ción
comprobando que el que va a accede sea un usuario que cumpla estas condiciones:
condiciones
1. Que esté registrado y sus credenciales sean válidas.
2. Tenga permisos o rol para hacer uso de los recursos y servicios prestados por la API.
¿Qué es un Token?
Referencia,identificador
“Un token también contiene una firma criptográfica que es generada por una clave privada
conocida solo por el servidor de autenticación” 35
34
Wiky.
35
Microsoft.
El usuario solicita los servicios API (POST: Login). El servidor valida y marca como true la
autenticación. De lo contrario indicará al usuario que su token ha expirado y genera un nuevo
TOKEN (payload). El usuario envía su request con el Token en un encabezado. El servidor
comprueba las credenciales y valida el token para su envío. Ahora se procede a asegurar el
recurso al usuario, quien obtendrá los servicios solicitados hasta que el token asignado se venza.
De ser así, el servidor requerirá una nueva autenticación para generar un nuevo token.
Los JWT se pueden firmar usando una SecretKey con el algoritmo criptográfico HMAC36 o un
par de claves pública / privada usando RSA37 o ECDSA38.
El token es realmente una cadena de texto (en la figura strToken usado en el laboratorio de la
lección) que tiene tres partes codificadas en Base6439, cada una de ellas separadas por un punto,
como se ve en la imagen del token que desarrollaremos en el laboratorio de la lección:
Header
El encabezado generalmente consta de dos partes:
a. El tipo de token, que en este caso es de tipo JWT, y
b. El algoritmo de firma que se utiliza, como HMAC SHA256 o RSA.
La cadena de texto generada en formato JSON está codificado en Base64Url para formar la
primera parte del JWT.
36
En la criptografía, un HMAC (a veces expandido como código de autentificación de mensajes en clave-
hash o código de autenticación de mensaje basado en hash) es una construcción específica para calcular
un código de autentificación de mensaje (MAC) que implica una función hash criptográfica en combinación
con una llave criptográfica secreta. Wiky.
37
En criptografía, RSA (Rivest, Shamir y Adleman) es un sistema criptográfico de clave
pública desarrollado en 1979, que utiliza factorización de números enteros. Es el primer y más
utilizado algoritmo de este tipo y es válido tanto para cifrar como para firmar digitalmente.
38
El algoritmo de firma digital de curva elíptica, o ECDSA, es uno de los algoritmos de
cifrado de criptografía de clave pública más complejos. Utiliza la criptografía de curva elíptica muy
usada para la creación de números pseudoaleatorios, firmas digitales y más.
39
Base64 es un algoritmo de codificación que le permite transformar cualquier carácter en un alfabeto que
consta de letras latinas, dígitos, más y barra diagonal. Gracias a él, puede convertir caracteres chinos,
emojis e incluso imágenes en una cadena "legible", que se puede guardar o transferir a cualquier lugar.
Código
{
"alg": "HS256", "typ": "JWT"
}
Código
{
user_id: 777,
account_id: 666,
email: '[email protected]',
full_name: 'Veronica Brown',
default_language: 'en_US'
}
El campo payload de JSON Web Token contiene la información real que se transmitirá por la
aplicación.. Aquí se definen algunos estándares que determinan qué datos se transmiten y
cómo. La información se proporciona como pares key/value (clave-valor).
● Los claims registrados son los que figuran en el JSON Web Token Claim Register de la
IANA40 y cuyo propósito se establece en un estándar. Algunos ejemplos son el emisor
40
Internet
et Assigned Numbers Authority
Authority:: La coordinación global de la raíz dns, el direccionamiento IP y
otros recursos de protocolo de Internet. Específicamente, asignan y mantienen códigos y sistemas de
numeración únicos que se utilizan en los estándares técnicos ("protocolos") que impulsan Internet.
Advertencia
Payload malicioso
El phishing (suplantación de identidad) es el método más común para hacer uso de un payload y descargar
archivos maliciosos, sea en el encabezado de un correo, secuestro de la DNS, SMS o VoIP
VoIP42.
Generalmente se diseñan en sistemas operativos Kali Linux con extensión .apk.
Por tal motivo, las APIs seguras deben hacer uso de herramientas ro
robustas,
bustas, probadas, verificadas por la
comunidad y frameworks reconocidos internacionalmente como es el caso de .Net Core framework.
Por ejemplo, si desea utilizar el algoritmo HMAC SHA256, la firma se creará de la siguiente
manera:
41
Microsoft.
42
El Protocolo de Voz sobre Internet (VoIP), también llamado telefonía IP,
, es un método y grupo de
tecnologías para la entrega de comunicaciones de voz y sesiones multimedia a través de redes de Protocolo
de Internet (IP), como Internet.
Código
{
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
}
La firma se usa para verificar que el mensaje no se haya modificado en el camino. En el caso de
los tokens firmados con una clave privada, también puede verificar que el remitente del JWT es
quien dice ser. El resultado son tres (3) cadenas de URL Base64 separadas por puntos que se
pueden pasar fácilmente en entornos HTML y HTTP, mientras que son más compactas en
comparación con estándares basados en XML como SAML.
Código
"{\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\":\"Cesar Brown\
\",
\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"
:\"[email protected]\",\"http://schemas.microsoft.com/ws/2008/06/identity/claims/role
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role\
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role
":\"Keymaster\",\"nbf\":1641304M
":1641304M\f\u000b\b�^\u001c\b��M�\fL�
\rL��\u000b\b�\\�Ȏ��\u001du001d\u001d\u001c\u001c��\u001b��[\u001a\u001b�� ��\u000e�̎ MKȋ\b�]
Y\b���\u001
d\u001d\u001c\u001c��\u001b
u001b��[\u001a\u001b��\u000e�̎ MKȟ"
En una aproximación inicial y como ejemplo real de nuestra API Escuela Norte, tenemos el
siguiente token:
Código
HEADER
{
"alg": "HS256",
"typ": "JWT"
}
PAYLOAD : DATOS
{
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Cesar Brown",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "[email protected]",
"[email protected]"
"http://schemas.microsoft.com/ws/2008/06/identity/claim
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Keymaster",
"nbf": 1641305140,
"exp": 1641305260,
"iss": "https://localhost:7295/"
"https://localhost:7295/",
"aud": "https://localhost:7295/"
}
your-256-bit-secret
TOKEN
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ
IkpXVCJ9
.eyJodHRwOi8vc2NoZW1hcy54bWxzb
bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW
uYW1lIjoiQ2VzYXIgQ
nJvd24iLCJodHRwOi8vc2NoZW1hcy54
54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy
NsYWltcy9lbWFpbGFkZHJ
lc3MiOiJkYmFjZXNhckBvdXRsb29rLmNvbSIsImh
rLmNvbSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3
3MvMjAwOC8wNi9pZ
GVudGl0eS9jbGFpbXMvcm9sZSI6IktleW
IktleW1hc3RlciIsIm5iZiI6MTY0MTMwNTE0MCwiZXhwIjoxNjQxMzA
MCwiZXhwIjoxNjQxMzA1MjYwLCJpc3M
iOiJodHRwczovL2xvY2FsaG9zdDo3Mjk
Mjk1LyIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjcyOTUvIn
OjcyOTUvIn0
.ky1mAo4CAUYxazBifiJQpwxlNDk_K
CAUYxazBifiJQpwxlNDk_K3uMGaXhgeBssvs
Error 401
Si el usuario cliente no está autorizado y debe solicitar su token sea por ser primera vez o por
vencimiento, se desplegará un error de tipo response HTTP 401 como se observa en la imagen:
Cuando las credenciales de autenticación del usuario no corresponden a las digitadas se activa
un error HTTP 404 como se observa en la imagen, para nuestro caso, con el mensaje
personalizado visto en el laboratorio de la lección:
Base64Url
Namespace:Microsoft.IdentityModel.Tokens
Convierte la cadena especificada, que codifica datos binarios como dígitos base64-url, en una
matriz de enteros sin signo de 8 bits equivalente. Los esquemas de codificación Base64 son
comúnmente usados cuando se necesita codificar datos binarios para que sean almacenados y
transferidos sobre un medio diseñado para tratar con datos textuales. Esto es para asegurar que
los datos se mantienen intactos y sin modificaciones durante la transmisión. 43
43
Mozilla.org
FUENTE: devopsschool.com.
Una vez diseñado el servicio para la generación de un Token, el proceso y resultado sería el
siguiente con el nuevo endpoint KeyAPI para el manejo de la seguridad de nuestra API, tarea a
desarrollar en el siguiente laboratorio:
Explicación
El cliente realiza la solicitud haciendo uso de la API. Se ejecuta el servicio para generar el token,
IConfiguration.. Ahora el control se pasa a la capa de seguridad o clase
clase-objeto Login para
recoger las credenciales del usuario, haciendo uso del endpoint Http Post.. El objeto token ahora
debe validar o autenticar las credenciales suministradas por el cliente contra el repositorio o
fuente de datos quien devolverá true o false. Si es true se procede a generar el token para ser
enviado al cliente.
Por supuesto,
to, ya hemos diseñado previamente la funcionalidad de la capa y su objeto token
como se debe observar en el despliegue de los endpoint, implementando el método Post del
endpoint KeyAPI:
Ahora el cliente haciendo uso del endpoint implementado en la API, procede a enviar sus
credenciales que corresponden al parámetros de validación de Login, a través de la URL que
apunta al endpoint y método GET como se observa en la imagen:
Obtenemos entonces la respuesta API a la solicitud del usuario una vez generado
el Token dentro del Response body como se nota en la imagen:
Ahora el usuario una vez verificadas sus credenciales y generado su token podrá acceder a los
servicios API con el token generado, asegurando su operación.
44
Todo sistema propende a la Entropía. En el link a pie de página encontrará recomendaciones para enfrentar
potenciales fallos con JWT.
Material adjunto
// SEGURIDAD...
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JWT:Issuer"],
ValidAudience = builder.Configuration["JWT:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"]))
};
});
44
https://connect2id.com/products/nimbus
https://connect2id.com/products/nimbus-jose-jwt/vulnerabilities
Parte II : La autenticación
El cliente debe validar sus credenciales en nuestra app y solicitar
un token. Nuestra API debe generar el token que formará parte del Header en
el request del cliente > Hedear > Key.
VÍDEO N° 17
VÍDEO N° 18
Laboratorio I:
Laboratorio II:
Como parte de la generación de las librerías cliente, se incluye la lógica de autenticación para
poder interactuar con las API. Swagger provee una serie de plantillas y archivos para simplificar
la integración con el consumo final de la API. Recuerde que toda API necesita autenticación para
que un usuario-cliente pueda realizar llamadas a la API, sea por su interfaz o haciendo uso de
Azure Active Directory (AAD), y a través del respectivo endpoint post/Login, proceso que se
realiza con los tokens de acceso, para nuestro caso, al portador (Bearer acceso token), esquema
de seguridad OpenApi que es un portador de seguridad ligero para otorgar acceso al cliente a
los servicios protegidos API.
Una vez verificada la validez de las credenciales del usuario y la regla de negocio, se procede a
generar el token que para efectos de nuestro laboratorio, se entrega en la misma UI Swagger,
así el usuario procederá a autenticarse en el sistema API con dicho token. De esta manera se la
habilita el endpoint respectivo PostAsync, HttpPost para la creación de un nuevo estudiante,
como lo verá en los siguientes laboratorios.
En los vídeos ¡Aplica ya! de esta lección desarrollaremos los siguientes pasos:
45
Todos los ensamblados .NET contienen la definición de tipos, la información de control de versiones para
el tipo, los metadatos y el manifiesto
PARTE III
Llene los espacios con el concepto correcto. Pueden sobrar términos o frases:
notificaciones reservadas
Parte IV:
46
Esquemas de seguridad OpenApi
Como aprendimos en lecciones anteriores, autenticación es el proceso para identificar que el
usuario que dice ser lo es realmente, haciendo uso de sus credenciales. Y la autorización alude a
los permisos que la aplicación otorga con base en las reglas del negocio para acceder a
recursos.
El estándar OpenApi hace uso de esquemas de autenticación y autorización HTTP que implica
utilizar la palabra reservada Authorization en el encabezado, y que se clasifican en:
1. Básico
2. Portador (Bearer)
3. Esquemas varios definidos por RFC 7235
4. Cookies
5. Oauth 2
6. OpenID Connect
46
Swagger.io
El encabezado enviado al servidor durante el request¸ llevará la palabra Bearer más la cadena
codificada en base64, y haciendo uso de protocolos de seguridad como HTTPS / SSL, y se verá
algo así:
Authorization: Basic ñsfkjgpadg*ñof==
47
Su descripción podrá ser algo similar a este código:
openapi: 3.0.0
...
components:
securitySchemes:
basicAuth: # <-- arbitrary name for the security scheme
type: http
scheme: basic
security:
- basicAuth: [] # <-- use the same name here
1. opt.AddSecurityDefinition
2. opt.AddSecurityRequirement
El primer método define los esquemas de seguridad para la protección API. Aquí podrá aplicar
cualquiera de los esquemas mencionados anteriormente, como se observa en el siguiente
código general:
47
Ibid.
PARTE IV
Como parte final del proceso de gestión de usuarios, credenciales y autorización para acceso a
recursos restringidos, realicemos el laboratorio que nos debe generar los siguientes resultados:
Podrá modificar el nombre del Controlador UsuarioController que hemos venido utilizando, por
LoginController o como lo prefiera, para brindar información al usuario.
Como puede notar, se ha habilitado el botón Authorize además de los íconos (candados) al final
de cada endpoint indicando al usuario que la API goza de un sistema de seguridad. El proceso a
seguir y que desarrollaremos en el siguiente laboratorio ¡Aplica ya! se resume así:
Recuerde que el recurso para crear usuarios sólo está autorizado a usuarios con rol de
adminstradores (código 1). Ahora la API valida las credenciales y generará el token con un
tiempo de vida fijado acorde al contrato API, como se observa en la imagen:
Por su parte, el sistema API asegura el recurso modificando el estado abierto representado en el
icono del candado a cerrado.
3. Realizar las pruebas. Las explicaciones del código se realizan en los vídeos de la lección.
VÍDEO N° 19 – 23
Repositorio y UnitOfWork
La implementación de estos patrones puede ayudar a aislar la aplicación de los cambios en el almacén de
datos y puede facilitar las pruebas unitarias automatizadas o el desarrollo basado en pruebas (TDD). 48
El principio DRY
Don’t Repeat Yourself
O DIE —Duplicidad está mal. Este acrónimo es un principio aplicado al desarrollo de software
que facilita la reducción de comportamientos o patrones de código repetitivo, evitando la
redundancia. En el libro The Pragmatic programmer49 (El El programador pragmático)
pragmático se indica que
al aplicar este principio y realizar posteriores modificaciones, no se requerirán cambios o al
menos significativos en el resto de los elementos. Es decir, se busca reutilizar código existente.
48
Microsoft, Tom Dykstra.
49
Andy Hunt, Dave Thomas.
Como ha estudiado hasta aquí, las entidades del modelo requieren además de estas
operaciones de mantenimiento
tenimiento de datos, al igual que el protocolo HTTP, sus verbos y los
Queries (SQL) implicados en el proceso. Son estructuras algorítmicas predecibles. Todo esto nos
señala la importancia de comprender desde el principio los requerimientos de usuario para
definir los módulos comunes a toda la aplicación, como las funciones vistas en la lección
anterior —autenticación
autenticación y autorización API. Abstraer este tipo de necesidades del usuario
permitirá minimizar la redundancia de código y centralizar la funcionalidad de modo genérico y
globalizado.
Repositorios
Patrón
“… Un repositorio realiza las tareas de intermediario entre las capas del modelo de dominio y la asignación de
datos, actuando de manera similar a un conjunto de objetos de dominio en la memoria. Los objetos del cliente
crean consultas mediante declaración y las envían a los repositorios para obtener respuestas.
Los repositorios, también, apoyan el propósito de separar, claramente y en una dirección, la dependencia entre el
50
dominio de trabajo y la asignación de datos“
Como puede observar, este patrón está orientado a abstraer la relación entre las capas de
acceso a datos (dominio y entidades) con su lógica, y la capa de la lógica empresarial. Ya ha
visto en laboratorio anteriores las clases dedicadas a los repositorios y que constituyen una
parte importante de la capa Infraestructura en proyectos que hacen uso de la arquitectura Clean
Architecture.
50
Patterns of Enterprise Application
● Código duplicado
● Un mayor potencial de errores de programación
● Escritura débil de los datos profesionales
● Dificultad para centralizar las directivas relacionadas con los datos, como el
almacenamiento en caché
● Incapacidad para probar fácilmente la lógica empresarial de forma aislada de las
dependencias externas
● Fuerte adhesión, acoplamiento y dependencias.
Ventajas de un repositorio
Como parte de las ventajas de hacer uso de este patrón encontramos:
1. Facilitar el Testing por el desacople de dependencias.
2. Mantenimiento y legibilidad de código.
3. Eliminar la redundancia gracias al reuso de la lógica de conexión a datos desde cualquier
usuario y para el resto de los objetos que realizan requests.
4. Establecer un solo canal para realizar mantenimiento de datos en la fuente de datos. El
área transaccional51 (actualizaciones), siempre será gestionada por los repositorios.
Tipos de repositorios
Como parte de la clasificación de repositorios encontramos:
Ejemplo:
En laboratorios anteriores hemos
implementado este tipo de repositorios
para las entidades Estudiante y Usuario
51
Una transacción es una forma de agrupar, o juntar por lotes, una serie de actualizaciones de un origen
de datos para que todas se confirmen a la vez o no se confirme ninguna si se deshace la transacción. Si no
usa una transacción, los cambios realizados en el origen de datos se confirman automáticamente en lugar de
confirmarse a petición. Microsoft.
Repositorio genérico
Este tipo de repositorios facilita centralizar todas las operaciones CRUD en un solo repositorio
que será utilizado por la entidades del modelo. Entremos a revisar este tipo de patrón de
ddiseño a continuación.
De esa manera, cuando una UoW está completa, puede llamar al método transaccional en esa
instancia del contexto y estar seguro de que todos los cambios relacionados se coordinarán.
Todo lo que la clase necesita es un método especializado en la transacción y una propiedad
para cada repositorio.52
En resumen: todas las transacciones (CRUD) en una unidad UoW se realizan en una sola
transacción haciendo uso de una única instancia del DbContext como se observa en la
siguiente imagen.
52
Microsoft
Los repositorios y la Unit of Work garantizan que todos utilicen el mismo DbContext.
Data Mapper
Un repositorio emite las consultas adecuadas al origen de datos y, a continuación, asigna los conjuntos de
resultados a las entidades empresariales expuestas externamente. Los repositorios a menudo usan el patrón Data
53
Mapper para traducir entre representaciones. Los repositorios eliminan las dependencias que los clientes que
llaman tienen en tecnologías específicas.
1. Entidad base: Crear la clase especializada en el control de nuestro repositorio tipo UOW.
Capa -> Core, Entities, EntidadBase.
- Declarar la propiedad tipo Id universal como llave primaria (PK)
2. Cada Entidad hereda de la clase base…
- Capa ->> Core, Entities, cada una he
hereda de EntidadBase
- Eliminar las propiedades ID (PK) en cada Entidad del modelo
53
https://docs.automapper.org/en/latest/Getting
https://docs.automapper.org/en/latest/Getting-started.html
3. Renombrar por la propiedad maestra ID, las PK de cada entidad relacionada en Configuración
Capa - > Infraestructura ->
> DAL -> Configuraciones
10. Reemplazar las interfaces referenciadas en cada servicio, por la interfaz del nuevo repositorio genérico -
> IRepoGenerico
Capa -> Core ->> Servicios
-Ajustar
Ajustar los métodos actuales por los genéricos.
VÍDEO N° 24
Recuerde que…
Así, dependiendo del Core de negocio, se tendrán UoW para cada unidad de trabajo por el modelo de negocio
para ventas, compras, etc.
Además se podrá compartir una sola instancia de conexión a la base de datos para todos los repositorios.
reposi
4. Implementar IUoW
Capa - > Infraestructura ->
> Repositorios, UoW
6. Encapsular para instanciar con el repositorio genérico IRepoGenerico cada entidad del modelo existente.
7. Ajustar los miembros o firmas implementadas de la interfaz que corresponden a cada entidad y su
repositorio genérico.
¡RECUERDE!:
Con UoW se hace uso de la MISMA INSTANCIA DEL CONTEXTO para cada repositorio genérico!
Finalmente…
10. Referenciar nuestro UoW en cada servicio para acceder a su repositorio genérico respectivo.
- DI para inyectar el UoW.
Capa Core -> Servicios
VÍDEO N° 25
Todas
odas las transacciones (CRUD) en la UoW se realizan en una sola
transacción haciendo uso de una única instancia del DbContext
Todas
odas las transacciones (CRUD) en la UoW se realizan en una sola
transacción haciendo uso de una única instancia del DbContext
Azure
Esta plataforma de nube pública y privada de Microsoft, ofrece informática en la nube
entregando servicios con un amplio catálogo de soluciones como almacenamiento, software,
utilidades, análisis, servidores, detección de amenazas, bases de datos entre otras. Facilita
F una
innovación rápida y economía de escala, reduciendo costos operativos. Se cobra por lo que se
utiliza.
Fuente: Microsoft
Azure Devops
DevOps reúne a individuos, procesos y tecnología mediante la automatización de la entrega de software para
ofrecer un valor continuo a los usuarios.
Con Azure DevOps puede crear, compilar y publicar canalizaciones que proporcionan integración, entrega e
implementación continuas de las aplicaciones.
Puede integrar los repositorios y las pruebas de aplicaciones, realizar la supervisión de aplicaciones y trabajar
con artefactoss de compilación. También puede trabajar con elementos de trabajo pendiente para realizar el
seguimiento, automatizar la implementación de la infraestructura e integrar una gama de herramientas y
servicios de terceros.
Si no está familiarizado con Azure, puede registrarse para obtener una cuenta gratuita en el sitio web de Azure y,
de este modo, empezar a explorar sin costo alguno.
54
Microsoft
En la siguiente ilustración se resume una cuenta de Azure. Para crear y usar los servicios de
Azure, necesita una suscripción de Azure. Después de crear una cuenta de Azure, puede crear
suscripciones adicionales. Por ejemplo, es posible que la empresa use una única cuenta de Azure
para el negocio, y suscripciones independientes para los departamentos de desarrollo,
marketing y ventas.
Fuente: Microsoft
La cuenta gratuíta será suficiente para realizar la migración de nuestra API, lo que nos
proporciona acceso a productos populares por un año y acceso a más de 40 productos que
siempre son gratuítos, de modo similar que una cuenta de estudiante.
55
https://azure.microsoft.com/es-mx/explore/security/
Escenarios de desarrollo
Para incorporar Azure con sus aplicaciones, debe considerar las necesidades durante la
programación de código,, desarrollo y las expectati
expectativas
vas de los clientes. Tenemos en resumen los
siguientes escenarios:
1. Sin servidor: Esto es, experiencia de desarrollo completa! Utilice los servicios de Azure
Functions que ofrece más de 250 conectores a otros servicios y en general, todos los
relacionados conon un servidor en la nube. Permite programar, compilar y depurar
localmente sin configuraciones extras. Además tiene integración automática con
DevOps.
Esta alternativa ofrece programar en los lenguajes de programación más populares y
variados escenarios como la implementación de aplicaciones web, APIs con .NET, Node.js
o Java; flujos de trabajo de aprendizaje automático con Python y automatización en la
nube con PowerShell. 56
2. En la nube desde aplicaciones
aplicaciones:: No se requiere modificar la arquitectura de nuestras
nues
aplicaciones o modelo de implementación actual. Puede tener su aplicación localmente e
integrar servicios Azure para ampliar la funcionalidad.
Por ejemplo, el servicio de Azure Key Vault sería un gran valor agregado para nuestra API pues permite almacenar
de forma segura y controlar de modo estricto el acceso a tokens, contraseñas, administración de certificados de la
capa de transporte y sockets seguros (TLS/SSL) públicos y privados, administración y control de claves de API, y
otros secretos.
Lo anterior significa que ya no tendrá que almacenar localmente o en la aplicación datos secretos como la famosa
cadena de conexión que hemos implementado en laboratorios anteriores.
3. Hospedaje en Azure:: Podrá hospedar toda la pila de la API, aplicaciones web hasta
nuestra base de datos y servicios de almacenamiento.
56
Microsoft
Este servicio provee hospedaje escalable para aplicaciones web, API REST y servicios de back-
end. App Service admite código escrito en .NET Core, .NET Framework, Java, Ruby, Node.js, PHP
y Python. App Service es recomendable para la mayoría de los sitios web, especialmente si no se
necesita un control estricto sobre la infraestructura de hospedaje.
Azure App Service es un servicio basado en HTTP, que facilita el hospedaje de aplicaciones web,
API de REST por ejemplo. También puede aprovechar sus capacidades de DevOps, como la
implementación continua desde Azure DevOps, GitHub, Docker Hub y otros orígenes, la
administración de paquetes, los entornos de ensayo, el dominio personalizado y los certificados
TLS/SSL.
Fuente: Microsoft
Para el laboratorio de la lección implica que el servidor de la aplicación IIS y la base de datos
de Microsoft SQL Server se ejecutarán en nuestra máquina o cliente. Puede implementar una
aplicación de .NET Core como:
1. Una implementación dependiente del marco, que incluye los binarios de la aplicación
pero depende de la presencia de .NET Core en el sistema de destino. Los usuarios de la
aplicación tienen que instalar por separado el tiempo de ejecución de .NET.
2. Como una implementación autónoma, que incluye los binarios de la aplicación y de .NET
Core. Es decir, los usuarios de la aplicación pueden ejecutarla en un equipo que no tenga
instalado el tiempo de ejecución de .NET.
El archivo dll lleva el nombre del proyecto. Por ejemplo, para nuestra API ‘Escuela Norte’, se crea
un archivo denominado escuela_norte.dll. Las aplicaciones publicadas de esta manera se
ejecutan con el comando .dotnet <filename.dll> y se pueden ejecutar en cualquier plataforma
57
Microsoft
Un ejecutable no es multiplataforma
Son específicos de un sistema operativo y una arquitectura de CPU. Al publicar la aplicación y crear un ejecutable,
puede publicarla como autónoma o dependiente del marco.
La publicación de una aplicación como autónoma incluye el tiempo de ejecución de .NE.NETT con la aplicación, y los
usuarios de la aplicación no tienen que preocuparse por instalar .NET antes de ejecutar la aplicación.
Las aplicaciones publicadas como dependientes del marco no incluyen el tiempo de ejecución y las bibliotecas de
58
.NET; solo see incluyen la aplicación y las dependencias de terceras partes .
Publicar una aplicación significa generar una aplicación compilada que se puede hospedar en un servidor.
El paso de publicación lo controla el SDK de .NET Core, mientras que el paso de implementación se puede
controlar
trolar mediante distintos enfoques.
58
Ibdi
Verificaciones iniciales
El paso a paso nos guiará en el proceso de publicación de la API. Adoptando el enfoque de
carpetas.. Para poder correr nuestra API en el servidor local, realizaremos las siguientes
verificaciones básicas:
Verificaciones iniciales:
Ruta: Panel de control > Programas y características > Activar o desactivar las
características de Windows
2. Verificar que su sistema operativo cuenta con IIS (Servidor de Microsoft Windows)
Ruta: Panel de control como se observa en la imagen:
Nota: Ideal si tiene la versión más actualizada.
3. Instale o comprube si está instalado Microsoft SQL Server Management Studio, en su panel
de control:
4. Para poder desplegar los servicios de .NET Core en IIS necesita el instalador .NET Core
Hosting Bundle el cual prepara IIS para .net core.
Verificar si se tiene instalado el Hosting Bundle como se ve en la figura:
Nota: para esto, ya debe tener instalado su administrador de IIS Services:
1. Abra el IIS Services y ubique y abra en el panel principal la opción > Módulos.
2. Verifique si el paquete de la figura se halla instalado:
59
https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-aspnetcore-6.0.10-windows-hosting-
bundle-installer
VÍDEO N° 26
Preparando Azure
Publicando el proyecto
VÍDEO N° 27, 28
¡Notifique al usuario!
Seguridad JWT
Proveedores externos
¡Pasarelas de pago!
Apéndice A
Relación de los vídeos aduntos al curso.
Apéndice B