Aprendiendo Serverless Framework (Parte 2): Introducción, Instalación y Ejemplos Básicos

Publicado por Víctor Madrid el

Arquitectura de SolucionesServerlessServerless Framework

En este segundo artículo de la serie "Aprendiendo Serverless Framework" se va a detallar cómo realizar la instalación de la herramienta/plataforma Serverless, los conceptos básicos, una configuración básica y dos ejemplos prácticos basados en su uso.

A modo de recordatorio pongo los enlaces a los artículos anteriores:

Este artículo está dividido en 4 partes:

1. Serverless Framework: introducción a esta herramienta, su instalación, sus conceptos básicos y, lo que es más importante, su uso.

2. Ejemplo Práctico : "Hello World!".

3. Ejemplo Práctico : "Invocar a una API Externa".

4. Conclusiones : Opinión sobre lo que se ha explicado.

1. Serverless Framework

https://serverless.com/

Se trata de una Herramienta CLI Open Source que proporciona comandos para facilitar ciertas operativas a la hora de realizar la construcción, configuración, implementación y despliegue de funciones serverless en varios proveedores (Amazon AWS, Microsoft Azure, Google Cloud, IBM Open Whisk, Kubeless, etc.).

Si has entendido de que va FaaS y has currado un poco con los diferentes proveedores Cloud habrás visto que aparecen una nueva serie de problemas hasta ahora desconocidos (aunque algunos tienen cierto olor a "déjà vu" de otras batallas arquitectónicas):

  • ¿Cómo configuro las lambdas / functions?
  • ¿Qué parámetros puedo utilizar? ¿Son los adecuados?
  • Vale... soy un crack configurando y ahora ... ¿Cómo los llamo? ¿Es el naming adecuado? ¿Dentro de 3 semanas los podré distinguir?
  • ¿Los gestiono "uno a uno" o "en grupo"? ¿Puedo enfocarlos como unidades funcionales o uso otro criterio?
  • ¿Y cambiar o toquetear algo de la infraestructura? ¿cómo me puedo enterar de que algo ha cambiado?
  • ¿Con qué región de Cloud me interesa trabajar?
  • ¿Puedo tener diferentes entornos? ¿Cómo lo puedo cambiar?
  • ¿Por dónde comienzo a la hora de empezar a desplegar?
  • ...

Anteriormente a su aparición todas estas operativas se realizaban manualmente mediante la consola (o bien mediante scripts "caseros" que automatizaban los pasos para los más vaguetes o para los que no nos gusta repetir las cosas 1000 veces ;-) ) y si tenías mucha suerte alguien en Internet quizás ya lo hubiera hecho (esto es tener suerte y saber buscar... jeje).

Todo este coste en tiempo y esfuerzo es más o menos asumible cuando se trabaja con uno o varios proyectos pequeños (incluye normalmente una función serverless pero algunos pueden tener varias). Os garantizo que cuando el nº de proyectos se incrementa en una cantidad importante la "película" cambia, aparecen las primeras lagrimitas y la sensación de que el tiempo se nos escapa... jejeje

Nota : Muchos de los proveedores Cloud suelen facilitar alguna herramienta CLI para poder trabajar con ellos, por lo que conviene revisar su documentación.

Características de Serverless Framework :

  • Proporciona una serie de comandos para realizar acciones completas.
  • Permite homogeneizar el mismo uso de acciones entre proyectos diferentes (aunque cada uno tenga sus propias características) -> usando los comandos.
  • Habilita reutilizar operativas entre proyectos.
  • Permite trabajar con diferentes infraestructuras Cloud o Proveedores.
  • Soporta diferentes lenguajes
  • Es Production-Ready.
  • Permite la configuración "offline".
  • Proporciona una "buena documentación".
  • Proporciona extensibilidad mediante plugins.
  • Permite trabajar con layers de AWS.
  • ...
Símil:  Serverless Framework - Maven
Serverless Framework vendría ser respecto a las operativas de aprovisionamiento y despliegue de componentes FaaS lo que Maven al proceso de construcción, empaquetado y despliegue de aplicaciones Java, es decir, una herramienta con unos comandos determinados que nos permiten realizar ciertas operativas con independencia de la tipología del proyecto -> Para ello establece un ciclo determinado de pasos.

