Servicios REST con .NET Core
  • Servicios REST con ASP.NET Core y Entity Framework Core
  • 1. Introducción
    • 1.1 Instalación Visual Studio Community
    • 1.2 Instalación de SQL Server en Mac
    • 1.3 Extensión Intellicode
    • 1.4 Aplicación de ejemplo
  • 2. Explicación de Conceptos
    • 2.1 Servicios REST
      • 2.1.1 OData
      • 2.1.2 GraphQL
    • 2.2 Entity Framework para versiones de tu base de datos
    • 2.3 Paquetes Nuget
    • 2.4 Archivo de recursos
    • 2.5 Estructura de los Proyectos .Net Core
    • 2.6 Código Limpio
    • 2.7 Programación asíncrona
    • 2.8 Git
    • 2.9 ¿Qué es Scrum?
      • 2.9.1 Personas
      • 2.9.2 Roles Scrum
      • 2.9.3 Backlog
      • 2.9.4 Reuniones del Scrum
  • 3. Control de código fuente y Scrum con Azure DevOps
    • 3.1 Introducción a Azure DevOps
      • 3.1.1 Crear un nuevo Proyecto en Azure DevOps
      • 3.1.2 Agregando personas al equipo de trabajo
      • 3.1.3 Agregando los sprints y la capacidad de trabajo
      • 3.1.4 Crear el Backlog y asignar User Stories al Sprint
      • 3.1.5 Crear Prototipos
      • 3.1.6 Conectar a Azure DevOps desde Visual Studio
        • 3.1.6.1 Crear tu proyecto con Visual Studio Community y sincronizarlo a Azure DevOps
      • 3.1.7 Consultar tus tareas pendientes
        • 3.1.7.1 Tareas y Dashboards con Azure DevOps
        • 3.1.7.2 Consultar tus tareas desde Visual Studio Community
      • 3.1.8 Trabajando con Ramas (Branches)
        • 3.1.8.1 Crear la rama desarrollo desde Azure DevOps
          • 3.1.8.1 Crear una rama(branch) desde tu tarea en Azure DevOps
        • 3.1.8.2 Como trabajar con ramas (branches) desde Visual Studio Community
        • 3.1.8.3 Crear el Pull Request con Azure DevOps
      • 3.1.9 Retrospectiva del Sprint
      • 3.1.10 Agregando una Wiki
    • 3.2 Integrando tu código fuente a GitHub
      • 3.2.1 Trabajando con ramas en GitHub
      • 3.2.2 Sincronizar los cambios del código con GitHub y Visual Studio
      • 3.3 Trabajando con Branches (Ramas) con Visual Studio
        • 3.3.1 Branches con Visual Studio
        • 3.3.2 Creando un template para tus PR (Pull Request)
        • 3.3.3 Protegiendo tu branch
  • 4. Creando tu primer servicio
    • 4.1 Crear las base de datos y los usuarios en MySQL
    • 4.2 Crear la tabla Categoría y sus validaciones
    • 4.3 Creando el servicio Categorias
    • 4.4 Probando tus servicios con POSTMAN
    • 4.5 Documentar y Probar tus servicios con Swagger
      • 4.5.1 Configurar Swagger
      • 4.5.2 Comentarios XML
      • 4.5.3 Generando la página de documentación
    • 4.6 Agregando índices
    • 4.7 Mejorando tu código
      • 4.7.1 Creando Objetos de Accesos a Datos
      • 4.7.2 Creando tus mensajes de error en diferentes idiomas
      • 4.7.3 Cambiando el formato del Json de los servicios
  • 5. Agregando el servicio para los productos
    • 5.1 Crear la tabla de Productos
    • 5.2 Formas de cargar información de tablas relacionadas
    • 5.3 Crear llaves fóraneas e índices
    • 5.4 Creando el servicio Productos
    • 5.5 Validar Reglas Mejorando tu código
      • 5.5.1 Agregando una excepción a todos nuestros servicios
      • 5.5.2 Agregando clases genéricas para validar y/o consultar información
      • 5.5.3 Alternativa para validar reglas con ef core
  • 6. Cambiar de base de datos a SQL Server
    • 6.1 Cambiar la base de datos a SQL Server
    • 6.2 Cambiar a SQL Server en Azure
  • 7. Crear servicios con OData
    • 7.1.1 Creando el modelo Clientes
    • 7.1.2 Creando el modelo ClienteCategoría
    • 7.1.3 Agregando paquete Nuget para OData
    • 7.1.4 Configurar el EDM Model
    • 7.1.5 Configurar el servicio OData y llaves foráneas
    • 7.1.6 Creando el Controller para clientes
    • 7.1.7 Configurar y probar los servicios con OData
    • 7.1.8 Práctica Crear el servicio para ClientesCategorias
    • 7.1.9 Recomendaciones de seguridad y rendimiento a tomar en cuenta con OData
  • 8. GraphQL
    • 8.1.1 Creando la tabla Caducidad
    • 8.1.2 Creando el query
    • 8.1.3 Configurando y probando graphQL
    • 8.1.4 Creando la Mutation
    • 8.1.5 Probando nuestros servicios con Postman
  • 9. Seguridad
    • 9.1 Json Web Tokens
    • 9.2 Seguridad basada en roles y usuarios
      • 9.2.1 Creando nuestra tabla roles e insertando los roles principales
      • 9.2.2 Consideraciones de seguridad para almacenar tus passwords
      • 9.2.3 Creando las tablas para manejar la seguridad
      • 9.2.4 Agregando usuarios y roles
    • 9.3 Agregando seguridad a nuestros servicios
    • 9.4 Creando nuestro servicio de login y generar el token
    • 9.5 ¿Cómo agregar seguridad basada en roles a los Servicios REST?
      • 9.5.1 Seguridad basada en claims
      • 9.5.2 Creando las tablas para validar permisos por cada tabla
      • 9.5.3 Seguridad basada en directivas
      • 9.5.4 Seguridad con Action Filters
    • 9.6 Guardando el historial de cambios
    • 9.7 Refrescando tu token
    • 9.8 Seguridad Mejorando tu código
      • 9.8.1 ¿Cómo limitar el número de intentos incorrectos en el login?
      • 9.8.2 ¿Cómo obtener la ciudad del usuario por medio de la IP?
      • 9.8.3 Habilitando CORS
  • 10. Pruebas Unitarias
    • 10.1 Agregando el proyecto de pruebas unitarias
    • 10.2 Crear una prueba unitaria
      • 10.2.1 Ejecutando las pruebas unitarias
    • 10.3 Agregando una base de datos en memoria para nuestras pruebas unitarias
    • 10.4 Agregando la referencia de nuestro proyecto CaducaRest
      • 10.4.1 Agregando paquetes nuget necesarios
    • 10.5 Configurando Clases para Objetos Sustitutos
      • 10.5.1 Configurando el Contexto para utilizar la base de datos en Memoria
      • 10.5.2 Configurando el objeto para sustituir mensajes de Error por idioma
    • 10.6 Agregando pruebas para las Categorías
  • 11. Integración continua
    • 11.1 ¿Qué es la integración continua?
    • 11.2 Subir tu código fuente a BitBucket
      • 11.2.1 Integración continua y pruebas automáticas con Bitbucket
    • 11.3 Integración continua y pruebas automáticas en Azure DevOps
  • 12. Pruebas de integración
    • 12.1 ¿Qué es SpecFlow?
    • 12.2 Agregando el proyecto de pruebas de integración
    • 12.3 Configurando Specflow
    • 12.4 Creando pruebas para el login
    • 12.5 Agregando las pruebas de Integración a Azure Devops
    • 12.6 Specflow Mejorando tu código
      • 12.6.1 Cambiando las pruebas a español
      • 12.6.2 Pasando tablas a nuestras pruebas
      • 12.6.3 Agregar los passwords como variables de ambiente
      • 12.6.4 Probando con SQLite
      • 12.6.5 Agregando diferentes parámetros con MSTest
      • 12.6.6 Generando el reporte living doc de specflow
  • 13. Integración continua con Postman
    • 13.1 Recomendaciones para probar tus servicios
    • 13.2 Instrucciones básicas para probar con Postman
    • 13.3 Crear colecciones en Postman
    • 13.4 Agregar pruebas a tus servicios
    • 13.5 Crear environments
    • 13.6 Agregando datos de prueba con archivos .csv
    • 13.7 Exportando tus colecciones y ejecutarlas con Newman.
    • 13.8 Agregando las colecciones de postman al pipeline
  • 14. Pruebas de usuario
    • 14.1 Page Object Model
    • 14.2 ¿Qué es Selenium?
      • 14.2.1 Selenium Instrucciones básicas
      • 14.2.2 Agregando el proyecto de pruebas de usuario
    • 14.3 ¿Qué es Cypress?
      • 14.3.1 Cypress Instrucciones básicas
    • 14.4 ¿Qué es Playwright?
      • 14.4.1 Playwright Instrucciones básicas
  • 15. Despliegue Continuo con Azure DevOps y Azure
    • 15.1 Crear un App Service en Azure
    • 15.2 Generando Artifacts en Azure Pipelines
    • 15.3 Generando el Release en Azure Pipelines al App Service de Azure
    • 15.4 Ejecutando las colecciones de Postman después del release
    • 15.5 Agregando las pruebas de usuario en Azure Pipelines
  • 16. Instalación en Windows Server e IIS
    • 16.1 Instalar IIS en Windows Server
    • 16.2 Instalación del ASP.NET Core Module/Hosting Bundle
    • 16.3 Crea el Sitio Web en IIS
  • 17. Instalación en Linux
    • 17.1 Creando una máquina virtual linux en Azure
    • 17.2 Habilitando el acceso remoto
    • 17.3 Configura linux para .Net Core
    • 17.4 Instalando mysql
    • 17.5 Instalando Nginx y configurando tu servicio
    • 17.6 Instalando un certificado SSL gratuito con CertBot
    • 17.7 Agregando diferentes subdominios
  • 18. Docker
