Saltar al contenido

Etiqueta: develop

Integrando AWS Lambda a un Pipeline de Azure Devops

Hola, espero que estén bien. Este post es una continuación al post anterior, en el cual revisamos como podemos realizar el deploy de un api en un AWS Lambda. Ahora seguiremos con lo que es más lógico que suceda luego de hacer esto, automatizar este proceso. En post anteriores, he mostrado el uso de Azure Devops para los procesos de devops que uso, veamos como realizamos en este caso.

Primero, debemos tener creado un rol en IAM para que nuestro pipeline de devops se pueda authenticar. Con esto realizado, ingresar a azure devops y nos dirigimos a la siguientes opción.

Configuramos una nueva credencial
Elegimos AWS

Con esto hecho, vamos a AWS para crear un bucket, que es donde configuraremos luego para que nuestra pipeline deje los archivos.

Creamos un nuevo bucket

En nuestro pipeline, vamos a seleccionar la siguiente tarea.

Tarea a seleccionar
Información a completar

Para que funcione nuestro proceso de devops, es necesario crear una serie de archivos en nuestro proyecto, son dos los archivos, el primero es “serverless.template” y “aws-lambda-tools-defaults.json”. Les dejo el detalle de la información que va en cada archivo y les comento cual es la información que debe variar.

serverless.template

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.",
  "Parameters": {},
  "Conditions": {},
  "Resources": {
    "AspNetCoreApi": {
      "Type": "AWS::Serverless::Api",
      "Properties": {
        "StageName": "Prod",
        "BinaryMediaTypes": [
          "*~1*"
        ]
      }
    },
    "AspNetCoreFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Handler": "Exphadis.Services.WebApi::Exphadis.Services.WebApi.LambdaEntryPoint::FunctionHandlerAsync",
        "Runtime": "dotnetcore3.1",
        "CodeUri": "",
        "MemorySize": 512,
        "Timeout": 30,
        "Role": null,
        "Policies": [
          "AWSLambda_FullAccess"
        ],
        "Environment" : {
          "Variables" : {
            "ASPNETCORE_ENVIRONMENT" : "",
            "SENTRY_CONNECTION" : "",
            "DATABASE_CONNECTION" : "",
            "JWT_KEY" : "",
            "JWT_EXPIRATION" : ""
          }
        },
        "Events": {
          "ProxyResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/{proxy+}",
              "Method": "ANY",
              "RestApiId": {
                "Ref": "AspNetCoreApi"
              }
            }
          },
          "RootResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/",
              "Method": "ANY",
              "RestApiId": {
                "Ref": "AspNetCoreApi"
              }
            }
          }
        }
      }
    }
  },
  "Outputs": {
    "ApiURL": {
      "Description": "Site for Exphadis Support",
      "Value": {
        "Fn::Sub": "https://${AspNetCoreApi}.execute-api.${AWS::Region}.amazonaws.com/site/"
      }
    }
  }
}

Consideren, que deben modificar de esta plantilla:

  • Handler
  • Environment – Variables

aws-lambda-tools-defaults.json

{
    "Information" : [
        "All the command line options for the Lambda command can be specified in this file."
    ],
    "profile"     : "default",
    "region"      : "us-east-1",
    "configuration" : "Release",
    "framework"     : "netcoreapp3.1",
    "s3-prefix"     : "exphadis-service/",
    "template"      : "serverless.template",
    "template-parameters" : "",
    "s3-bucket"           : "exphadis-service",
    "stack-name"          : "Exphadis-Service-Serverless"
}

Consideren modificar la información que visualizan, de-acuerdo al escenario que tengan.

Veamos como tendría que quedar nuestro archivo “pipeline.yml”.

trigger:
  - feature/deploy-lambda

pool:
  vmImage: 'windows-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:
  - task: LambdaNETCoreDeploy@1
    name: 'Deploy_Lambda'
    displayName: 'Deploy to lambda'
    inputs:
      awsCredentials: 'AWS Admin'
      regionName: 'us-east-1'
      command: 'deployServerless'
      packageOnly: false
      lambdaProjectPath: '$(System.DefaultWorkingDirectory)\\Exphadis.Services.WebApi'
      stackName: 'Exphadis-Service-Serverless'
      s3Bucket: 'exphadis-service'
      s3Prefix: 'Exphadis.Services.WebApi/'