1.1 Instalación y Configuración

Estos son los pasos para realizar la instalación:

  • 1.1.1 Prerrequisitos.
  • 1.1.2 Instalar Serverless Framework
  • 1.1.3 Configurar Credenciales del Proveedor
  • 1.1.4 Añadir dependencia cuando se trabaja con un proyecto
  • 1.1.5 Añadir scripts de acceso rápido a tareas de serverless [Opcional]
  • Depende del lenguaje que se utilice

1.1.1 Prerrequisitos

Instalar Node.js

Importante:
No te preocupes si no sabes de todo esto, todo aparece detallado en los ficheros README.md de los proyectos de ejemplo.
Nota: Serverless se ejecuta en Node v4 o superior https://nodejs.org/en/download/

Revisar la versión de node:

node --version  

1.1.2 Instalar Serverless Framework

Ejecutar el siguiente comando para instalar o actualizar el framework desde la última versión:

npm install -g serverless  

Nota : El ámbito de instalación es global al usar "-g" y hará uso de última versión disponible. En  caso de querer instalar la misma versión que la utilizada en este este tutorial ejecutar el siguiente comando:

npm install -g serverless@1.34.1  

Una vez instalado se puede acceder a su funcionalidad utilizando el comando: serverless o sls.

Revisar la versión instalada:

serverless --version  

1.1.3 Configurar las Credenciales del Proveedor

En este punto se debería de realizar una configuración especifica de los elementos relacionados con la cuenta del proveedor Cloud utilizado.

En el caso de AWS se proporciona el siguiente enlace: https://serverless.com/framework/docs/providers/aws/guide/credentials/

En el caso de Azure se proporciona el siguiente enlace: https://serverless.com/framework/docs/providers/azure/guide/credentials/

Para ver más proveedores mirar la página oficial.
Nota : Cada proveedor tiene sus particularidades.

1.1.4 Añadir la dependencia cuando se trabaja con un proyecto

En este caso se ha puesto como lenguaje de ejemplo Node.js, pero esto será similar en otro lenguaje utilizado.

Añadir al fichero 'package.json':

"devDependencies": {
    ...
    "serverless": "^1.34.1",
    ...
  }

O bien ejecutando: npm install serverless --save-dev (recordar lo de instalar una versión concreta).

En este caso se instalará la última versión disponible.

1.1.5 Añadir scripts de acceso rápido a tareas de serverless [Opcional]

En este caso se ha diseñado para el lenguaje Node.js.

Añadir tareas para su uso en el mismo fichero:

  • En este caso se añaden accesos rápidos a dos comandos para tener el control de su invocación desde Node.js y establecer algunos parámetros de ejecución.

Ejemplo de scripts añadidos:

"scripts": {
    ...
    "deploy": "sls deploy -v",
    "undeploy": "sls remove"
    ...
  },

deploy:  realiza un despliegue mediante serverless en la configuración proporcionada en su fichero serverless.yml.

npm run deploy  

undeploy:  eliminar un despliegue mediante serverless en la configuración proporcionada en su fichero serverless.yml.

$ npm run undeploy

1.2 Conceptos Básicos

En este punto se van a detallar los conceptos básicos a entender si uno quiere dominar Serverless Framework:

  • Fichero de control : serverless.yml.
  • Servicio (Service).
  • Propiedades (Properties).
  • Proveedor (Provider).
  • Función (Function).
  • Recursos (Resources).
  • Plugins (Plugins).

Fichero de control : serverless.yml

El framework normalmente hace uso de un fichero de control "serverless.yml" donde se detallan las definiciones del servicio, funciones, configuración, recursos, permisos, etc.

  • Registra todos los elementos que son necesarios para la definición del componente "FaaS".
  • Se suele ubicar en la raíz del proyecto.

Ejemplo de fichero "serverless.yml"

¿Se puede controlar la versión que se está ejecutando?

Se puede establecer un mecanismo de control de la versión del framework para un fichero concreto.

Esto se debe a que las últimas versiones pueden incluir o haber cambiado algo de funcionalidad y con ello se puede asegurar la compatibilidad del framework respecto al momento de desarrollo.

Se puede establecer una versión exacta "=" o bien un rango de versiones ">="