Powered by GitBook
On this page
  • S - Single responsibility principle (SRP)
  • O - Open/Closed principle (OCP)
  • L - Liskov substitution principle
  • I - Interface segregation principle
  • D - Dependency Inversion Principle

Was this helpful?

  1. 2. Explicación de Conceptos

2.6 Código Limpio

Previous2.5 Estructura de los Proyectos .Net CoreNext2.7 Programación asíncrona

Last updated 2 years ago

Was this helpful?

A lo largo de este tutorial vamos a modificar el código para generar un código más fácil de entender y de probar.

Robert C. Martin escribió un libro () para explicar cómo crear un código limpio para que sea más fácil de entender y entender por otros programadores. Otro libro también muy bueno es el siguiente . Un libro enfocado en C# aunque no esta tan actualizado es:

Algunas de sus sugerencias son:

  • Escribir nombres de variables y funciones descriptivos.

  • En lugar de repetir código crear funciones.

  • Que tus funciones no tengan muchas líneas de código. Alrededor de 10 líneas máximo .

  • Tus funciones no deben tener más de 3 parámetros.

  • El código debe ser suficientemente claro para que no necesite comentarios.

  • Evita utilizar números que signifiquen algo como 0 Cancelado 1 Activo 2 Vendido, es mejor utiilzar Enumeraciones.

  • Que tus funciones hagan lo que dice de acuerdo al nombre. Por ejemplo una función que se llame enviaEmail y que aparte de email imprima un recibo y guarde en base de datos.

  • No almacenar passwords en el código.