Notemos especificamente estas partes.

Información importante

Fuera de elegir las credenciales que ya hemos configurado previamente, la región, es necesario indicar:

  • lambdaProjectPath: La ruta en nuestro proyecto donde está nuestro entrypoint para lambda.
  • stackName: El nombre de nuestro lambda
  • s3Bucket: El nombre del bucket en S3 que hemos creado
  • s3Prefix: El prefijo de la carpeta en la que se va a desplegar nuestra proyecto compilado.

Probemos ahora toda nuestra configuración.

Despliegue en lambda desde azure devops

Finalmente, validamos en AWS este cambio.

Publicación visualizada desde AWS
Consumo del api

La demo de todo lo anteriormente explicado, pueden revisarla en el siguiente video.

Espero que les sea util, si tienen alguna duda, los espero en la sección de comentarios.

Saludos.

Deja un comentario

Publicando NetCore Api en AWS Lambda y AWS API Gateway

Hola, en un par de post anteriores les explicaba como dockerizar un entorno de trabajo:

Primera parte

Segunda parte

Hoy quiero explicarles como podemos desplegar nuestro api (para este ejercicio en NetCore 3.1) en AWS Lambda, usando un AWS API Gateway para poder consumir los recursos.

Empezamos en nuestra aplicación en visual studio.

Aplicación en visual studio

Notemos que en esta aplicación hemos modificado nuestro archivo “Program.cs” y los hemos renombrado “LocalEntryPoint.cs” y además hemos añadido un archivo “LambdaEntryPoint.cs”. Sobre el primer archivo no entrare en detale por que es el tipico archivo que tenemos cuando creamos un api en netcore. Pero el segundo si nos interesa, dado que ese es el handler que usará nuestro lambda. Veamos entonces, que nos encontramos.

Contenido de LambdaEntryPoint

Este archivo hereda de “API GatewayProxyFunction”, veremos más adelante que se usa un método dentro de esta clase para el handler, por lo demás estamos usando el overload de uno de los “Init”, especificamente el que tiene como entrada un “IWebHostBuilder” que es una interfaz ya conocida. Ahora vemos que tenemos que hacer en AWS. Como nota aparte, les comento que las demos las estoy usando la web de AWS. Sin embargo, todo esto puede ser reemplazada por la configuración desde la consola para crear los recursos y conectarlos entre si. Sin embargo, pienso que las demás son más dinámicas de este modo.

CREACIÓN DEL LAMBDA

Creación del lambda

Configuramos el handler

Handler por defecto
Nuevo hanlder

El handler que vamos a usar, se configura del siguiente modo.

Exphadis.Services.WebApi::Exphadis.Services.WebApi.LambdaEntryPoint::FunctionHandlerAsync

Donde, el orden es el siguiente:

Orden de elementos para el handler
  1. La ruta del proyecto en la cual esta ubicado
  2. El namespace de nuestra clase
  3. El nombre de la clase
  4. Un método dentro de la clase de la cual estamos heredando

Subimos nuestro código fuente

Realizamos un build de nuestras fuentes y comprimimos
Seleccionamos subir .zip
Seleccionamos nuestro archivo

CREACIÓN DEL API GATEWAY

Crear nueva api
Seleccionamos api rest
Finalizamos la creación

Vamos a configurar nuestro API Gateway para que consuma los recursos de nuestro lambda.

Creamos un nuevo recurso
Configuramos el recurso como un proxy
Indicamos el ARN de nuestro lambda

Mapeamos el ARN de nuestro lambda en el api gateway y aceptamos que nuestro api gateway pueda consumir nuestro lambda.

Brindamos permisos al lambda
Realizamos el deploy del api
Ponemos el nombre del stage que deseemos
Obtenemos el url de nuestro api

En este punto, para mi caso, es necesario configurar algunas variables de entorno, dado que sin esto va a fallar nuestra ejecución. Para poder hacer esto, debemos ir a nuestro lambda, a configuración y adicionar las variables de entorno.

Configuramos variables de entorno, de ser necesario

