{"@attributes":{"version":"2.0"},"channel":{"title":"DEV Community: joserafaelSH","description":"The latest articles on DEV Community by joserafaelSH (@joserafaelsh).","link":"https:\/\/dev.to\/joserafaelsh","image":{"url":"https:\/\/media2.dev.to\/dynamic\/image\/width=90,height=90,fit=cover,gravity=auto,format=auto\/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1732441%2F031fc71c-56a7-4c51-8ac1-4391ba12f05c.jpg","title":"DEV Community: joserafaelSH","link":"https:\/\/dev.to\/joserafaelsh"},"language":"en","item":[{"title":"Transa\u00e7\u00f5es distribu\u00eddas em Microservices com Kafka","pubDate":"Sat, 17 Aug 2024 18:05:05 +0000","link":"https:\/\/dev.to\/joserafaelsh\/transacoes-distribuidas-em-microservices-com-kafka-6c5","guid":"https:\/\/dev.to\/joserafaelsh\/transacoes-distribuidas-em-microservices-com-kafka-6c5","description":"<h3>\n  \n  \n  Introdu\u00e7\u00e3o aos Microservices\n<\/h3>\n\n<p><strong>Microservices<\/strong> se trata de uma abordagem arquitet\u00f4nica e organizacional para o desenvolvimento de software que consiste em pequenas aplica\u00e7\u00f5es (servi\u00e7os) com suas APIs e comunica\u00e7\u00f5es bem definidas. Essa abordagem promove equipes e aplica\u00e7\u00f5es autossuficientes, especialistas em contextos espec\u00edficos e independentes.<\/p>\n\n<h3>\n  \n  \n  Benef\u00edcios dos Microservices\n<\/h3>\n\n<p>A utiliza\u00e7\u00e3o de microservices aparece para solucionar problemas como:<\/p>\n\n<ul>\n<li>\n<strong>Escalabilidade e Alta Disponibilidade:<\/strong> Com microservices aliados a estrat\u00e9gias de DevOps, \u00e9 poss\u00edvel identificar gargalos de forma facilitada, devido \u00e0 separa\u00e7\u00e3o de dom\u00ednios das aplica\u00e7\u00f5es, e aplicar escalabilidade horizontal apenas nos pontos de gargalo.<\/li>\n<li>\n<strong>Promo\u00e7\u00e3o de T\u00e9cnicas de CI\/CD:<\/strong> Como um mesmo sistema possui suas funcionalidades distribu\u00eddas em servi\u00e7os independentes, a integra\u00e7\u00e3o e implanta\u00e7\u00e3o de c\u00f3digo tamb\u00e9m ser\u00e3o independentes, permitindo deploys a qualquer momento de qualquer parte do sistema.<\/li>\n<li>\n<strong>Promo\u00e7\u00e3o do Desenvolvimento de Testes:<\/strong> Utilizando servi\u00e7os com contextos menores e bem definidos, os desenvolvedores ter\u00e3o um maior poder de teste, visto que n\u00e3o ter\u00e3o que realizar mocks de l\u00f3gicas alheias \u00e0 funcionalidade que est\u00e3o desenvolvendo.<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  Desafios dos Microservices\n<\/h3>\n\n<p>Apesar de solucionar alguns problemas, tamb\u00e9m surgem desafios como:<\/p>\n\n<ul>\n<li>\n<strong>Complexidade do Sistema:<\/strong> Um sistema monol\u00edtico, querendo ou n\u00e3o, \u00e9 mais simples que um sistema de microservices distribu\u00eddos, pois todas as funcionalidades e regras de neg\u00f3cio s\u00e3o concentradas em um s\u00f3 lugar, facilitando l\u00f3gicas de rollbacks, por exemplo.<\/li>\n<li>\n<strong>Pontos de Falhas:<\/strong> Quanto mais microservices o sistema tiver, mais pontos de falhas ele pode ter.<\/li>\n<li>\n<strong>Observabilidade:<\/strong> Como vamos trabalhar com mais pontos de falhas, a observabilidade se torna algo crucial para todos os microservices, coletando e analisando logs, m\u00e9tricas e traces (gerando um novo desafio, trace distribu\u00eddo).<\/li>\n<li>\n<strong>Consist\u00eancia dos Dados:<\/strong> Com o sistema distribu\u00eddo, tornam-se necess\u00e1rias pr\u00e1ticas para manter a consist\u00eancia dos dados em todos os pontos da aplica\u00e7\u00e3o, visto que com aplica\u00e7\u00f5es distribu\u00eddas, as transa\u00e7\u00f5es que acontecem no sistema tamb\u00e9m ser\u00e3o distribu\u00eddas, necessitando do maior n\u00edvel de consist\u00eancia de dados poss\u00edvel.<\/li>\n<\/ul>\n\n<h3>\n  \n  \n  Transa\u00e7\u00f5es Distribu\u00eddas em Microservices\n<\/h3>\n\n<p>Transa\u00e7\u00f5es distribu\u00eddas nada mais s\u00e3o que transa\u00e7\u00f5es normais, como em banco de dados, mas distribu\u00eddas ao longo dos microservices envolvidos no sistema distribu\u00eddo. Ou seja, devem respeitar os princ\u00edpios de atomicidade, consist\u00eancia, isolamento e durabilidade.<\/p>\n\n<h3>\n  \n  \n  Exemplo de Transa\u00e7\u00e3o Distribu\u00edda\n<\/h3>\n\n<p>Como um bom exemplo, podemos utilizar uma l\u00f3gica de realizar um pedido em um sistema hipot\u00e9tico. Esse pedido segue a seguinte l\u00f3gica:<\/p>\n\n<ol>\n<li>Receber o pedido<\/li>\n<li>Validar o pedido recebido<\/li>\n<li>Validar o estoque<\/li>\n<li>Criar a cobran\u00e7a para o cliente<\/li>\n<\/ol>\n\n<h3>\n  \n  \n  Microservices e Rollback Distribu\u00eddo\n<\/h3>\n\n<p>Dentro de um monolito, essa opera\u00e7\u00e3o pode ser simples, podendo ser realizada dentro de um caso de uso, por exemplo. Mas, quando falamos de microservices, podemos quebrar essa l\u00f3gica em 4 microservices independentes, e \u00e9 a\u00ed que os desafios come\u00e7am.<\/p>\n\n<h3>\n  \n  \n  Exemplo de Implementa\u00e7\u00e3o com Microservices\n<\/h3>\n\n<p>Vamos supor a utiliza\u00e7\u00e3o de quatro microservices:<\/p>\n\n<ol>\n<li>API para realizar a comunica\u00e7\u00e3o com sistemas\/usu\u00e1rios externos<\/li>\n<li>Servi\u00e7o de valida\u00e7\u00e3o de pedidos<\/li>\n<li>Servi\u00e7o de valida\u00e7\u00e3o de estoque<\/li>\n<li>Servi\u00e7o de pagamento<\/li>\n<\/ol>\n\n<p>Nesse caso, supondo um fluxo de \u201ca\u201d para \u201cd\u201d, se ocorrer algum erro, por exemplo, n\u00e3o ter o produto em estoque, a ordem de compra deve ser inteira cancelada. Esse rollback distribu\u00eddo \u00e9 uma opera\u00e7\u00e3o cr\u00edtica e muito mais complexa do que se tudo estivesse concentrado em apenas um monolito, pois surge a necessidade de comunica\u00e7\u00e3o entre os microservices de forma coordenada.<\/p>\n\n<h3>\n  \n  \n  Comunica\u00e7\u00e3o Ass\u00edncrona entre Microservices\n<\/h3>\n\n<p>Para sanar a necessidade de comunica\u00e7\u00e3o, e levando em conta que estamos trabalhando com sistemas distribu\u00eddos, nada melhor do que usarmos um sistema de comunica\u00e7\u00e3o que permita comunica\u00e7\u00e3o ass\u00edncrona entre aplica\u00e7\u00f5es. Nesses casos, me v\u00eam \u00e0 mente servi\u00e7os como Kafka e RabbitMQ, dois brokers de mensagens amplamente utilizados, mas qual devo utilizar?<\/p>\n\n<h3>\n  \n  \n  Compara\u00e7\u00e3o entre RabbitMQ e Kafka\n<\/h3>\n\n<p>A escolha entre RabbitMQ e Kafka deve ser feita levando em conta o contexto da aplica\u00e7\u00e3o e o objetivo da comunica\u00e7\u00e3o ass\u00edncrona entre as aplica\u00e7\u00f5es.<\/p>\n\n<ul>\n<li>\n<strong>RabbitMQ<\/strong> trabalha com um sistema muito parecido com uma fila (estrutura de dados), excluindo a mensagem ap\u00f3s ser consumida.<\/li>\n<li>\n<strong>Kafka<\/strong> trabalha com a ideia de Pub\/Sub, onde produtores publicam mensagens (eventos) em t\u00f3picos e consumidores se inscrevem nesses t\u00f3picos para receber os eventos que ser\u00e3o transmitidos nesse t\u00f3pico. O Kafka tamb\u00e9m trabalha com parti\u00e7\u00f5es em seus t\u00f3picos, permitindo a distribui\u00e7\u00e3o de eventos em paralelo, ampliando a capacidade de leitura e escrita de um t\u00f3pico.<\/li>\n<\/ul>\n\n<p>Para esse exemplo, vou utilizar o Kafka devido \u00e0 sua capacidade de reten\u00e7\u00e3o de eventos.<\/p>\n\n<h3>\n  \n  \n  Implementa\u00e7\u00e3o de Padr\u00e3o SAGA em Microservices\n<\/h3>\n\n<p>Ok, temos a estrutura de microservices definidas e a forma de comunica\u00e7\u00e3o entre eles, mas ainda precisamos fazer algo em rela\u00e7\u00e3o ao fluxo de transa\u00e7\u00e3o distribu\u00edda.<\/p>\n\n<p>Para auxiliar no problema de transa\u00e7\u00e3o que temos, podemos utilizar o padr\u00e3o de microservices chamado de <strong>SAGA<\/strong>, que aparece para garantir que a execu\u00e7\u00e3o de um fluxo de transa\u00e7\u00e3o distribu\u00edda ocorra de forma correta e que, em caso de falhas, todos os rollbacks sejam executados.<\/p>\n\n<h3>\n  \n  \n  SAGA Orquestrado vs. SAGA Coreografado\n<\/h3>\n\n<p>As duas formas de implementa\u00e7\u00e3o do padr\u00e3o SAGA entregam o mesmo resultado, por\u00e9m existem particularidades em cada uma.<\/p>\n\n<ul>\n<li>\n<strong>SAGA Orquestrado<\/strong>: Parte da adi\u00e7\u00e3o de um novo microservice na arquitetura de microservice presente, respons\u00e1vel por centralizar todos os eventos nele e realizar de fato a orquestra\u00e7\u00e3o desses eventos (SEC - Saga Execution Controller).<\/li>\n<li>\n<strong>SAGA Coreografado<\/strong>: Nessa abordagem, cada microservice tem conhecimento do servi\u00e7o anterior e posterior a ele para que consiga executar o fluxo em casos de sucesso e de rollbacks.<\/li>\n<\/ul>\n\n<p>Para esse exemplo, vou utilizar o padr\u00e3o SAGA orquestrado e a organiza\u00e7\u00e3o de t\u00f3picos ficar\u00e1 da seguinte maneira:<\/p>\n\n<ol>\n<li>Todos os microservices v\u00e3o gerar eventos para o t\u00f3pico \u201cORQUESTRADOR\u201d, t\u00f3pico consumido pelo SEC.<\/li>\n<li>Cada microservice vai receber eventos de dois t\u00f3picos, um para o caso de realizar uma a\u00e7\u00e3o com sucesso e outro para realizar o rollback do evento da vez.<\/li>\n<\/ol>\n\n<p>Exemplo de arquitetura.<br>\n<a href=\"https:\/\/media.dev.to\/cdn-cgi\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lna7qd6iiun1wq70h1d.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/cdn-cgi\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lna7qd6iiun1wq70h1d.png\" alt=\"Exemplo de arquitetura\" width=\"800\" height=\"360\"><\/a><\/p>\n\n<h3>\n  \n  \n  Considera\u00e7\u00f5es Finais\n<\/h3>\n\n<p>Um ponto que se deve levar em conta \u00e9 a <strong>idempot\u00eancia de eventos<\/strong>, ou seja, um evento de pagamento, por exemplo, n\u00e3o deve ser executado duas vezes, ent\u00e3o mecanismos como identificador \u00fanico e verifica\u00e7\u00f5es em banco de dados devem ser implementados, principalmente em ambientes com redund\u00e2ncia a n\u00edvel de cluster do Kafka.<\/p>\n\n<h2>\n  \n  \n  Recursos extras\n<\/h2>\n\n<ul>\n<li>Padr\u00e3o SAGA: <a href=\"https:\/\/microservices.io\/patterns\/data\/saga.html\" rel=\"noopener noreferrer\">padrao-saga<\/a>\n<\/li>\n<li>ACID: <a href=\"https:\/\/www.databricks.com\/br\/glossary\/acid-transactions\" rel=\"noopener noreferrer\">ACID<\/a>\n<\/li>\n<li>Microservices: <a href=\"https:\/\/aws.amazon.com\/microservices\/\" rel=\"noopener noreferrer\">microservices<\/a>\n<\/li>\n<li>Repo com c\u00f3digo de exemplo: <a href=\"https:\/\/github.com\/joserafaelSH\/first-saga-pattern-app\" rel=\"noopener noreferrer\">joserafaelSH\/first-saga-pattern-app<\/a>\n<\/li>\n<li>kafka-x-rabbitmq : <a href=\"https:\/\/aws.amazon.com\/pt\/compare\/the-difference-between-rabbitmq-and-kafka\/\" rel=\"noopener noreferrer\">kafka-x-rabbitmq<\/a>\n<\/li>\n<\/ul>\n\n"},{"title":"Caso de uso: LocalStack","pubDate":"Fri, 05 Jul 2024 02:46:39 +0000","link":"https:\/\/dev.to\/joserafaelsh\/caso-de-uso-localstack-3gc5","guid":"https:\/\/dev.to\/joserafaelsh\/caso-de-uso-localstack-3gc5","description":"<h2>\n  \n  \n  Um pouco sobre o LocalStack\n<\/h2>\n\n<p>O <strong>LocalStack<\/strong> \u00e9 um emulador de servi\u00e7os <strong>AWS<\/strong> que abrange seus principais servi\u00e7os, alguns de forma gratuita e outros n\u00e3o. O objetivo dessa ferramenta \u00e9 facilitar o desenvolvimento de aplica\u00e7\u00f5es que utilizam servi\u00e7os da <strong>AWS<\/strong>, aumentando a seguran\u00e7a em rela\u00e7\u00e3o a custos de desenvolvimento, melhorando a experi\u00eancia do desenvolvedor em rela\u00e7\u00e3o a problemas de configura\u00e7\u00f5es de permiss\u00f5es com o <strong>IAM<\/strong> e permitindo a exist\u00eancia de um ambiente de testes, visando tanto a parte de aprendizado e experimenta\u00e7\u00e3o dos servi\u00e7os da <strong>AWS<\/strong>, quanto processos como <strong>CI<\/strong> com integra\u00e7\u00f5es com o Github Actions.<\/p>\n\n<p>O <strong>LocalStack<\/strong> tamb\u00e9m possui integra\u00e7\u00f5es com outras ferramentas incr\u00edveis como <strong>Pulumi<\/strong>, <strong>Serverless<\/strong>, <strong>Terraform<\/strong> e <strong>Testcontainers<\/strong>.<\/p>\n\n<p>Leia mais sobre: <a href=\"https:\/\/docs.localstack.cloud\/getting-started\/\">https:\/\/docs.localstack.cloud\/getting-started\/<\/a><\/p>\n\n<h2>\n  \n  \n  O cen\u00e1rio\n<\/h2>\n\n<p>Suponha uma aplica\u00e7\u00e3o simples que realiza todas as opera\u00e7\u00f5es de um <strong>CRUD<\/strong> utilizando uma tabela no <strong>DynamoDB<\/strong>. Independentemente da forma com que o desenvolvedor vai construir essa aplica\u00e7\u00e3o, cedo ou tarde ele vai ter que acessar a tabela de produ\u00e7\u00e3o para validar o que foi feito, seja com testes manuais que todos n\u00f3s fazemos ou com <strong>testes de integra\u00e7\u00e3o<\/strong> e <strong>e2e<\/strong>, e \u00e9 a\u00ed que o problema come\u00e7a a aparecer.<\/p>\n\n<h2>\n  \n  \n  O problema\n<\/h2>\n\n<p>O <strong>DynamoDB<\/strong> cobra por opera\u00e7\u00f5es na tabela, ou seja, antes de realmente finalizar e disponibilizar a aplica\u00e7\u00e3o, j\u00e1 v\u00e3o ter sido gerados custos. Adicione um pouco mais de complexidade nesse sistema, integrando um processamento ass\u00edncrono com <strong>SQS<\/strong>, eventos com <strong>EventBridge<\/strong> e notifica\u00e7\u00f5es com <strong>SNS<\/strong> e <strong>SES<\/strong>, e pronto, sua fatura da AWS j\u00e1 vai estar rodando antes do dia 0 da sua aplica\u00e7\u00e3o.<\/p>\n\n<h2>\n  \n  \n  A solu\u00e7\u00e3o\n<\/h2>\n\n<p>Nesse cen\u00e1rio, podemos utilizar em nosso ambiente de desenvolvimento o <strong>LocalStack<\/strong>, um emulador de servi\u00e7os cloud <strong>AWS<\/strong> que tem como objetivo agilizar e simplificar o desenvolvimento e testes de aplica\u00e7\u00f5es que utilizem servi\u00e7os da cloud <strong>AWS<\/strong>.<\/p>\n\n<p>Utilizando o <strong>Docker<\/strong>, <strong>docker-compose<\/strong> e <strong>AWS SDK<\/strong> da linguagem de programa\u00e7\u00e3o utilizada, conseguimos subir um container do <strong>LocalStack<\/strong> e, atrav\u00e9s da configura\u00e7\u00e3o de <strong>URL<\/strong> do <strong>SDK<\/strong> e de <strong>vari\u00e1veis de ambiente<\/strong>, conseguimos manipular os ambientes para que, em desenvolvimento e testes, as chamadas apontem para o <strong>LocalStack,<\/strong> minimizando os custos durante o desenvolvimento.<\/p>\n\n<p>No caso apresentado acima, conseguir\u00edamos execut\u00e1-lo totalmente dentro do <strong>LocalStack<\/strong> utilizando os servi\u00e7os do <strong>DynamoDB<\/strong> e os servi\u00e7os extras como <strong>SQS<\/strong>, <strong>EventBridge<\/strong>, <strong>SNS<\/strong> e <strong>SES<\/strong> (de forma simulada). Isso garantiria um <strong>custo zero<\/strong> de servi\u00e7os cloud durante o desenvolvimento e em <strong>pipelines<\/strong> de <strong>CI\/CD<\/strong>, uma vez que o <strong>LocalStack<\/strong> tamb\u00e9m possui integra\u00e7\u00e3o com <strong>GitHub Actions<\/strong>.<\/p>\n\n<h2>\n  \n  \n  Implementa\u00e7\u00e3o\n<\/h2>\n\n<p>Afim de exemplificar o uso e, embasado na aplica\u00e7\u00e3o apresentada acima, implementei parcialmente um <strong>CRUD<\/strong> simples de produtos com apenas duas opera\u00e7\u00f5es: criar um item e ler todos os itens da tabela.<\/p>\n\n<p>A implementa\u00e7\u00e3o foi feita utilizando apenas recursos do Node 22, inclusive seu pr\u00f3prio test runner. A aplica\u00e7\u00e3o \u00e9 uma API normal com duas rotas: uma para criar um item e outra para ler todos os itens de uma tabela do <strong>DynamoDB<\/strong>.<\/p>\n\n<p>Para configurar meu ambiente de desenvolvimento, utilizei um arquivo <strong>.env<\/strong> para guardar minhas vari\u00e1veis de acesso e <strong>endpoint<\/strong> da <strong>AWS<\/strong>. Esse arquivo ser\u00e1 utilizado para que possamos alterar de forma r\u00e1pida, sem ter que de fato abrir o c\u00f3digo, o ambiente em que nossa aplica\u00e7\u00e3o vai rodar.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight tsx\"><code><span class=\"nx\">NODE_ENV<\/span><span class=\"o\">=<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">dev<\/span><span class=\"dl\">\"<\/span>\n<span class=\"nx\">PORT<\/span><span class=\"o\">=<\/span><span class=\"mi\">3000<\/span>\n<span class=\"nx\">AWS_ENDPOINT<\/span><span class=\"o\">=<\/span><span class=\"nx\">http<\/span><span class=\"p\">:<\/span><span class=\"c1\">\/\/localhost:4566<\/span>\n<span class=\"nx\">AWS_REGION<\/span><span class=\"o\">=<\/span><span class=\"nx\">us<\/span><span class=\"o\">-<\/span><span class=\"nx\">east<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span>\n<span class=\"nx\">AWS_ACCESS_KEY_ID<\/span><span class=\"o\">=<\/span><span class=\"nx\">fake_id<\/span>\n<span class=\"nx\">AWS_SECRET_ACCESS_KEY<\/span><span class=\"o\">=<\/span><span class=\"nx\">fake_secret<\/span>\n<span class=\"nx\">ITEMS_TABLE_NAME<\/span><span class=\"o\">=<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">items_table<\/span><span class=\"dl\">\"<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p><strong>Tamb\u00e9m foi utilizado o LocalStack com Docker e docker-compose.<\/strong><br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight tsx\"><code><span class=\"nx\">services<\/span><span class=\"p\">:<\/span>\n  <span class=\"nx\">localstack<\/span><span class=\"p\">:<\/span>\n    <span class=\"nx\">container_name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">localstack<\/span><span class=\"dl\">\"<\/span>\n    <span class=\"nx\">image<\/span><span class=\"p\">:<\/span> <span class=\"nx\">localstack<\/span><span class=\"o\">\/<\/span><span class=\"nx\">localstack<\/span>\n    <span class=\"nx\">ports<\/span><span class=\"p\">:<\/span>\n      <span class=\"o\">-<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">127.0.0.1:4566:4566<\/span><span class=\"dl\">\"<\/span>            <span class=\"err\">#<\/span> <span class=\"nx\">LocalStack<\/span> <span class=\"nx\">Gateway<\/span>\n      <span class=\"o\">-<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">127.0.0.1:4510-4559:4510-4559<\/span><span class=\"dl\">\"<\/span>  <span class=\"err\">#<\/span> <span class=\"nx\">external<\/span> <span class=\"nx\">services<\/span> <span class=\"nx\">port<\/span> <span class=\"nx\">range<\/span>\n    <span class=\"nx\">volumes<\/span><span class=\"p\">:<\/span>\n      <span class=\"o\">-<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">\/var\/run\/docker.sock:\/var\/run\/docker.sock<\/span><span class=\"dl\">\"<\/span> <span class=\"err\">#<\/span><span class=\"nx\">required<\/span> <span class=\"k\">for<\/span> <span class=\"nx\">some<\/span> <span class=\"nx\">services<\/span>\n      <span class=\"o\">-<\/span> <span class=\"p\">.<\/span><span class=\"o\">\/<\/span><span class=\"nx\">setup<\/span><span class=\"p\">.<\/span><span class=\"nx\">sh<\/span><span class=\"p\">:<\/span><span class=\"o\">\/<\/span><span class=\"nx\">etc<\/span><span class=\"o\">\/<\/span><span class=\"nx\">localstack<\/span><span class=\"o\">\/<\/span><span class=\"nx\">init<\/span><span class=\"o\">\/<\/span><span class=\"nx\">ready<\/span><span class=\"p\">.<\/span><span class=\"nx\">d<\/span><span class=\"o\">\/<\/span><span class=\"nx\">start<\/span><span class=\"o\">-<\/span><span class=\"nx\">localstack<\/span><span class=\"p\">.<\/span><span class=\"nx\">sh<\/span>\n\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p><strong>No docker-compose, \u00e9 importante ressaltar o \u00faltimo volume utilizado. Ele \u00e9 um script .sh que ser\u00e1 copiado para dentro do container do LocalStack e ser\u00e1 executado junto com a inicializa\u00e7\u00e3o do container. Esse arquivo cont\u00e9m o comando para criar uma tabela no DynamoDB.<\/strong><br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight tsx\"><code><span class=\"cp\">#!\/bin\/bash\n<\/span>\n<span class=\"nx\">awslocal<\/span> <span class=\"nx\">dynamodb<\/span> <span class=\"nx\">create<\/span><span class=\"o\">-<\/span><span class=\"nx\">table<\/span>     <span class=\"o\">--<\/span><span class=\"nx\">table<\/span><span class=\"o\">-<\/span><span class=\"nx\">name<\/span> <span class=\"nx\">items_table<\/span>     <span class=\"o\">--<\/span><span class=\"nx\">key<\/span><span class=\"o\">-<\/span><span class=\"nx\">schema<\/span> <span class=\"nx\">AttributeName<\/span><span class=\"o\">=<\/span><span class=\"nx\">id<\/span><span class=\"p\">,<\/span><span class=\"nx\">KeyType<\/span><span class=\"o\">=<\/span><span class=\"nx\">HASH<\/span>     <span class=\"o\">--<\/span><span class=\"nx\">attribute<\/span><span class=\"o\">-<\/span><span class=\"nx\">definitions<\/span> <span class=\"nx\">AttributeName<\/span><span class=\"o\">=<\/span><span class=\"nx\">id<\/span><span class=\"p\">,<\/span><span class=\"nx\">AttributeType<\/span><span class=\"o\">=<\/span><span class=\"nx\">S<\/span>     <span class=\"o\">--<\/span><span class=\"nx\">billing<\/span><span class=\"o\">-<\/span><span class=\"nx\">mode<\/span> <span class=\"nx\">PAY_PER_REQUEST<\/span>     <span class=\"o\">--<\/span><span class=\"nx\">region<\/span> <span class=\"nx\">us<\/span><span class=\"o\">-<\/span><span class=\"nx\">east<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span>\n\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Todos os comandos para lidar com os servi\u00e7os da <strong>AWS<\/strong> no <strong>LocalStack<\/strong> podem ser encontrados na documenta\u00e7\u00e3o da ferramenta.<\/p>\n\n<p>Com o container rodando e o arquivo <strong>.env<\/strong> configurado, o pr\u00f3ximo passo \u00e9 configurar via c\u00f3digo o cliente do servi\u00e7o que ser\u00e1 utilizado. Nesse caso, o servi\u00e7o ser\u00e1 o <strong>DynamoDBClient<\/strong>.<\/p>\n\n<p><strong>Vale ressaltar que foi utilizado o SDK v3 para o NodeJs.<\/strong><\/p>\n\n<p>O <strong>DynamoDBClient<\/strong> recebe como par\u00e2metro as seguintes configura\u00e7\u00f5es:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight jsx\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">awsConfig<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">endpoint<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_ENDPOINT<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">region<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_REGION<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">credentials<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">accessKeyId<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_ACCESS_KEY_ID<\/span><span class=\"p\">,<\/span>\n    <span class=\"na\">secretAccessKey<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_SECRET_ACCESS_KEY<\/span><span class=\"p\">,<\/span>\n  <span class=\"p\">},<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Como passamos toda a nossa configura\u00e7\u00e3o via vari\u00e1veis de ambiente, n\u00e3o precisamos alterar nenhuma parte do c\u00f3digo para trocar entre nosso ambiente de desenvolvimento e o ambiente de produ\u00e7\u00e3o.<\/p>\n\n<p>A configura\u00e7\u00e3o do cliente do <strong>DynamoDB<\/strong> da nossa aplica\u00e7\u00e3o ficou da seguinte forma:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight jsx\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">CreateTableCommand<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">DeleteTableCommand<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">DynamoDBClient<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">PutItemCommand<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">ScanCommand<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">@aws-sdk\/client-dynamodb<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">PutCommand<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">@aws-sdk\/lib-dynamodb<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">awsConfig<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">endpoint<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_ENDPOINT<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">region<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_REGION<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">credentials<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">accessKeyId<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_ACCESS_KEY_ID<\/span><span class=\"p\">,<\/span>\n    <span class=\"na\">secretAccessKey<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_SECRET_ACCESS_KEY<\/span><span class=\"p\">,<\/span>\n  <span class=\"p\">},<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">dynamoClient<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nc\">DynamoDBClient<\/span><span class=\"p\">(<\/span><span class=\"nx\">awsConfig<\/span><span class=\"p\">);<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">Dynamo<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">getAllItems<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">tableName<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nx\">dynamoClient<\/span><span class=\"p\">.<\/span><span class=\"nf\">send<\/span><span class=\"p\">(<\/span>\n      <span class=\"k\">new<\/span> <span class=\"nc\">ScanCommand<\/span><span class=\"p\">({<\/span>\n        <span class=\"na\">TableName<\/span><span class=\"p\">:<\/span> <span class=\"nx\">tableName<\/span><span class=\"p\">,<\/span>\n      <span class=\"p\">})<\/span>\n    <span class=\"p\">);<\/span>\n  <span class=\"p\">},<\/span>\n\n  <span class=\"na\">createItem<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">item<\/span><span class=\"p\">,<\/span> <span class=\"nx\">tableName<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nx\">dynamoClient<\/span><span class=\"p\">.<\/span><span class=\"nf\">send<\/span><span class=\"p\">(<\/span>\n      <span class=\"k\">new<\/span> <span class=\"nc\">PutCommand<\/span><span class=\"p\">({<\/span>\n        <span class=\"na\">TableName<\/span><span class=\"p\">:<\/span> <span class=\"nx\">tableName<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">Item<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n          <span class=\"p\">...<\/span><span class=\"nx\">item<\/span><span class=\"p\">,<\/span>\n        <span class=\"p\">},<\/span>\n      <span class=\"p\">})<\/span>\n    <span class=\"p\">);<\/span>\n  <span class=\"p\">},<\/span>\n\n  <span class=\"na\">createTable<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">tableName<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nx\">dynamoClient<\/span><span class=\"p\">.<\/span><span class=\"nf\">send<\/span><span class=\"p\">(<\/span>\n      <span class=\"k\">new<\/span> <span class=\"nc\">CreateTableCommand<\/span><span class=\"p\">({<\/span>\n        <span class=\"na\">TableName<\/span><span class=\"p\">:<\/span> <span class=\"nx\">tableName<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">KeySchema<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\n          <span class=\"p\">{<\/span>\n            <span class=\"na\">AttributeName<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">id<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n            <span class=\"na\">KeyType<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">HASH<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n          <span class=\"p\">},<\/span>\n        <span class=\"p\">],<\/span>\n        <span class=\"na\">AttributeDefinitions<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\n          <span class=\"p\">{<\/span>\n            <span class=\"na\">AttributeName<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">id<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n            <span class=\"na\">AttributeType<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">S<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n          <span class=\"p\">},<\/span>\n        <span class=\"p\">],<\/span>\n        <span class=\"na\">ProvisionedThroughput<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n          <span class=\"na\">ReadCapacityUnits<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span>\n          <span class=\"na\">WriteCapacityUnits<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span>\n        <span class=\"p\">},<\/span>\n      <span class=\"p\">})<\/span>\n    <span class=\"p\">);<\/span>\n  <span class=\"p\">},<\/span>\n\n  <span class=\"na\">deleteTable<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">tableName<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nx\">dynamoClient<\/span><span class=\"p\">.<\/span><span class=\"nf\">send<\/span><span class=\"p\">(<\/span>\n      <span class=\"k\">new<\/span> <span class=\"nc\">DeleteTableCommand<\/span><span class=\"p\">({<\/span>\n        <span class=\"na\">TableName<\/span><span class=\"p\">:<\/span> <span class=\"nx\">tableName<\/span><span class=\"p\">,<\/span>\n      <span class=\"p\">})<\/span>\n    <span class=\"p\">);<\/span>\n  <span class=\"p\">},<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Como um dos intuitos do <strong>LocalStack<\/strong> \u00e9 permitir o teste local de servi\u00e7os <strong>AWS<\/strong>, criei uma rotina simples de teste, apenas para garantir que conseguimos executar as duas opera\u00e7\u00f5es que a nossa aplica\u00e7\u00e3o se prop\u00f5e a fazer.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight jsx\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">describe<\/span><span class=\"p\">,<\/span> <span class=\"nx\">it<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">node:test<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">Dynamo<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">..\/dynamo-db.js<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"nx\">assert<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">node:assert\/strict<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"nf\">describe<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Integrations tests with DynamoDB and LocalStack<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">database<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Dynamo<\/span><span class=\"p\">;<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">testTableName<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">items_table_test<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n  <span class=\"nf\">it<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">it should create the items table<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">response<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">database<\/span><span class=\"p\">.<\/span><span class=\"nf\">createTable<\/span><span class=\"p\">(<\/span><span class=\"nx\">testTableName<\/span><span class=\"p\">);<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">status<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">response<\/span><span class=\"p\">[<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">$metadata<\/span><span class=\"dl\">\"<\/span><span class=\"p\">].<\/span><span class=\"nx\">httpStatusCode<\/span><span class=\"p\">;<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">response<\/span> <span class=\"o\">!=<\/span> <span class=\"kc\">undefined<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">status<\/span><span class=\"p\">,<\/span> <span class=\"mi\">200<\/span><span class=\"p\">);<\/span>\n  <span class=\"p\">});<\/span>\n\n  <span class=\"nf\">it<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">it should create a item<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">response<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">database<\/span><span class=\"p\">.<\/span><span class=\"nf\">createItem<\/span><span class=\"p\">(<\/span>\n      <span class=\"p\">{<\/span>\n        <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">123<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">teste<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">price<\/span><span class=\"p\">:<\/span> <span class=\"mi\">100<\/span><span class=\"p\">,<\/span>\n      <span class=\"p\">},<\/span>\n      <span class=\"nx\">testTableName<\/span>\n    <span class=\"p\">);<\/span>\n\n    <span class=\"kd\">const<\/span> <span class=\"nx\">status<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">response<\/span><span class=\"p\">[<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">$metadata<\/span><span class=\"dl\">\"<\/span><span class=\"p\">].<\/span><span class=\"nx\">httpStatusCode<\/span><span class=\"p\">;<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">response<\/span> <span class=\"o\">!=<\/span> <span class=\"kc\">undefined<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">status<\/span><span class=\"p\">,<\/span> <span class=\"mi\">200<\/span><span class=\"p\">);<\/span>\n  <span class=\"p\">});<\/span>\n\n  <span class=\"nf\">it<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">it should get all items<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">response<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">database<\/span><span class=\"p\">.<\/span><span class=\"nf\">getAllItems<\/span><span class=\"p\">(<\/span><span class=\"nx\">testTableName<\/span><span class=\"p\">);<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">status<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">response<\/span><span class=\"p\">[<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">$metadata<\/span><span class=\"dl\">\"<\/span><span class=\"p\">].<\/span><span class=\"nx\">httpStatusCode<\/span><span class=\"p\">;<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">response<\/span> <span class=\"o\">!=<\/span> <span class=\"kc\">undefined<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">status<\/span><span class=\"p\">,<\/span> <span class=\"mi\">200<\/span><span class=\"p\">);<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">response<\/span><span class=\"p\">.<\/span><span class=\"nx\">Count<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">response<\/span><span class=\"p\">.<\/span><span class=\"nx\">ScannedCount<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">response<\/span><span class=\"p\">.<\/span><span class=\"nx\">Items<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n  <span class=\"p\">});<\/span>\n\n  <span class=\"nf\">it<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">it should delete the items table<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"k\">async <\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">response<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">database<\/span><span class=\"p\">.<\/span><span class=\"nf\">deleteTable<\/span><span class=\"p\">(<\/span><span class=\"nx\">testTableName<\/span><span class=\"p\">);<\/span>\n    <span class=\"nx\">assert<\/span><span class=\"p\">.<\/span><span class=\"nf\">equal<\/span><span class=\"p\">(<\/span><span class=\"nx\">response<\/span> <span class=\"o\">!=<\/span> <span class=\"kc\">undefined<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n  <span class=\"p\">});<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p><strong>Ao final do desenvolvimento, criei uma action no GitHub para que possamos rodar nossos testes em um pipeline de CI\/CD.<\/strong><br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight jsx\"><code><span class=\"nx\">name<\/span><span class=\"p\">:<\/span> <span class=\"nx\">CI<\/span> <span class=\"nx\">using<\/span> <span class=\"nx\">localstack<\/span>\n\n<span class=\"nx\">on<\/span><span class=\"p\">:<\/span> <span class=\"nx\">push<\/span>\n\n<span class=\"nx\">jobs<\/span><span class=\"p\">:<\/span>\n  <span class=\"nx\">continuos<\/span><span class=\"o\">-<\/span><span class=\"nx\">integration<\/span><span class=\"p\">:<\/span>\n    <span class=\"nx\">runs<\/span><span class=\"o\">-<\/span><span class=\"nx\">on<\/span><span class=\"p\">:<\/span> <span class=\"nx\">ubuntu<\/span><span class=\"o\">-<\/span><span class=\"nx\">latest<\/span>\n    <span class=\"nx\">environment<\/span><span class=\"p\">:<\/span> <span class=\"nx\">poc<\/span><span class=\"o\">-<\/span><span class=\"nx\">node<\/span><span class=\"o\">-<\/span><span class=\"nx\">js<\/span><span class=\"o\">-<\/span><span class=\"nx\">localstack<\/span><span class=\"o\">-<\/span><span class=\"nx\">env<\/span>\n\n    <span class=\"nx\">steps<\/span><span class=\"p\">:<\/span>\n      <span class=\"o\">-<\/span> <span class=\"nx\">uses<\/span><span class=\"p\">:<\/span> <span class=\"nx\">actions<\/span><span class=\"o\">\/<\/span><span class=\"nx\">checkout<\/span><span class=\"p\">@<\/span><span class=\"nd\">v3<\/span>\n      <span class=\"o\">-<\/span> <span class=\"nx\">name<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Using<\/span> <span class=\"nx\">Node<\/span><span class=\"p\">.<\/span><span class=\"nx\">js<\/span>\n        <span class=\"nx\">uses<\/span><span class=\"p\">:<\/span> <span class=\"nx\">actions<\/span><span class=\"o\">\/<\/span><span class=\"nx\">setup<\/span><span class=\"o\">-<\/span><span class=\"nx\">node<\/span><span class=\"p\">@<\/span><span class=\"nd\">v2<\/span>\n        <span class=\"kd\">with<\/span><span class=\"p\">:<\/span>\n          <span class=\"nx\">node<\/span><span class=\"o\">-<\/span><span class=\"nx\">version<\/span><span class=\"p\">:<\/span> <span class=\"mi\">22<\/span><span class=\"p\">.<\/span>\n\n      <span class=\"o\">-<\/span> <span class=\"nx\">name<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Start<\/span> <span class=\"nx\">LocalStack<\/span>\n        <span class=\"nx\">uses<\/span><span class=\"p\">:<\/span> <span class=\"nx\">LocalStack<\/span><span class=\"o\">\/<\/span><span class=\"nx\">setup<\/span><span class=\"o\">-<\/span><span class=\"nx\">localstack<\/span><span class=\"p\">@<\/span><span class=\"nd\">v0<\/span><span class=\"p\">.<\/span><span class=\"mf\">2.0<\/span>\n        <span class=\"kd\">with<\/span><span class=\"p\">:<\/span>\n          <span class=\"nx\">image<\/span><span class=\"o\">-<\/span><span class=\"nx\">tag<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">latest<\/span><span class=\"dl\">'<\/span>\n          <span class=\"nx\">install<\/span><span class=\"o\">-<\/span><span class=\"nx\">awslocal<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">true<\/span><span class=\"dl\">'<\/span>\n\n      <span class=\"o\">-<\/span> <span class=\"nx\">name<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Create<\/span> <span class=\"p\">.<\/span><span class=\"nx\">env<\/span> <span class=\"nx\">file<\/span>\n        <span class=\"nx\">run<\/span><span class=\"p\">:<\/span> <span class=\"o\">|<\/span>\n          <span class=\"nx\">touch<\/span> <span class=\"p\">.<\/span><span class=\"nx\">env<\/span>\n          <span class=\"nx\">echo<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">AWS_ACCESS_KEY_ID=${{vars.AWS_ACCESS_KEY_ID}}<\/span><span class=\"dl\">\"<\/span> <span class=\"o\">&gt;&gt;<\/span> <span class=\"p\">.<\/span><span class=\"nx\">env<\/span>\n          <span class=\"nx\">echo<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">AWS_ENDPOINT=${{vars.AWS_ENDPOINT}}<\/span><span class=\"dl\">\"<\/span> <span class=\"o\">&gt;&gt;<\/span> <span class=\"p\">.<\/span><span class=\"nx\">env<\/span>\n          <span class=\"nx\">echo<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">AWS_REGION=${{vars.AWS_REGION}}<\/span><span class=\"dl\">\"<\/span> <span class=\"o\">&gt;&gt;<\/span> <span class=\"p\">.<\/span><span class=\"nx\">env<\/span>\n          <span class=\"nx\">echo<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">AWS_SECRET_ACCESS_KEY=${{vars.AWS_SECRET_ACCESS_KEY}}<\/span><span class=\"dl\">\"<\/span> <span class=\"o\">&gt;&gt;<\/span> <span class=\"p\">.<\/span><span class=\"nx\">env<\/span>\n          <span class=\"nx\">echo<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">ITEMS_TABLE_NAME=${{vars.ITEMS_TABLE_NAME}}<\/span><span class=\"dl\">\"<\/span> <span class=\"o\">&gt;&gt;<\/span> <span class=\"p\">.<\/span><span class=\"nx\">env<\/span>\n          <span class=\"nx\">cat<\/span> <span class=\"p\">.<\/span><span class=\"nx\">env<\/span>\n\n      <span class=\"o\">-<\/span> <span class=\"nx\">name<\/span><span class=\"p\">:<\/span> <span class=\"nx\">run<\/span> <span class=\"nx\">install<\/span><span class=\"p\">,<\/span> <span class=\"nx\">build<\/span> <span class=\"nx\">and<\/span> <span class=\"nx\">test<\/span>\n        <span class=\"nx\">run<\/span><span class=\"p\">:<\/span> <span class=\"o\">|<\/span>\n          <span class=\"nx\">npm<\/span> <span class=\"nx\">install<\/span>\n          <span class=\"nx\">npm<\/span> <span class=\"nx\">run<\/span> <span class=\"nx\">test<\/span>\n\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Aqui est\u00e1 o link para mais informa\u00e7\u00f5es sobre a integra\u00e7\u00e3o do <strong>LocalStack<\/strong> com <strong>GitHub Actions<\/strong>: <a href=\"https:\/\/docs.localstack.cloud\/user-guide\/ci\/github-actions\/\">https:\/\/docs.localstack.cloud\/user-guide\/ci\/github-actions\/<\/a> .Esse recurso pode ajudar a configurar <strong>pipelines<\/strong> de <strong>CI\/CD<\/strong> que utilizam o <strong>LocalStack<\/strong> para testes locais de servi\u00e7os <strong>AWS<\/strong>.<\/p>\n\n<h2>\n  \n  \n  Limita\u00e7\u00f5es\n<\/h2>\n\n<p>Nem todos os servi\u00e7os que podem ser emulados via <strong>LocalStack<\/strong> est\u00e3o inteiramente implementados e est\u00e1veis. Pegando o <strong>DynamoDB<\/strong> e o <strong>SES<\/strong>, podemos notar que a maioria das funcionalidades do <strong>DynamoDB<\/strong> est\u00e3o implementadas parcialmente e, para o SES, a maioria de seus servi\u00e7os est\u00e3o inst\u00e1veis.<\/p>\n\n<p>Com isso, podemos concluir que precisamos nos atentar aos servi\u00e7os e suas funcionalidades para que n\u00e3o haja diverg\u00eancias bruscas entre nosso ambiente de desenvolvimento, testes e o de produ\u00e7\u00e3o.<\/p>\n\n<p>Na documenta\u00e7\u00e3o do <strong>LocalStack<\/strong>, podemos encontrar todos os servi\u00e7os e seus respectivos n\u00edveis de cobertura.<\/p>\n\n<h2>\n  \n  \n  Alguns exemplos\n<\/h2>\n\n<ul>\n<li>docker-compose\n<\/li>\n<\/ul>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight docker\"><code>services:\n  localstack:\n    container_name: \"localstack\"\n    image: localstack\/localstack\n    ports:\n      - \"127.0.0.1:4566:4566\"            # LocalStack Gateway\n      - \"127.0.0.1:4510-4559:4510-4559\"  # external services port range\n    volumes:\n      - \"\/var\/run\/docker.sock:\/var\/run\/docker.sock\" #required for some services\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<ul>\n<li>NodeJS DynamoDB example\n<\/li>\n<\/ul>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight jsx\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">DynamoDBClient<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">@aws-sdk\/client-dynamodb<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">dynamodbConfig<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">region<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">us-east-1<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">};<\/span>\n<span class=\"kd\">const<\/span> <span class=\"nx\">isLocal<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">IS_OFFLINE<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">true<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">if <\/span><span class=\"p\">(<\/span><span class=\"nx\">isLocal<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">host<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">LOCALSTACK_HOST<\/span> <span class=\"o\">||<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">localhost<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n  <span class=\"nx\">dynamodbConfig<\/span><span class=\"p\">[<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">endpoint<\/span><span class=\"dl\">\"<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">`http:\/\/<\/span><span class=\"p\">${<\/span><span class=\"nx\">host<\/span><span class=\"p\">}<\/span><span class=\"s2\">:4566`<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">client<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nc\">DynamoDBClient<\/span><span class=\"p\">(<\/span><span class=\"nx\">dynamodbConfig<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<ul>\n<li>NodeJS SQS NestJS example\n<\/li>\n<\/ul>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight tsx\"><code><span class=\"p\">@<\/span><span class=\"nd\">Injectable<\/span><span class=\"p\">()<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">class<\/span> <span class=\"nc\">SqsService<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">private<\/span> <span class=\"k\">readonly<\/span> <span class=\"nx\">client<\/span><span class=\"p\">:<\/span> <span class=\"nx\">SQSClient<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nc\">SQSClient<\/span><span class=\"p\">({<\/span>\n    <span class=\"na\">endpoint<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">envConfigService<\/span><span class=\"p\">.<\/span><span class=\"nf\">getAwsEndpoint<\/span><span class=\"p\">()<\/span> <span class=\"o\">||<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">AWS_ENDPOINT<\/span><span class=\"p\">,<\/span>\n    <span class=\"na\">region<\/span><span class=\"p\">:<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">envConfigService<\/span><span class=\"p\">.<\/span><span class=\"nf\">getAwsRegion<\/span><span class=\"p\">(),<\/span>\n    <span class=\"na\">credentials<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n      <span class=\"na\">accessKeyId<\/span><span class=\"p\">:<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">envConfigService<\/span><span class=\"p\">.<\/span><span class=\"nf\">getAwsAccessKeyId<\/span><span class=\"p\">(),<\/span>\n      <span class=\"na\">secretAccessKey<\/span><span class=\"p\">:<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">envConfigService<\/span><span class=\"p\">.<\/span><span class=\"nf\">getAwsSecretAccessKey<\/span><span class=\"p\">(),<\/span>\n    <span class=\"p\">},<\/span>\n  <span class=\"p\">});<\/span>\n\n  <span class=\"nf\">constructor<\/span><span class=\"p\">()<\/span> <span class=\"p\">{}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight tsx\"><code><span class=\"nx\">NODE_ENV<\/span><span class=\"o\">=<\/span><span class=\"nx\">prod<\/span>\n<span class=\"nx\">AWS_ENDPOINT<\/span><span class=\"o\">=<\/span><span class=\"nx\">protocol<\/span><span class=\"p\">:<\/span><span class=\"c1\">\/\/service-code.region-code.amazonaws.com<\/span>\n<span class=\"nx\">AWS_REGION<\/span><span class=\"o\">=<\/span><span class=\"nx\">us<\/span><span class=\"o\">-<\/span><span class=\"nx\">east<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span>\n<span class=\"nx\">AWS_ACCESS_KEY_ID<\/span><span class=\"o\">=<\/span><span class=\"nx\">real_id<\/span>\n<span class=\"nx\">AWS_SECRET_ACCESS_KEY<\/span><span class=\"o\">=<\/span><span class=\"nx\">real_secret<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight tsx\"><code><span class=\"nx\">NODE_ENV<\/span><span class=\"o\">=<\/span><span class=\"nx\">dev<\/span>\n<span class=\"nx\">AWS_ENDPOINT<\/span><span class=\"o\">=<\/span><span class=\"nx\">http<\/span><span class=\"p\">:<\/span><span class=\"c1\">\/\/localhost:4566<\/span>\n<span class=\"nx\">AWS_REGION<\/span><span class=\"o\">=<\/span><span class=\"nx\">us<\/span><span class=\"o\">-<\/span><span class=\"nx\">east<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span>\n<span class=\"nx\">AWS_ACCESS_KEY_ID<\/span><span class=\"o\">=<\/span><span class=\"nx\">fake_id<\/span>\n<span class=\"nx\">AWS_SECRET_ACCESS_KEY<\/span><span class=\"o\">=<\/span><span class=\"nx\">fake_secret<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>O <strong>endpoint<\/strong> \u00e9 formado pelo seguinte padr\u00e3o: \u201cprotocol:\/\/service-code.region-code.amazonaws.com\u201d. Um exemplo de <strong>endpoint<\/strong> \u00e9 \u201c<a href=\"https:\/\/dynamodb.us-west-2.amazonaws.com%E2%80%9D\">https:\/\/dynamodb.us-west-2.amazonaws.com\u201d<\/a>. Para o ambiente de desenvolvimento, o endpoint vai apontar para a porta que est\u00e1 rodando o container do LocalStack.<\/p>\n\n<p>Vale ressaltar que as chaves e IDs de acesso podem ser simplesmente um \u201cteste\u201d para rodar de forma local.<\/p>\n\n<h2>\n  \n  \n  Recursos extras\n<\/h2>\n\n<ul>\n<li>Servi\u00e7os disponiv\u00e9is: <a href=\"https:\/\/docs.localstack.cloud\/user-guide\/aws\/feature-coverage\/\">localstack-services<\/a>\n<\/li>\n<li>Lista de AWS SDKs: <a href=\"https:\/\/aws.amazon.com\/developer\/tools\/\">AWS-SDKs<\/a>\n<\/li>\n<li>Docker: <a href=\"https:\/\/docs.docker.com\/engine\/install\/ubuntu\/\">Docker<\/a>\n<\/li>\n<li>LocalStack GitHub: <a href=\"https:\/\/github.com\/localstack\/localstack\">localstack-github<\/a>\n<\/li>\n<li>Aws Endpoints: <a href=\"https:\/\/docs.aws.amazon.com\/general\/latest\/gr\/rande.html\">aws-endpoint-config<\/a>\n<\/li>\n<li>Exemplo de uso: <a href=\"https:\/\/www.youtube.com\/watch?v=rwyhw9UYHkA\">localstack-test-erick-wendel<\/a>\n<\/li>\n<li>Repo com c\u00f3digo desenvolvido: <a href=\"https:\/\/github.com\/joserafaelSH\/poc-node-js-localstack\">joserafaelSH\/poc-node-js-localstack<\/a>\n<\/li>\n<\/ul>\n\n","category":["aws","docker","githubactions","node"]}]}}