También explica unos principios llamados SOLID los cuales son:

S - Single responsibility principle (SRP)

Este principio indica que cada clase o función debe realizar solamente una actividad. Por ejemplo. el método insertar de una clase categoría solo debe insertar no debe tener parámetro por ejemplo acción el cual indique que la función debe insertar o modificar una categoría. O una clase que guarde en base de datos una factura y la imprima, se debe tener 2 clases una para comunicarse con la base de datos y otra para imprimir.

O - Open/Closed principle (OCP)

Tú código debe ser abierto a ser extendido por otros programadores para que lo puedan ajustar a sus necesidades y debe ser cerrado para que no necesites cambiar tu código para adecuarlo a las necesidades de los demás programadores.

Por ejemplo puedes tener un clase que calcule los impuestos de acuerdo al tipo de producto. Unos productos tienen un impuesto donde debes pagar el 16% del valor del producto como impuesto, otro impuesto puede ser de forma fija por ejemplo cada que compres el producto debes pagar $4.00 de impuesto. El código podría ser el siguiente

public class Producto
{
    public int Clave;
    public decimal Precio;
    public decimal Tasa;
    public int TipoImpuesto;
}

public class Impuesto
{
    public decimal CalculaImpuesto(List<Producto> productos)
    {
        decimal total = 0;
        foreach (var producto in productos)
        if (producto.TipoImpuesto == 1)
            total += producto.Precio * producto.Tasa;
        else
            total +=  producto.Tasa;
        return total;
    }
}