Finalmente, vamos a realizar la prueba de uno de los siguientes endpoint que tiene nuestro api. En mi caso es un get a un “health” que me permite obtener la fecha y hora del servidor y también de la base de datos, esto con la finalidad de determinar si todo esta funcionando correctamente. El endpoint a consumir sería el siguiente.

Respuesta del endpoint

Si quisieramos ver el log de nuestro lambda, tenemos que ir a la opción “Monitor”, y luego ir a cloudwatch a ver los LogGroups que se crean cada ves que consumimos nuestro api.

Opción de monitor en nuestro lambda
Cloudwatch logs
Ejecución de nuestro lambda

Finalmente, sólo voy a comentar que a este endpoint que hemos generado en AWS API Gateway, podemos mapearlo a otros domains, para esto tenemos una opción que se llama “Custom Domain Names”, en esta tenemos que indicar cual es el domain que queremos, además nos permite mapearlo a algún certificado que hayamos solicitado previamente para que nuestra api se pueda servir sobre https.

Por otro lado, como explico un poco en el video adjunto en este post, nuestro lambda es manejado enteramente por AWS, sin embargo, nosotros podemos agregar “layers” para poder realizar ajustes sobre nuestro ambiente, en tre los cuales se pueden manejar la instalación de otras librerías en nuestro ambiente de trabajo.

Finalmente, en un siguiente post, voy a mostrar como podemos integrar este flujo en un pipeline de devops con azure devops.

Espero que esten bien, cuidense.

Saludos.

Deja un comentario

Dockerizando una aplicación Net 5.0 – Part. II

Este post es una continuación de su versión anterior, en este nos enfocaremos en como configuramos nuestro ambiente de desarrollo con docker.

Retomemos en el código del proyecto.

Código del proyecto

Vamos a modificar nuestro “launchSetting.json”, eliminemos los profiles que tenemos actualmente, creemos uno nuevo para manejar nuestra instancia en docker que tendrá la siguiente estructura.

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:61567/",
      "sslPort": 44304
    }
  },
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "Docker": {
      "commandName": "Docker",
      "launchBrowser": true,
      "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
      "publishAllPorts": true,
      "useSSL": true,
      "httpPort": 7550,
      "sslPort": 7551,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "SENTRY_CONNECTION": "",
        "DATABASE_CONNECTION": "",
        "JWT_KEY": "",
        "JWT_EXPIRATION": "",
        "URL_BASE_PATH": "",
        "AWS_ACCESS_KEY": "",
        "AWS_SECRET_KEY": "",
        "EMAIL_NO_REPLY": "",
        "ASPNETCORE_URLS": "http://+:8080;https://+:443"
      }
    }
  }
}
Nueva configuración en launchSettings.json

Lo que hemos hecho es crear un nuevo perfil, le indicamos el comando que va a ejecutar “docker”, que debe abrir un navegador, la url que se va a abrir (esta como vemos se obtiene de variables, no valores en duro), un poco de dolor de cabeza me llevo descubrir el uso de “httpPort” y “sslPort”, dado que estos dos nos permiten indicarle que puertos queremos que use. Esto ultimo parece algo menor, pero nos sirve cuando mapeamos en nuestro postman o en el front el puerto del backend, y que este sea consistente en distintos ambientes, de otro modo, nos asignarán un puerto aleatorio y esto nos generar tener que estar cambiando nuestra configuración en el front. Finalmente, acá también podemos indicarle las variables de entorno que vamos a usar dentro de nuestro dockerfile.

En nuestro ide, selecionamos “Docker” y ejecutamos el proyecto. Si es la primera ves que se ejecuta, se va a crear la imagen en base a nuestro dockerfile, luego de esto, el ide automaticamente va a crear una instancia en base a esta imagen. Veamos que comando es el que usa para ejecutar este comando.

Primero se hace el build:

docker build -f "C:\Users\walbe\Development\Simplicity\simplicity-api\dockerfile-dev" --force-rm -t simplicityserviceapi:dev --target base  --label "com.microsoft.created-by=visual-studio" --label "com.microsoft.visual-studio.project-name=Simplicity.Service.Api" "C:\Users\walbe\Development\Simplicity"
Docker build

Luego realizamos se realiza el run de nuestra imagen recién creada:

docker run -dt -v "C:\Users\walbe\vsdbg\vs2017u5:/remote_debugger:rw" -v "C:\Users\walbe\Development\Simplicity\simplicity-api\Simplicity.Service.Api:/app" -v "C:\Users\walbe\Development\Simplicity:/src/" -v "C:\Users\walbe\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" -v "C:\Users\walbe\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" -v "C:\Users\walbe\.nuget\packages\:/root/.nuget/fallbackpackages2" -v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_LOGGING__CONSOLE__DISABLECOLORS=true" -e "ASPNETCORE_ENVIRONMENT=Development" -e "ASPNETCORE_HTTPS_PORT=7551" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -p 7550:8080 -p 7551:443 -P --name Simplicity.Service.Api_1 --entrypoint tail simplicityserviceapi:dev -f /dev/null
Docker run
docker run -dt 
-v "C:\Users\walbe\vsdbg\vs2017u5:/remote_debugger:rw" 
-v "C:\Users\walbe\Development\WebApplication1:/app" 
-v "C:\Users\walbe\Development\WebApplication1:/src/" 
-v "C:\Users\walbe\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" 
-v "C:\Users\walbe\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" 
-v "C:\Users\walbe\.nuget\packages\:/root/.nuget/fallbackpackages2" 
-v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" 
-e "DOTNET_USE_POLLING_FILE_WATCHER=1" 
-e "ASPNETCORE_LOGGING__CONSOLE__DISABLECOLORS=true" 
-e "ASPNETCORE_ENVIRONMENT=Development" 
-e "ASPNETCORE_URLS=https://+:443;http://+:80" 
-e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" 
-e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" 

Con esto, ya tendriamos todo funcionando correctamente, les dejo un video de todo este proceso, donde podemos ver mejor como nuestra aplicación corre desde nuestra imagen docker y los cambios que realizamos se actualizan directamente.

Espero que les sirva, en el siguiente post explicare como realizar el deploy de una aplicación en NetCore o Net 5.0 en AWS Lambda.

Deja un comentario

Liskov Substitution Principle

El tercer principio que compone SOLID es el Liskov Substitution Principle, y este nos plantea

Plantea una guía de cuando debemos usar herencia en POO.

Let Φ (x) be a property provable about objects x of type T. Then Φ (y) should be true for objects y of type S where S is a subtype of T.

Barbara Liskov, 1988

Este principio, podemos entenderlo en su modo más simple de la siguiente forma “Las clases derivadas, deben poder ser sustituidas por sus clases base”.

Para ejemplificar este principio, usaremos el típico ejemplo del rectángulo y el cuadrado. Veamos el proyecto que tenemos definido:

Nuestra clase base es el rectángulo:

Clase base rectángulo

Queremos calcular el área de nuestro rectángulo o las clases que lo implementen, como un cuadrado. Para esto, creamos el siguiente método:

Método para hallar el área

Digamos que ahora necesitamos implementar un cuadrado, y con esta finalidad creamos la siguiente clase:

Clase del cuadrado

Como ven, el cuadrado implementa nuestro rectángulo y lo que hacemos es definir en sus propiedades que las propiedades base (las que esta implementado) son iguales en su ancho y altura, que como sabemos corresponden a los lados de un cuadrado. Si probamos obtener el cuadrado de ambas clases, tendremos el siguiente resultado:

Resultado al obtener el área sin probar el principio Liskov

Notese que se esta creando una nueva instancia del Square y por esto obtenemos los resultados correctos. Sin embargo, si buscamos poner en práctica lo definido por el principio liskov, veamos que resultado obtenemos.

Resultado poniendo a prueba el principio Liskov

Lo que sucede es que nuestra clase Rectangle, es la que es inicializada, por lo tanto sólo posee el Width igual a 4, y no el Height. Cuando se busca obtener el área realiza la multiplicación del ancho por la altura y tenemos como resultado 0. Para poder respetar este principio, es necesario que realicemos el siguiente cambio:

Cambios para respetar el principio Liskov

Lo que hacemos es declarar como virtuales las propiedades de nuestra clase base, por lo que cuando se incialice y el compilador determine que es virtual, va a buscar algún override que es justo lo que hacemos en la clase derivada. De este modo, sabe que debe usar la propiedad de Square e iguala los lados a 4 que es lo que definimos en el Width. Si probamos ahora nuestro método, tendremos el siguiente resultado:

Prueba final del cálculo del área

Donde como se ve, ya se respeta el principio que hemos tocado ampliamente en este post. Espero que les ayude a entender un poco más este principio, así mismo comentarles que este es el tercer post de los principios que componen SOLID. Como siempre, dejo el video del desarrollo de esta post y el link al repositorio en github.

REPO: GITHUB

Listado de POST sobre SOLID anteriores:

Deja un comentario

Open Close Principle

Siguiendo con la linea de post que presentan los principios que componen marco de buenas prácticas SOLID. El que vamos a desarrollar en este post es el “Open Close Principle”, esto hace referencia a que el código que desarrollamos debería estar abierto a ser extendido y cerrado a las modificaciones.

Sin más, veamos el ejemplo que vamos a desarrollar en este post:

Supongamos que estamos desarrollando un sistema para un almacen, en el cual se manejan productos. Estos productos, poseen características, las cuales pueden ser:

Clase de Productos

En donde los “Enums” mostrados poseen los siguientes elementos:

Clase de Enums

Digamos que por necesidad del negocio, nos vemos obligados a filtrar los productos que vamos almacenando por su color, teniendo esto en mente, creamos una nueva clase que llamaremos “ProductFilter”.

De este modo, si queremos filtrar un grupo de colores, por un color en particular deberíamos hacer algo muy similar a esto:

Filtro por color los productos

De este modo, tenemos 3 productos, los cuales se agregan a una lista y esta lista se proporciona al método de filtrado que hemos creado. Si ejecutamos esta aplicación, tenemos el siguiente resultado:

Filtrado por color a los productos
Filtrado Correcto

Como podemos ver, se ejecuta sin problemas. El problema viene cuando requerimos realizar más filtros. Digamos que ahora nos indican que el filtrado también sea por tamaño, o por color y tamaño, terminariamos llenando nuestra clase “ProductFilter” de métodos. Algo muy similar a esto:

Llenado de métodos para diversos tipos de filtros

Como ven, esto rompe claramente el principio de Open Close Principle. Deberíamos de poder extender nuestros filtros, sin afectar la clase que ya se tiene, esto se puede conseguir a través de interfaces. En este caso, para respetar este principio voy a usar el “specification pattern”. Veamos como podemos implementarlo y respetar este principio.

Es necesario creas dos interfaces para poder llevar a cabo la implementación de este patrón de diseño, siendo la primera una que nos permitirá definir la especificación, cuando esta va a estar satisfecha y el tipo de elemento que recibe como tipo de entrada y la segunda espara implementar el filtro y como este se lleva a cabo.

Interfaz IFilter
Interfaz ISpecification

Ahora, implementemos el primero de los filtros, este es es el “ColorSpecification”:

ColorSpecification

Como pueden ver, implementamos la interfaz definida en el paso anterior, tenemos una variable que se inicializa en el constructor, que para el filtro es el color y en el método “IsSatisfied” solo se retorna true si el color del producto que nos proporcionan es igual al que se inicializa en el constructor.

Para poder usar este filtro es necesario implementar también la interfaz “IFilter”, lo cual realizamos del siguiente modo:

Implementación del IFilter

Del mismo modo que en la implementación anterior, se se le indica el tipo de elemento con el cual se desea filtrar, iteramos sobre la lista de estos elementos y solo solo cuando la especificación haya sido satifecha retornamos, el “yield” lo usamos por la eficiencia en el recorrido de nuestra lista.

Veamos como usamos este filtro en nuestra clase principal y que resultado obtenemos:

Uso del filtro
Resultado del nuevo filtrado

Finalmente, mostrare como se puede realizar el más de un filtro a la vez, para ejemplificar esto, creare un filtro por tamaño, siguiendo el mismo esquema ejemplificado anteriormente. Para poder adicionar más de un filtro es necesario crear una nueva clase pero ahora su tipo también será genérico, del siguiente modo:

AndSpecification para realizar más de un filtro

Inicializamos dos variables en el constructor, ambas del tipo “ISpecification”, y sólo se satisface cuando ambas variables lo están. Para consumir este método, debemos de hacerlo del siguiente modo:

Consumiendo más de un filtro a la vez