Para ello se suele incluir el siguiente atributo al fichero :

frameworkVersion: ">=1.34.1"  
...

En caso de ejecutar el fichero con una versión inferior, se mostrará un error.

Ejemplo del mensaje de error por problemas con la versión

Servicio (Service)

Hace referencia normalmente a un proyecto.

  • Representa la unidad "organizativa".
  • Importante el naming del proyecto -> suele ser el nombre con el que se desplegará.
  • Un servicio puede estar compuesto por una o varias funciones.
  • Una aplicación puede estar compuesta por uno o varios servicios (cada uno con su fichero serverless.yml).
  • Si se considera utilizar varios servicios se aconseja que cada uno disponga de su propio fichero -> Consideración de multiproyecto.

Ejemplo de la declaración de la información de un servicio

frameworkVersion: ">=1.31.1"

service:  
  name: demo-nodejs-hello-serverless
...
Importante : (Traducción desde la página oficial) Currently, every service will create a separate REST API on AWS API Gateway. Due to a limitation with AWS API Gateway, you can only have a custom domain per one REST API. If you plan on making a large REST API, please make note of this limitation. Also, a fix is in the works and is a top priority.
Actualmente, cada servicio creará una API REST separado en AWS API Gateway. Debido a una limitación con AWS API Gateway, sólo se puede tener un dominio personalizado por cada REST API. Si planea hacer una API REST de gran tamaño, tenga en cuenta esta limitación. Además, se está trabajando en una solución que es de máxima prioridad.

Propiedades (Properties)

Una propiedad es cada una de los atributos que se puede utilizar para establecer el valor de otro elemento del fichero , es decir, como si fuera una declaración de constantes o variables del proyecto.

  • Se referencia a cada una ellas mediante el uso de ${}.
  • Permite aplicar recursividad en sus llamadas.
  • Se pueden utilizar con ámbito global a todas las partes del fichero incluidas las funciones o bien se pueden definir unas propiedades específicas por función -> Recordar que existen las propiedades heredadas.
# Caso 1 : un único valor
${testValue}
# Caso 2 : un valor que de no estar definido utiliza un segundo valor
${testValue, 'test'}

Gracias a su tipología a la hora de utilizarlos se pueden conseguir configuraciones muy flexibles y dinámicas.

Existe cierta tipología que se verá a continuación :

  • Básica.
  • Opciones del CLI.
  • Variables de entorno.
  • Fichero de propiedades.
  • Otros.
Básica

Par clave-valor que se puede utilizar una sola vez o reutilizar en varios puntos. Su declaración sirve como punto para centralizar los valores de ciertos aspectos del desarrollo (valores por defecto, configuraciones, etc.) o bien del despliegue.

Por ejemplo :

custom:  
   # Global configuration
  config:
    stage-default : local
    region-default : us-east-1
...

En este caso se puede clasificar la información de forma estructurada para facilitar su comprensión, para ello se utiliza haciendo referencia a toda la estructura.

  • Cuando se quiere hacer referencia a un elemento del fichero se usa el prefijo : self.

Por ejemplo :

# Se quiere hacer uso de stage-default
${self:custom.config.stage-default}
...
Opciones del CLI

Paso de parámetros por línea de comandos para ser recuperados por el fichero mediante el prefijo opt  (dentro de las opciones permitidas).

Por ejemplo :

custom:  
  config:
    stage-default : local
    stage-active: ${opt:stage, self:custom.config.stage-default}
...

Truco : definir la elección de un parámetro y establecer un valor por defecto en caso de que no pueda ser leído o encontrado.

Por ejemplo usar el comando "deploy" con parámetros de entrada por línea de comandos:

# Caso 1: stage-active tiene el valor del stage pasado por parámetro
serverless deploy --stage prod --region us-east-1

# Caso 2: stage-active tiene el valor por defecto de la configuración
serverless deploy --region us-east-1  
...
Variables de entorno

Variable de entorno del sistema, así como proporcionar los medios para acceder y añadir nuevas variables

  • Cuando se quiere hacer referencia a una variable de entorno del ordenador se utiliza el prefijo env

Por ejemplo :

custom:  
  config:
    stage-default : ${env:STAGE_DEFAULT}
    stage-name : ${env:STAGE_DEFAULT}-name