Si luego surge un nuevo tipo de impuesto que sea una combinación de los dos, tendrías que modificar tu clase para agregar el nuevo tipo de impuesto y pasar el cambio a todos los programadores que estén utilizando tu clase. Con el principio abierto cerrado esto no es necesario.

Para esto usa el concepto de interfaces el cual te permite definir las propiedades y métodos comunes a todos los objetos.

//La interface contiene los métodos comunes
public interface IProducto
{
   int Clave { get; set; }
   decimal Precio { get; set; }
   decimal Tasa { get; set; }
       
   decimal CalculaImpuesto();
}
//Al crear una clase que implementa la interfaz defines 
//la forma de calcular el impuesto para este tipo de
//producto
public class ProductoPorcentaje : IProducto
{
    public int Clave { get; set; }
    public decimal Precio { get; set; }
    public decimal Tasa { get; set; }
    
    public decimal CalculaImpuesto()
    {
        return Precio * Tasa;
    }
}

public class ProductoCuota : IProducto
{
     public int Clave { get; set; }
     public decimal Precio { get; set; }
     public decimal Tasa { get; set; }
     
     public decimal CalculaImpuesto()
     {
         return Tasa;
     }
}
//Esta clase permite calcular el impuesto de una lista de productos
public class Impuesto
{
     public decimal CalculaImpuesto(List<IProducto> productos)
     {
         decimal total = 0;
         foreach (var producto in productos)
              total+= producto.CalculaImpuesto();
         return total;
     }
}

Aunque parece que es mucho más código de esta forma se cumple el principio 1, 2 en el cual cada clase solo tiene un objetivo y si luego surgen nuevos tipos de impuestos solo creas una clase que implemente la interfaz producto y no tienes que pedirle a la persona que diseño la clase Impuesto que agregue el cálculo cuando surja un nuevo tipo de impuesto. Si luego surgen muchos tipos de impuestos diferentes tendrías un código muy largo lleno de cases o de ifs

Este es un ejemplo sencillo ya un ejemplo más complicado es un método que borre un registro de acuerdo a su Id, el código siempre es el mismo buscar el registro por su id y luego borrarlo, lo único que cambiamos es el nombre de la tabla.

L - Liskov substitution principle

Este principio indica que cuando una clase hereda de otra clase se pueden sustituir sin que el funcionamiento sea diferente.

Continuando con el ejemplo anterior si creas otra clase que herede de ProductoPorcentaje la función CalculaImpuesto deberéa regresar el impuesto con la misma fórmula Precio * Tasa y no deberia hacer algo diferente como Precio * Tasa + Tasa ya que el funcionamiento seria diferente y se debería crear otra clase nueva que implemente la interfaz IProducto.

// Esta clase no cumple con el principio
public class ProductoPorcentaje : ProductoPorcentaje
{
    public int Clave { get; set; }
    public decimal Precio { get; set; }
    public decimal Tasa { get; set; }
    
    public decimal CalculaImpuesto()
    {
        return Precio * Tasa + Tasa;
    }
}

// Esta otra clase no cumple con el principio ya que se modifican
// las propiedades de la clase Base de una forma diferente 
// al funcionamiento esperado
public class ProductoPorcentaje : ProductoPorcentaje
{
    public int Clave { get; set; }
    public decimal Precio { get; set; }
    public decimal Tasa { get; set; }
    
