

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Implementar el patrón saga sin servidor mediante AWS Step Functions
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions"></a>

*Tabby Ward, Joe Kern y Rohan Mehta, Amazon Web Services*

## Resumen
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-summary"></a>

En una arquitectura de microservicios, el objetivo principal es crear componentes disociados e independientes para promover la agilidad, la flexibilidad y reducir el tiempo de comercialización de sus aplicaciones. Como resultado del desacoplamiento, cada componente del microservicio tiene su propia capa de persistencia de datos. En una arquitectura distribuida, las transacciones comerciales pueden abarcar varios microservicios. Como estos microservicios no pueden utilizar una sola transacción de atomicidad, coherencia, aislamiento y durabilidad (ACID), es posible que acabe con transacciones parciales. En este caso, se necesita alguna lógica de control para deshacer las transacciones que ya se han procesado. El patrón saga distribuido se utiliza normalmente para este propósito. 

El patrón saga es un patrón de gestión de fallos que ayuda a establecer la coherencia en las aplicaciones distribuidas y coordina las transacciones entre varios microservicios para mantener la coherencia de los datos. Si se utiliza el patrón «saga», cada servicio que realiza una transacción publica un evento que desencadena que los servicios subsiguientes realicen la siguiente transacción de la cadena. Esto continúa hasta que se complete la última transacción de la cadena. Si una transacción comercial fracasa, Saga organiza una serie de transacciones compensatorias que anulan los cambios introducidos en las transacciones anteriores.

Este patrón demuestra cómo automatizar la configuración y el despliegue de una aplicación de muestra (que gestiona las reservas de viajes) con tecnologías sin servidor, como AWS Step Functions, AWS Lambda y Amazon DynamoDB. La aplicación de ejemplo también utiliza Amazon API Gateway y Amazon Simple Notification Service (Amazon SNS) para implementar un coordinador de ejecución de saga. El patrón se puede implementar con un marco de infraestructura como código (IaC), como el AWS Cloud Development Kit (AWS CDK), el AWS Serverless Application Model (AWS Serverless Application Model SAM) o Terraform.

## Requisitos previos y limitaciones
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-prereqs"></a>

**Requisitos previos**
+ Una cuenta de AWS activa.
+ Permisos para crear una CloudFormation pila de AWS. Para obtener más información, consulte [Controlar el acceso](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-template.html) en la CloudFormation documentación.
+ El marco de IaC de su elección (AWS CDK, AWS SAM o Terraform) se configuró con su cuenta de AWS para que pueda usar la CLI del marco para implementar la aplicación.
+ NodejS, utilizado para compilar la aplicación y ejecutarla localmente. 
+ Un editor de código de su elección (como Visual Studio Code, Sublime o Atom).