...
Fichero de propiedades

Lectura de un fichero de propiedades y cargar las propiedades desde ese punto:

  • El fichero puede ser un YML o JSON.
  • Se pueden utilizar varios ficheros de configuración:
...
custom:  
  config:
    file: ${file(config/config.env.default.json)}

provider:  
  name: aws
  runtime: nodejs8.10
  stage: ${opt:stage, self:custom.config.file.STAGE}
...

Usando las variables adecuadas bien combinadas se puede disponer de una configuración específica por entorno

Ejemplo de propuesta:

...
custom:  
  config:
    stage-default : local
    file: ${file(config/config.env.${self:provider.stage}.json)}

provider:  
  name: aws
  runtime: nodejs8.10
  stage: ${opt:stage, self:custom.config.stage-default}
  environment:
    ENV: ${file(config/config.env.${self:provider.stage}.json):ENV} 
    DYNAMODB_TABLE: ${self:custom.config.file.DYNAMODB_TABLE}
...

Explicación del código anterior:

  • stage : se resuelve con el valor de línea de comandos o el valor por defecto y representa el entorno de ejecución.
  • file : se resuelve accediendo a un fichero en base al stage seleccionado, lo que supone que existe diferentes ficheros de propiedades en base al entorno de ejecución.
  • DYNAMODB_TABLE : se resuelve con la propiedad definida con el fichero concreto, por lo que sería el nombre que tiene la tabla utilizada en ese entorno de ejecución.

Otros en la versión actual del framework también se permite :

  • Referencias a salidas de CloudFormation.
  • Referencias a objetos de S3.
  • Referencias a AWS Secrets Manager.
  • Variables en ficheros JavaScript -> ${file(../myFile.js):someModule}.
  • ...

Proveedor  (Provider)

Punto de declaración de la configuración del proveedor, su infraestructura y los permisos a aplicar sobre los recursos declarada en el fichero de control serverless.yml en el apartado "provider":

  • Se indica el nombre del proveedor y detalles de su configuración como versión del lenguaje, memoria, etc.
  • La configuración y los permisos establecidos en este punto son heredados por todas las funciones.

Ejemplo de configuración específica

...
provider:  
  name: aws
  runtime: nodejs6.10
  memorySize: 512 # optional (1024 MB default)
  versionFunctions: false # optional default is true
...

Ejemplo de permisos

...
provider:  
  name: aws
  ...
  iamRoleStatements: 
    - Effect: Allow
      Action: 
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:us-east-1:*:*"
...

Funciones (Functions)

Cada una de las implementaciones de FaaS se declara en el fichero de control serverless.yml, dentro del apartado "functions".

Características :

  • Se suele considerar la unidad mínima de despliegue.
  • Un servicio puede estar compuesto por uno o más servicios.
  • Normalmente un servicio se encarga de abordar una única funcionalidad, pero en algunos casos puede realizar más de una funcionalidad.
  • Cada función declarada puede tener una configuración específica. Si no, la heredará de la configuración establecida en el provider o bien de los valores por defecto de ella.
Importante
Una función es invocada a partir de un evento que la dispara bajo demanda (por lo que es interesante conocer cómo son los eventos que utiliza el proveedor utilizado)

Cuando se define un evento para una función, el framework toma la responsabilidad de crear automáticamente cualquier infraestructura necesaria que necesite para dar solución a ese evento así como las configuraciones necesarias.

Ejemplo de orden y tipo de invocaciones (En este caso con origen en HTTP)

Algunos ejemplos de eventos pueden ser : HTTP, schedule, S3, SQS, etc.

Ejemplo de una función con una única funcionalidad en el fichero serverless.yml

...
functions:  
  createMessage: 
    handler: src/message.handler.create
    name: ${self:provider.stage}-lambdaName # optional
    description: Description # optional
    memorySize: 512 # optional (1024 MB) 
    events:
      - http:
          path: message
          method: post
...

Ejemplo de varias funciones con una única funcionalidad en el fichero serverless.yml

...
functions:  
  createMessage: 
    handler: src/message.handler.create
    events:
      - http:
          path: message
          method: post
  listMessages: 
    handler: src/message.handler.list.listMessages
    events:
      - http:
          path: message
          method: get