Se le tienen que indicar los filtros que se quieren realizar y al consumir este filtro en el cual buscamos un producto del color verde y de tamaño pequeño, nuestro resultado es el siguiente:

Resultado de ambos filtros

Espero que les ayude a entender un poco más este principio, así mismo comentarles que este es el segundo post de los principios que componen SOLID. Como siempre, dejo el video del desarrollo de esta post y el link al repositorio en github.

REPO: GITHUB

Listado de POST sobre SOLID anteriores:

Deja un comentario

Single Responsibility Principle

Ya hace un tiempo llevo trabajando en una empresa en el sector financiero, en la cual como toda empresa de su envergadura el stack de tecnologías y aplicaciones que poseen es diverso. Y como comprenderan eso tarde o temprano nos llevaba tener que trabajar con legacy code, y las serie de retos que estos involucran. Más de una vez, he tenido que atajar métodos de más de 1000 líneas de código que tenían como objetivo extornar algún monto, grabar log, etc. Esto se vuelve un dolor se cabeza al querer realizar pruebas unitarias para que sean parte del pipeline que se ejecuta en un ambiente de integración y despliegue continuo.

Este problema pone en evidencia la importancia de desarrollar teniendo siempre en mente la utilización de patrones de diseño. Esto me lleva a tratar en este post el primero de los principios de SOLID, siendo los principios que lo componen no los únicos que existen, pero si un marco de buenas prácticas en el desarrollo de software que nos permite asegurar un nivel de calidad en lo que desarrollamos.

Single Responsability Principle representa la “S” en SOLID, este principio entre otras cosas nos indica que los métodos que desarrollamos deben poseer una sola reponsabilidad y por lo tanto un sólo motivo para cambiar. Cuando nos referimos a responsabilidad, esta puede tomar la forma de las siguiente maneras:

Tipos de responsabilidad
  • Grabar en una base de datos, archivo de texto o cualquier otro medio de persistencia que se desee.
  • Realizar un log del método que se está consumiendo y/o también de los errores que puedan ocurrir.
  • Validar la información ingresada al método.
  • Lógica de negocio.

En lineas generales, debemos buscar que el código que desarrollemos tenga el menor acoplamiento posible y la única manera de conseguirlo es definiendo adecuadamente las responsabilidades. Una buena manera de probar que tanto acoplamiento posee nuestro código es cuantos test unitarios es necesario desarrollar para poder coberturarlo al 100%, es muy usual que si nuestro método desarrollado tiene muchas responsabilidades, el poder realizarle pruebas unitarios se va a volver algo extremadamente completo y tedioso. Para poder probar este patrón de diseño, vamos a reformular código teniendo en cuenta las consideraciones antes mencionadas.

La aplicación que vamos a refactorizar nos permite obtener el cálculo de ratio para la realización de la contratación de una poliza, tenemos un método de más de 100 lineas de código que posee distintas responsabilidades, tal como podemos ver en la siguiente imagen:

Código a refactorizar
  1. Grabación del log cuando inicia la llamada al método.
  2. Lectura de un archivo de texto y conversión a un objeto.
  3. Validación de la data obtenida.
  4. Lógica de negocio.
  5. Persistencia de la información obtenida en un archivo de texto.
  6. Grabación del log cuando finaliza la llamada al método.

Como podemos ver, es necesario reformular el método para que cumpla con los requerimientos descritos por este patrón. Esto lo realizaremos extrayendo ciertas responsabilidades del método principal. El primero nos permitirá realizar el grabado de logs:

Método para grabar logs

Luego, desarrollaremos un método para leer el archivo de texto, el cual debe poder retornar la entidad que le pasemos, por lo tanto es debe ser genérico.

Método para leer de un archivo de texto y retornar una entidad

Además, es necesario extraer el grabado de la persistencia en un método, este también debe aceptar como variables de entradas genericos.

Grabado de la persistencia

Finalmente, quedando el método del siguiente modo:

Método refactorizado

Notese que también he separado en un método distinto las validaciones de las polizas de vida.

Espero que les ayude a entender un poco más este principio, así mismo comentarles que este es el primer post de los principios que componen SOLID. Como siempre, dejo el video del desarrollo de esta post y el link al repositorio en github.

REPO: GITHUB

2 comentarios