    public ProductoPorcentaje()
    {
       Precio = Precio + Tasa;
       Tasa = Tasa * 2
    }
    public decimal CalculaImpuesto()
    {
        return Precio * Tasa;
    }
}

En el proyecto utilizaremos este principio para cambiar en enitity framework que en lugar de utilizar una base de datos de mysql utilice una de sql server o una base de datos en memoria.

El funcionamiento del código es el mismo, por lo cual podemos cambiar de base de datos utilizando el paquete nuget correspondiente.

I - Interface segregation principle

Este principio indica que las interfaces solo deben tener los métodos y propiedades que realmente son necesarios. Así los programadores que implementan tu interfaz solo implementan los métodos que son útiles para ellos.

Continuando con nuestro ejemplo nuestra interfaz IProducto si necesitamos tener productos que si manejan control de existencias es decir que no se pueden vender si no tienen existencias y productos sin existencia como por ejemplo la compra de un dominio de internet, sería un error agregar a nuestra interfaz IProducto un campo que diga existencia, lo mejor seria tener 2 interfaces una para productos con existencia otra para productos sin existencia

public interface IProducto
{
    int Clave { get; set; }
    decimal Precio { get; set; }
    decimal Tasa { get; set; }
    int existencia { get; set; }
    
    decimal CalculaImpuesto();
    //No cumple con el principio porque obligas 
    //a los programadores a implementar este método
    //cuando no lo necesitan
    int ObtieneExistencia();
}

public class ProductoExistencia : IProducto
{
     public int Clave { get; set; }
     public decimal Precio { get; set; }
     public decimal Tasa { get; set; }
     public int existencia { get; set; }

     public decimal CalculaImpuesto()
     {
         return Tasa;
     }

     public int ObtieneExistencia()
     {
          //Cálculos para obtener existencia         
          return existencia;
     }
}

public class ProductoVirtual : IProducto
{
      public int Clave { get; set; }
      public decimal Precio { get; set; }
      public decimal Tasa { get; set; }
      public int existencia { get; set; }

      public decimal CalculaImpuesto()
      {
           return Tasa;
      }
      //Los programadores tienen que escribir el código de este método
      //aunque no lo necesiten
      public int ObtieneExistencia()
      {
            return 0;
      }
}

D - Dependency Inversion Principle

Este principio indica que los módulos de alto nivel no deben depender de módulos de bajo nivel. Ya que si cambias una clase mas sencilla debes cambiar la clase mas complicada.

Veamos un ejemplo, tenemos una clase casa, la cual incluye un objeto de tipo Puerta y Ventana

public class Puerta
{
}

public class Ventana
{
}

public class Casa
{
   private Puerta _puerta;
   private Ventana _ventana;
   
   public Casa()
   {
       _puerta = new Puerta();
       _ventana = new Ventana();
   }
}

Esta clase no cumple con este principio ya que si luego necesitamos otro tipo de puerta o de ventana tenemos que modificar la clase casa, para esto lo mejor es crear interfaces para la clase Puerta y la clase Ventana y pasar en el constructor un objeto puerta y un objeto ventana para personalizar la casa. Este principio se utiliza mucho en .NET Core para pruebas unitarias

public class Casa
{
   private IPuerta _puerta;
   private IVentana _ventana;
   
   public ICasa(IPuerta puerta, IVentana ventana)
   {
       _puerta = puerta;
       _ventana = ventana;
   }
}

Nuestro ejemplo del principio 2 también cumple con este principio ya que pasamos la lista de productos como parámetro en lugar de que la clase cree los productos.

public class Impuesto
{
     public decimal CalculaImpuesto(List<IProducto> productos)
     {
         decimal total = 0;
         foreach (var producto in productos)
              total+= producto.CalculaImpuesto();
         return total;
     }
}

En el proyecto utilizamos este principio para pasar un objeto para guardar el log de errores y para poder cambiar la base de datos y para validar los permisos a los servicios.

Clean Code
Clean Architecture: A Craftsman's Guide to Software Structure and Design (Robert C. Martin Series)
Clean Code in C#: Refactor your legacy C# code base and improve application performance by applying best practices