Ejemplo de una función con varias funcionalidades en el fichero serverless.yml

...
functions:  
  crudMessage: 
    handler: handler.crudMessage 
    events:
      - http:
          path: message
          method: post
      - http:
          path: message/all
          method: delete

Ejemplo de una función con eventos SQS en el fichero serverless.yml

...
functions:  
  commandReceiveMessage:
    handler: src/command.receiver.message.handler.receiveMessage
    events:
      - sqs:
          arn:
            Fn::GetAtt:
              - event-example-sqs
              - Arn   

Permite definir de forma básica:

  • Identificador: nombre con el que se identifica la función
  • Handler : Clase y método manejador específico
  • Configuración Específica: Propiedades que sólo afectan a esta función:
  • Indicador del tipo de evento con el que trabajar.
  • Elementos específicos de ese tipo de evento.
  • Permisos específicos si trabajaba con algún recurso.
  • ...

Existe la posibilidad de desacoplar su declaración del fichero serverless.yml pero yo no lo considero una buena práctica.

Recursos (Resources)

Cada uno de los recursos (componentes de infraestructura) que son utilizados por las funciones se declara en el fichero de control serverless.yml en el apartado "resources".

  • Puede haber uno o más recursos declarados.
  • Cuando se despliegan las funciones también se incluyen los recursos.
  • En AWS se suelen hacer uso de referencias a resource con CloudFormation.

Ejemplo de declaración de una tabla en DynamoDB

...
resources:  
  Resources:
    example-table:
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        TableName: example-table
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
    event-example-sqs:
      Type: "AWS::SQS::Queue"
      Properties:
        QueueName: ${self:provider.environment.EVENT_QUEUE}

Plugins

Módulo de código Javascript que permite crear y extender los comandos que utiliza sobre el framework.

Cada plugins tiene su forma de instalarse, aunque normalmente siguen los siguientes pasos:

  • Instalar plugins.
  • Añadir dependencia al proyecto.
  • Añadir su referencia en el fichero serverless.yml.
  • Capacidad para crear plugins custom.

Ejemplo de declaración de un plugin en el fichero serverless.yml

...
plugins:  
  - serverless-offline

1.3 Comandos

Como ya os he comentado Serverless Framework es una herramienta que proporciona una serie de comandos para hacer más agradable nuestra vida alrededor de serverless.

Para ver el listado de comandos disponibles para esta versión podemos ejecutar el siguiente comando:

serverless

o

sls  

Lista de comandos disponible:

Todos los comandos son importantes pero los que más se utilizan son:

  • create
  • package
  • deploy
  • invoke
  • remove

create

El framework proporciona un mecanismo de creación de plantillas mediante el comando : create:

  • Dispone de plantillas para múltiples lenguajes (Node.js, Python, Clojure, etc.)
  • Para el caso de Node.js facilita la creación de: handler.js, serverless.yml y package.json.
  • ...

Ejemplo de uso del comando

serverless create --template aws-nodejs --path exampleBasicService  

Se crea una plantilla customizada en el directorio indicado en el parámetro "--path" donde se ejecut. Ahí se añade un fichero serverless.yml relleno (con muchas partes comentadas) y una clase manejadora básica.

Ejemplo del código "plantilla" creado mediante este mecanismo para node

'use strict';

module.exports.hello = async (event, context) => {  
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};

package

El framework proporciona un mecanismo de empaquetado de la aplicación mediante el comando : package.

Ejemplo de uso del comando

serverless package 

Serverless: Packaging service...  
Serverless: Excluding development dependencies...  

Con eso se genera el empaquetado del proyecto que se trata de un ZIP con el nombre del proyecto dentro del directorio oculto ".serverless" del proyecto. Todo ello en base a unas plantillas basadas en CloudFormation de AWS ya que en ese caso sería el proveedor definido (estas plantillas se pueden ver dentro de ese directorio oculto).

Se puede establecer donde ubicar el artefacto, así como establecer qué ficheros o directorios incluye y cuáles serán excluidos.

  • Esto también se puede definir en base a un fichero JSON y no solo aplicable a a todas las funciones , sino que cada una puede tener definidos sus propios criterios.

Ejemplo de exclusión e inclusión de elementos