**Versiones de producto**
+ [NodeJS versión 14](https://nodejs.org/en/download/)
+ [AWS CDK versión 2.37.1](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)
+ [AWS SAM versión 1.71.0](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)
+ [Terraform versión 1.3.7](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)

**Limitaciones**

El abastecimiento de eventos es una forma natural de implementar el patrón de orquestación de la saga en una arquitectura de microservicios en la que todos los componentes están acoplados de forma flexible y no se conocen directamente entre sí. Si su transacción incluye un número reducido de pasos (de tres a cinco), el patrón saga podría ser una buena opción. Sin embargo, la complejidad aumenta con el número de microservicios y el número de pasos. 

Las pruebas y la depuración pueden resultar difíciles cuando se utiliza este diseño, ya que es necesario tener todos los servicios en ejecución para poder simular el patrón de transacciones.

## Arquitectura
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-architecture"></a>

**Arquitectura de destino**

La arquitectura propuesta utiliza AWS Step Functions para crear un patrón de saga para reservar vuelos, reservar alquileres de vehículos y procesar los pagos de las vacaciones.

El siguiente diagrama de flujo de trabajo ilustra el flujo típico del sistema de reservas de viajes. El flujo de trabajo consiste en reservar un viaje en avión (» ReserveFlight «), reservar un automóvil (» ReserveCarRental «), procesar los pagos (» ProcessPayment «), confirmar las reservas de vuelos (» ConfirmFlight «) y confirmar el alquiler de vehículos (» ConfirmCarRental «), seguido de una notificación de éxito cuando se hayan completado estos pasos. Sin embargo, si el sistema detecta algún error al ejecuter alguna de estas transacciones, empezará a fallar hacia atrás. Por ejemplo, un error en el procesamiento del pago (» ProcessPayment «) desencadena un reembolso (» RefundPayment «), que luego desencadena la cancelación del coche de alquiler y del vuelo (» CancelRentalReservation "y CancelFlightReservation «), lo que finaliza toda la transacción con un mensaje de error.

Este patrón implementa funciones Lambda independientes para cada tarea que se resalta en el diagrama, así como tres tablas de DynamoDB para vuelos, alquileres de vehículos y pagos. Cada función de Lambda crea, actualiza o elimina las filas de las tablas de DynamoDB respectivas, en función de si la transacción se confirma o se revierte. El patrón utiliza Amazon SNS para enviar mensajes de texto (SMS) a los suscriptores y notificarles las transacciones fallidas o satisfactorias. 

![\[Flujo de trabajo para un sistema de reservas de viajes basado en el patrón saga.\]](http://docs.aws.amazon.com/es_es/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/daad3e8e-6e6b-41c2-95c1-ca79d53ead64.png)


 

**Automatización y escala**

Puede crear la configuración de esta arquitectura mediante uno de los marcos de IaC. Utilice uno de los siguientes enlaces para su iAC preferido.
+ [Implementar con AWS CDK](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [Implementar con AWS SAM](https://serverlessland.com/workflows/saga-pattern-sam)
+ [Implementar con Terraform](https://serverlessland.com/workflows/saga-pattern-tf)

## Tools (Herramientas)
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-tools"></a>

**Servicios de AWS**
+ [AWS Step Functions](https://aws.amazon.com/step-functions/) es un servicio de orquestación sin servidor que le permite combinar funciones de AWS Lambda y otros servicios de AWS para crear aplicaciones esenciales desde el punto de vista empresarial. A través de la consola gráfica Step Functions, puede ver el flujo de trabajo de su aplicación como una serie de pasos basados en eventos.
+ [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) es un servicio de base de datos NoSQL totalmente administrado que ofrece un rendimiento rápido y predecible, así como una perfecta escalabilidad. Puede utilizar DynamoDB para crear una tabla de base de datos capaz de almacenar y recuperar cualquier cantidad de datos, así como de atender cualquier nivel de tráfico de solicitudes.
+ [AWS Lambda](https://aws.amazon.com/lambda/) es un servicio de computación que permite ejecutar código sin aprovisionar ni administrar servidores. Lambda ejecuta su código solo cuando es necesario y escala de manera automática, desde unas pocas solicitudes por día hasta miles por segundo.
+ [Amazon API Gateway](https://aws.amazon.com/api-gateway/) es un servicio de AWS para crear, publicar, mantener, supervisar y proteger REST, HTTP y WebSocket APIs a cualquier escala.
+ [Amazon Simple Notification Service (Amazon SNS)](https://aws.amazon.com/sns/) es un servicio administrado que proporciona la entrega de mensajes de los publicadores a los suscriptores.
+ El [AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/) es un marco de desarrollo de software para definir los recursos de las aplicaciones en la nube mediante lenguajes de programación conocidos TypeScript, como Python JavaScript, Java y C\$1/.Net.
+ [AWS Serverless Application Model (AWS SAM)](https://aws.amazon.com/serverless/sam/) es un marco de código abierto que permite crear aplicaciones sin servidor. Proporciona una sintaxis abreviada para expresar funciones, bases de datos y mapeos de fuentes de APIs eventos. 

**Código**

El código de una aplicación de ejemplo que muestra el patrón saga, incluida la plantilla IaC (AWS CDK, AWS SAM o Terraform), las funciones de Lambda y las tablas de DynamoDB, se encuentra en los siguientes enlaces. Siga las instrucciones de la primera epic para instalarlos.
+ [Implementar con AWS CDK](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [Implementar con AWS SAM](https://serverlessland.com/workflows/saga-pattern-sam)
+ [Implementar con Terraform](https://serverlessland.com/workflows/saga-pattern-tf)

## Epics
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-epics"></a>

### Instalar paquetes, compilar y compilar
<a name="install-packages-compile-and-build"></a>


| Tarea | Descripción | Habilidades requeridas | 
| --- | --- | --- | 
| Instalar los paquetes NPM. | Cree un directorio nuevo, navegue hasta ese directorio en una terminal y clone el GitHub repositorio que prefiera en la sección de *código* que aparece anteriormente en este patrón.En la carpeta raíz que contiene el archivo `package.json`, ejecute el siguiente comando para descargar e instalar todos los paquetes de Node Package Manager (NPM):<pre>npm install</pre> | Desarrollador, arquitecto de la nube | 
| Compilar scripts. | En la carpeta raíz, ejecute el siguiente comando para indicar al TypeScript transpilador que cree todos los archivos necesarios: JavaScript <pre>npm run build</pre> | Desarrollador, arquitecto de la nube | 
| Esté atento a los cambios y vuelva a compilar. | En la carpeta raíz, ejecute el siguiente comando en una ventana de terminal independiente para comprobar si hay cambios en el código y compílalo cuando lo detecte:<pre>npm run watch</pre> | Desarrollador, arquitecto de la nube | 
| Ejecute pruebas unitarias (solo AWS CDK).  | Si utiliza el CDK de AWS, en la carpeta raíz, ejecute el siguiente comando para realizar las pruebas unitarias de Jest:<pre>npm run test</pre> | Desarrollador, arquitecto de la nube | 

### Implemente recursos en la cuenta de AWS de destino
<a name="deploy-resources-to-the-target-aws-account"></a>


| Tarea | Descripción | Habilidades requeridas | 
| --- | --- | --- | 
| Implemente la pila de demostración en AWS. | La aplicación es independiente de la región de AWS. Si utiliza un perfil, debe declarar la región de forma explícita en el perfil de la [interfaz de la línea de comandos de AWS (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) o mediante variables de [entorno de la CLI de AWS](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html).En la carpeta raíz, ejecute el siguiente comando para crear un ensamblaje de implementación e implementarlo en la cuenta y región de AWS predeterminadas.AWS CDK:<pre>cdk bootstrap<br />cdk deploy</pre>AWS SAM:<pre>sam build<br />sam deploy --guided</pre>Terraform:<pre>terraform init<br />terraform apply</pre>Este paso puede tardar varios minutos en completarse. Este comando usa las credenciales predeterminadas que se configuraron para la AWS CLI.Anote la URL de API Gateway que se muestra en la consola una vez completada la implementación. Necesitará esta información para probar el flujo de ejecución de la saga. | Desarrollador, arquitecto de la nube | 
| Compare la pila implementada con el estado actual. | En la carpeta raíz, ejecute el siguiente comando para comparar la pila implementada con el estado actual después de realizar cambios en el código fuente:AWS CDK:<pre>cdk diff</pre>AWS SAM:<pre>sam deploy</pre>Terraform:<pre>terraform plan</pre> | Desarrollador, arquitecto de la nube | 

### Pruebe el flujo de ejecución
<a name="test-the-execution-flow"></a>


| Tarea | Descripción | Habilidades requeridas | 
| --- | --- | --- | 
| Probar el flujo de ejecución de la saga. | Navegue hasta la URL de API Gateway que indicó en el paso anterior, cuando implementó la pila. Esta URL activa el inicio de la máquina de estados. Para obtener más información sobre cómo manipular el flujo de la máquina de estados pasando diferentes parámetros de URL, consulte la sección de [Información adicional](#implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional).Para ver los resultados, inicie sesión en la consola de administración de AWS y vaya a la consola de Step Functions. Aquí puede ver todos los pasos de la saga State Machine. También puede ver la tabla de DynamoDB para ver los registros insertados, actualizados o eliminados. Si actualiza la pantalla con frecuencia, puede ver cómo el estado de la transacción cambia de a`pending`. `confirmed` Puede suscribirse al tema de las redes sociales actualizando el código del archivo `stateMachine.ts` con su número de teléfono móvil para recibir mensajes SMS cuando las reservas se hayan realizado o no se hayan realizado correctamente. Para obtener más información, consulte *Amazon SNS* en la sección [Información adicional](#implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional). | Desarrollador, arquitecto de la nube | 

### Limpieza
<a name="clean-up"></a>


| Tarea | Descripción | Habilidades requeridas | 
| --- | --- | --- | 
| Limpiar recursos. | Para limpiar los recursos desplegados para esta aplicación, puede usar uno de los siguientes comandos.AWS CDK:<pre>cdk destroy</pre>AWS SAM:<pre>sam delete</pre>Terraform:<pre>terraform destroy</pre> | Desarrollador de aplicaciones, arquitecto de la nube | 

## Recursos relacionados
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-resources"></a>

**Documentos técnicos**
+ [Implementación de microservicios en AWS](https://docs.aws.amazon.com/pdfs/whitepapers/latest/microservices-on-aws/microservices-on-aws.pdf)
+ [Aplicaciones sin servidor](https://docs.aws.amazon.com/wellarchitected/latest/serverless-applications-lens/welcome.html)

**Documentación de servicio de AWS**
+ [Introducción a los AWS SDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html)
+ [Introducción a AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started.html)
+ [AWS Step Functions](https://docs.aws.amazon.com/step-functions/)
+ [Amazon DynamoDB](https://docs.aws.amazon.com/dynamodb/)
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/)
+ [Amazon API Gateway](https://docs.aws.amazon.com/apigateway/)
+ [Amazon SNS](https://docs.aws.amazon.com/sns/)

**Tutoriales**
+ [Talleres prácticos sobre informática sin servidor](https://aws.amazon.com/serverless-workshops/)

## Información adicional
<a name="implement-the-serverless-saga-pattern-by-using-aws-step-functions-additional"></a>

**Código**

Con fines de prueba, este patrón implementa API Gateway y una función de Lambda de prueba que activa la máquina de estados Step Functions. Con Step Functions, puede controlar la funcionalidad del sistema de reservas de viajes pasando un `run_type` parámetro para imitar los errores en «ReserveFlightReserveCarRental,»»» ProcessPaymentConfirmFlight,»» y «»ConfirmCarRental.

La `saga`función de Lambda (`sagaLambda.ts`) toma la entrada de los parámetros de consulta de la URL de la API Gateway, crea el siguiente objeto JSON y lo pasa a Step Functions para su ejecución:

```
let input = {
"trip_id": tripID, //  value taken from query parameter, default is AWS request ID
"depart_city": "Detroit",
"depart_time": "2021-07-07T06:00:00.000Z",
"arrive_city": "Frankfurt",
"arrive_time": "2021-07-09T08:00:00.000Z",
"rental": "BMW",
"rental_from": "2021-07-09T00:00:00.000Z",
"rental_to": "2021-07-17T00:00:00.000Z",
"run_type": runType // value taken from query parameter, default is "success"
};
```

Puede experimentar con diferentes flujos de la máquina de estados Step Functions pasando los siguientes parámetros de URL:
+ **Ejecución correcta**: https://\$1api gateway url\$1
+ Error en la **reserva del vuelo** ─ https://\$1api gateway url\$1? **Tipo de ejecución = failFlightsReservation**
+ **Confirma el error del vuelo** ─ https://\$1api gateway url\$1? **Tipo de ejecución = failFlightsConfirmation**
+ Error al **alquilar un coche al reservar** ─ https://\$1api gateway url\$1? **RunType = Reserva failCarRental**
+ **Confirme que el alquiler del vehículo ha fallado** ─ https://\$1api gateway url\$1? **RunType = Confirmación failCarRental**
+ **Error al procesar el pago**: https://\$1api gateway url\$1?**runType=failPayment**
+ **Pasar un identificador de viaje**: https://\$1api gateway url\$1? **tripID=** \$1de forma predeterminada, el ID de viaje será el ID de solicitud de AWS\$1

**Plantillas de iAC**

Los repositorios enlazados incluyen plantillas de IaC que puede utilizar para crear toda la aplicación de reserva de viajes de muestra.
+ [Implementar con AWS CDK](https://serverlessland.com/workflows/saga-pattern-cdk)
+ [Implementar con AWS SAM](https://serverlessland.com/workflows/saga-pattern-sam)
+ [Implementar con Terraform](https://serverlessland.com/workflows/saga-pattern-tf)

**Tablas de DynamoDB**

Estos son los modelos de datos para las tablas de vuelos, alquileres de coches y pagos.

```
Flight Data Model:
 var params = {
      TableName: process.env.TABLE_NAME,
      Item: {
        'pk' : {S: event.trip_id},
        'sk' : {S: flightReservationID},
        'trip_id' : {S: event.trip_id},
        'id': {S: flightReservationID},
        'depart_city' : {S: event.depart_city},
        'depart_time': {S: event.depart_time},
        'arrive_city': {S: event.arrive_city},
        'arrive_time': {S: event.arrive_time},
        'transaction_status': {S: 'pending'}
      }
    };

Car Rental Data Model:
var params = {
      TableName: process.env.TABLE_NAME,
      Item: {
        'pk' : {S: event.trip_id},
        'sk' : {S: carRentalReservationID},
        'trip_id' : {S: event.trip_id},
        'id': {S: carRentalReservationID},
        'rental': {S: event.rental},
        'rental_from': {S: event.rental_from},
        'rental_to': {S: event.rental_to},
        'transaction_status': {S: 'pending'}
      }
    };

Payment Data Model:
var params = {
      TableName: process.env.TABLE_NAME,
      Item: {
        'pk' : {S: event.trip_id},
        'sk' : {S: paymentID},
        'trip_id' : {S: event.trip_id},
        'id': {S: paymentID},
        'amount': {S: "750.00"}, // hard coded for simplicity as implementing any monetary transaction functionality is beyond the scope of this pattern
        'currency': {S: "USD"},
        'transaction_status': {S: "confirmed"}
      }
    };
```

**Funciones de Lambda**

Se crearán las siguientes funciones para respaldar el flujo y la ejecución de la máquina de estados en Step Functions:
+ **Reservar vuelos**: introduzca un registro en la tabla de vuelos de DynamoDB con `transaction_status` un `pending` de para reservar un vuelo.
+ **Confirmar vuelo**: actualiza el registro de la tabla de vuelos de DynamoDB para establecer `transaction_status` en `confirmed`, para confirmar el vuelo.
+ **Cancelar reserva de vuelos**: elimina el registro de la tabla de vuelos de DynamoDB para cancelar el vuelo pendiente.
+ **Reserve vehículos de alquiler**: inserta un registro en la tabla de CarRentals DynamoDB con `transaction_status` un `pending` de para reservar un alquiler de vehículos.
+ **Confirmar alquileres de vehículos**: actualiza el registro de la tabla de CarRentals DynamoDB para `transaction_status` `confirmed` establecerlo en y confirmar el alquiler de vehículos.
+ **Cancelar reserva de vehículos de alquiler:** elimina el registro de la tabla de CarRentals DynamoDB para cancelar el alquiler de vehículos pendiente.
+ **Procesar pago**: inserta un registro en la tabla de pagos de DynamoDB para el pago.
+ **Cancelar pago**: elimina el registro del pago de la tabla de pagos de DynamoDB.

**Amazon SNS**

La aplicación de ejemplo crea el tema y la suscripción siguientes para enviar mensajes SMS y notificar al cliente si las reservas se han realizado o no se han realizado correctamente. Si desea recibir mensajes de texto mientras prueba la aplicación de ejemplo, actualice la suscripción de SMS con su número de teléfono válido en el archivo de definición de la máquina de estados.

Fragmento de CDK de AWS (añada el número de teléfono en la segunda línea del siguiente código):

```
const topic = new  sns.Topic(this, 'Topic');
topic.addSubscription(new subscriptions.SmsSubscription('+11111111111'));
const snsNotificationFailure = new tasks.SnsPublish(this ,'SendingSMSFailure', {
topic:topic,
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
message: sfn.TaskInput.fromText('Your Travel Reservation Failed'),
});
 
const snsNotificationSuccess = new tasks.SnsPublish(this ,'SendingSMSSuccess', {
topic:topic,
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
message: sfn.TaskInput.fromText('Your Travel Reservation is Successful'),
});
```

Fragmento de SAM de AWS (sustituya las cadenas `+1111111111` por su número de teléfono válido):

```
  StateMachineTopic11111111111:
    Type: 'AWS::SNS::Subscription'
    Properties:
      Protocol: sms
      TopicArn:
        Ref: StateMachineTopic
      Endpoint: '+11111111111'
    Metadata:
      'aws:sam:path': SamServerlessSagaStack/StateMachine/Topic/+11111111111/Resource
```

Fragmento de Terraform (sustituya la cadena `+111111111` por su número de teléfono válido):

```
resource "aws_sns_topic_subscription" "sms-target" {
  topic_arn = aws_sns_topic.topic.arn
  protocol  = "sms"
  endpoint  = "+11111111111"
}
```

**Reservas realizadas satisfactoriamente**

En el siguiente flujo se muestra una reserva correcta con los caracteres "ReserveFlight,»ReserveCarRental,» y "" seguidos de ProcessPayment "" y ConfirmFlight "». ConfirmCarRental Se notifica al cliente que la reserva se ha realizado correctamente mediante mensajes SMS que se envían al suscriptor del tema de las redes sociales.

![\[Ejemplo de una reserva exitosa implementada por Step Functions mediante el patrón saga.\]](http://docs.aws.amazon.com/es_es/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/f58c894e-7721-4bc7-8f7d-29f23faa5dc1.png)


**Reservas fallidas**

Este flujo es un ejemplo de fracaso en el patrón de la saga. Si, después de reservar vuelos y alquileres de vehículos, "ProcessPayment" falla, los pasos se cancelan en orden inverso.  Se cancelan las reservas y se notifica al cliente del error mediante mensajes SMS que se envían al suscriptor del tema de las redes sociales.

![\[Ejemplo de una reserva fallida implementada por Step Functions mediante el patrón saga.\]](http://docs.aws.amazon.com/es_es/prescriptive-guidance/latest/patterns/images/pattern-img/fec0789c-d9b1-4d80-b179-dd9a7ecbec07/images/7c64d326-be27-42c3-b03f-d677efedb9a7.png)