Nota: Tiene ya establecidos unos valores por defecto como: .gitignore, .serverless/**

service:  
  name: demo-nodejs-hello-serverless

package:  
  artifact: path/to/demo-nodejs-hello-serverless-NO-VALID.zip
  exclude:
    - "bin/**"
    - "resources/**"
    - "reports/**"
    - ".*"
    - "*.md"
    - "package-lock.json"
...

deploy

El framework proporciona un mecanismo de despliegue de la aplicación mediante el comando : deploy:

  • Para ello traduce su declaración del fichero al elemento encargado de usar plantillas en cada proveedor (Cloud Formation en AWS, Resource Manager en Azure, etc.)
  • Permite desplegar todas las funciones o bien elegir cual.
  • Se considera el método más seguro de despliegue (usar stage para decir el entorno).

Ejemplo general :

serverless deploy --verbose  
  • Con el parámetro --verbose se permite mostrar más información durante el proceso.
  • Con el parámetro --aws-s3-accelerate se puede acelerar el proceso de subida.

Ejemplo utilizando parámetros:

serverless deploy --verbose --stage prod --region us-east-1  
  • Si NO se indica stage por defecto se considerá "dev" a menos que exista algún cambio en el fichero serverless.yml.
  • Si NO se indica region se utilizaría la configurada por el proveedor.

Ejemplo desplegando una única función:

serverless deploy function --function createMessage --verbose  

Ejemplo desplegando en base a un empaquetado:

serverless deploy --package path-to-package  

Con este parámetro se estaría facilitando su integración en cualquier flujo de CI / CD, ya que el empaquetado se podría estar moviendo entre entornos hasta que se le diera la orden de despliegue.

invoke

El framework proporciona un mecanismo de invocación general y/o local de las funciones: invoke.

Ejemplo de invocación local

serverless invoke local function --function createMessage --log  

Ejemplo de invocación de una función desplegada

serverless invoke function --function createMessage --stage prod --region us-east-1  

remove

El framework proporciona un mecanismo de eliminación del despliegue en el proveedor: remove.

  • No afecta al directorio del servicio.

Ejemplo de eliminación

serverless remove -verbose  

Con el parámetro --verbose se permite mostrar más información durante el proceso. También se le pueden pasar parámetros sobre el stage o region con la que trabajar.

2. Ejemplo Práctico: AWS "Hello World!"

Muestra un mensaje "Hello World" (Todo un clásico de cualquier desarrollo) junto a información del evento que lo desencadeno a través de una petición REST (evento HTTP).

Stack Tecnológico:

Enlace al Repositorio Git

Nota: He preferido unificarlo por si se incrementa la serie de artículos ;-)

3. Ejemplo Práctico: AWS "Invocar a una API Externa"

Ejecuta un servicio de invocación REST (evento HTTP) a la API de mensajes públicos yesno.wtf y muestra su mensaje en una respuesta REST.

Stack Tecnológico:

Enlace al Repositorio Git

Nota: He preferido unificarlo por si se incrementa la serie de artículos ;-)

4. Conclusiones

Yo personalmente estoy encantado con esta "pedazo" de herramienta desde el momento que la medio entendí y la pude ver funcionando en un caso real.

Su punto fuerte es que aplica mucha "magia" durante su utilización y nos hace la vida un poco más fácil, pero esto a su vez puede ser un problema si no entendemos muy bien que conceptos aplica, las partes o acciones por las que pasa un componente FaaS antes de verlo funcionar, los eventos que ocurren, etc.

Otro de los aspectos a considerar es la capacidad de poder agregar plugins lo que permite que exista una comunidad que ayude a impulsar esto y sobre todo que aparezcan "funcionalidades" que nos hagan la vida todavía más fácil.

Como bien ya conté, este mundillo está dando sus pasitos en la dirección correcta pero todavía es "joven" por lo que todavía nos quedan muchas cosas que ver a todos los niveles ... espero que esta herramienta nos acompañe en esta caminata.

Si quieres estar al día de próximas entregas de esta serie, ¡síguenos en Twitter!

Autor

Víctor Madrid

Líder Técnico de la Comunidad de Arquitectura de Soluciones en atSistemas. Aprendiz de mucho y maestro de nada. Técnico, artista y polifacético a partes iguales ;-)