You are on page 1of 13
Arquitetura de micro servicos em Node.js + MongoDB: Parte 2 No primeiro artigo desta série eu fiz um resumao do porque escolher uma arquitetura de micro servicos para seus sistemas vale a pena, quais as vantagens do modelo e indiquei Node e Mongo como uma dupla de tecnologias a serem consideradas para este tipo de abordagem. Finalizei o artigo passado explicando a arquitetura de um case de exemplo envolvendo um sistema para uma rede de cinemas. Neste artigo continuaremos a série, mas desta vez colocando a mao na massa: organizaremos a estrutura padrao que sera usada em nossos microservices, construiremos o primeiro deles e modelaremos o seu banco de dados. Veremos neste artigo: 1. Organizando a arquitetura 2. Organizando os dados 3. Conectando o banco 4, Consultando o banco Ent&o vamos 1a! Curso Nodes e MongoDB #1- Organizando a arquitetura Relembrando rapidamente o primeiro cendrio de uso da nossa arquitetura de microservices: GET Jeatalog/<> GET /moviesipremieres — — CINEMA CATALOG SERVICE Movies SERVICE CINEMA CATALOG DATABASE MOVIES DATABASE Consulta de Langamentos do Cinema Neste cendrio, iniciaremos nosso desenvolvimento com o microservice MOVIES e sua respectiva database. Cabe a esse servigo fornecer informacdes referentes ao catdlogo de filmes cujos direitos de exibicao foram comprados pela rede. Além do CRUD bisico, espera-se deste servico que seja possivel saber quais filmes sao os langamentos da rede, basicamente os que entraram nos tltimos 30 dias, que é mais ou menos a duragdo do status de langamento de um filme. Para estruturar este projeto como um todo, crie uma pasta central chamada cinema- microservice. Dentro dela colocaremos todos os microservices e dados dos mesmos, divididos em subpastas, por uma questo de organizacéio, como mostra a hierarquia de pastas abaixo. + cinema-microservice + movies-service + data + sre + cinema-catalog-service + data Obviamente quando fizermos o deploy dos mesmos, eles sero feitos de maneira independente, mas por uma questdo de organizagao do projeto e do repositério se vocé vier a versionar este projeto, faz sentido agrupé-los desta forma. Apenas lembre-se de nao versionar as pastas de dados e a node_modules de cada microservice, adicionando os respectivos caminhos no seu .gitignore, Dentro da subpasta movies-service, que é a que vamos focar neste artigo, temos as pastas data e src, Na pasta data armazenaremos os dados do nosso banco MongoDB (basta apontar o dbpath para cé na inicializagao do banco) deste microservice. Ja na pasta src armazenaremos 0s cédigos-fonte do mesmo. Dentro da pasta sre teremos a seguinte estrutura de pastas e arquivos, em todos os nossos microservices a partir deste aqui: + movies-service + sre + api + config + repository + server + indexjs + packagesjson Os arquivos index.js e packages.json sao auto-explicativos no cendrio de uma webapi em Nodejs. Na pasta api teremos os médulos das rotas deste microservice. Na pasta config, os médulos de configuracao e de acesso basico a dados (MongoDB cru). Na pasta repository nés teremos médulos seguindo o pattern Repository, uma versio mais “NoSQL” do pattern DAO (Data Access Object, focado em SQL). E basicamente esta é a estrutura, agora vamos aos dados! #2 - Organizando os dados Como estamos focando no microservice MOVIES, nossa base de dados sera bem tranquila pois teremos apenas uma colecao de documentos com todos os filmes dentro. Obviamente se vocé nao est acostumado com modelagem de dados em MongoDB (se é que modelagem ¢ o termo correto aqui), sugiro a série de artigos MongoDB para iniciantes em NoSQL e até mesmo o meu livro de MongoDB. Nossos filmes possuem a seguinte informagao: + identificador tnico + titulo + duracdo (em minutos) + imagem (capa promocional) + sinopse * data de langamento + categorias (aco, romance, etc) Obviamente vocé deve imaginar que poderiamos ter muitas outras informagées aqui como faixa etéria, trailer, formato de tela, idioma, etc. Vou ficar s6 com essas por uma questo de simplicidade. Em um banco relacional tradicional, como isso seria modelado? Algumas colunas da suposta tabela Filmes sdo bem dbvias como ID, Titulo, Duracao, Sinopse e DataLancamento. Mas e o campo imagem? Apesar dos bancos SQL suportarem BLOBs, nunca foi uma boa opcao por pesar demais nos SELECTS e no crescimento do banco como um todo. No entanto, o mesmo nao pode ser dito do MongoDB, onde podemos ter campos binarios facil mente sem abrir mao da performance. Ainda assim entenderei se vocé decidir por armazenar apenas a URL da imagem em uma URL publica (AWS $3?). Mas o que eu queria falar mesmo era das categorias. Esta é uma relagdo que pelas formas normais e levando muito a sério a ndo-repetigdo dos dados deveria ser N-N com 3 tabelas: uma Filmes, outra Categorias e a terceira CategoriaFilmes apenas com chaves-estrangeiras para as duas primeiras. No entanto, esta ndo é a abordagem sugerida para 0 MongoDB. Aqui até podemos ter uma colegdo de documentos Categorias, se necessdria, mas a abordagem mais comum é usar um campo multivalorado no documento de filme contendo as categorias do mesmo. simples assim. Obviamente vocé deve se preocupar em garantir que as categorias sejam escritas sempre da mesma forma, a nivel de aplicacao, caso contrario sera terrivel filtrar por elas mais tarde. Enfim, nossa colecdo de Filmes possuir documentos com a seguinte estrutura: aid: ObjectId("sacbaskbcksabckscstds67ds"), titulo: "Vingadores: Guerra Infinita", Sinopse: "Os herdis mais poderosos da’Marvel enfrentando o Thanos", duracao: 120, dataLancamento: ISoDate("2018-05-01T00:00:002"), imagem: "http://w. 1uiztools.com.br/vingadores-gi. jpg”, categorias: ["Aventura", "“Acdo"] waVanawnn t Para subir o banco de dados do nosso microservice, apenas use uma instancia do mongod apontando o dbpath para a pasta data dentro de cinema- microservice/movies-service/data. Obviamente em produgao vocé teré uma abordagem diferente, mas ainda de um banco para cada microservice. Curso Nodes e MongoDB #3 — Conectando o banco Agora que temos 0 modelo do nosso banco pronto e a estrutura de pastas organizada, vamos comecar a programar nosso primeiro microservice. Vamos comesar acessando a pasta do nosso movie-service/src via terminal, criando um arquivo index,js na raiz desta pasta e usando o comando ‘npm init’ nela que é para criar 0 package json do microservice. Depois, rode o comando abaixo pra garantir que teremos as nossas dependéncias minimas garantidas. i[npm install express nongodb dotenv-safe tape tap-spec helnet organ Tem varias coisas que devemos fazer e nao ha necessariamente uma ordem certa para que elas funcionem. 0 primeiro microservice seré um pouco chato e demorado de fazer, mas conforme a gente for avangando pelos demais vocé ira pegando 0 jeito. Sendo assim, vou comegar por algo que acho que é mais facil de todo mundo entender, 0 acesso a dados. Dentro da pasta movie-service/src/config vamos criar um arquivo mongodbjs, com 0 seguinte contetido dentro: I | const MongoClient = require” mongodb”).MongoClient; 2 | var connection = null; 3 | var db = null; function connect (cal 1back){ fCconnection) return callbacknull, db); MongoClient. connect(process.env.MONGO_CONNECTION, Cerr, conn) => { | ‘ifCerr) return callbackCerr, null); jar else { 2 connection = conn; 1B db = conn. db(process. env. DATABASE_NAME); 14 return callback(null, db); jas + 16] jaz] 18 | 19 | function disconnect 26 if(!connection) return true; |21|connection.close); 22 connection = null; |23} return true; 24\y (25 26 |module.exports = { connect, disconnect } Note que esse arquivo mongodb js espera que existam duas variaveis de ambiente com a string de conexdo ao banco. Essas varidveis de ambiente devem ser definidas em um arquivo sem nome com a extensdo env’ na raiz do movie-service/src/, sendo que o pacote dotenv-safe que instalamos anteriormente exige a existéncia de um “env.example’ com a definicao das varidveis de ambiente existentes. 1]#.env, don't commit to repo 2 | MONGO_CONNECTION=mongodb ://Localhost :27017 3 | DATABASE_NAME=novie-service 4 | PoRT=3000 1[#-env.exanple, commit to repo 2 | MONGO_CONNECTION 3 | DATABASE_NAME= 4| porT= Para nos certificarmos que este médulo esta funcionando, vamos escrever um teste unitério para ele? Se vocé nunca ouviu falar em testes unitarios antes, recomendo ler este post sobre TDD. Na mesma pasta movie-service/src/config crie um arquivo mongodb.test.js e dentro escreva o seguinte cédigo, que nada mais faz do que usar a biblioteca tape (que foi instalada anteriormente no nosso npm install) pra testar a conexdo: 1 [eonst test = require(’ tape"); 2 | const mongodb = require’ . /niongodb'); 4 | function runTests(t 5 ‘test('MongoDB Connection’, (t) => { 6 mongodb.connect(Cerr, conn) => { \7 ‘t.assert(conn, "Connection established"); 8 teendQ; |9 Ds wl) op jar 12] test('MongoDB Disconnection’, Ct) => { 3B 4 15 16 7 18 t.assert(mongodb.disconnect(), "Disconnected"; teendC: bd + module. exports { runTests } Como teremos muitos arquivos de teste diferentes em nossa aplicagdo, cada um em sua pasta, vamos criar na raiz de movie-service/sre um index.test,js que vai indexar todos nossos testes, a comecar por esse primeiro, como abaixo: T 2 require(”dotenv-safe"). load); require(”. /config/nongodb. test"). runTests( Note que também carreguei o médulo do dotenv-safe pois precisamos que as varidveis de ambiente estejam carregadas para que nossos testes funcionem. Falando em funcionar, antes de rodar este teste abra o seu packages,json que fica na raiz. de movie-service/src e edite-o para que os scripts de start e de test fiquem igual abaixo: Tit 2] “nome”: “movie-service", 3] "version": "1.0.0", 4] "description": "Service to provide detailed movies info.", 5 2 “index.js", 6 { 7 inode index", 8 “node index.test | tap-spec” 9 10 1 12 B , 14 15 “tap-spec": , 16] "tape": "4.9.0" 17] 3} 18 |} Se vocé rodar agora sua aplicagdo com o comando abaixo, o seu unit test deve ser executado. Caso seu banco esteja online, obviamente. GI Zmovie-service/src> npn test MacBook-Pro-de-Luiz:sre luizfduarteir$ npn test > movie-servicegt.0.0 test /Users/luiztduartejr/NodeProjects/cinema-microservice Imovies-service/src > node index.test | tap-spec Moni nection v Mongo08 Disconnection v total: 2 passing: 2 duration: 82ns MongoDB - Testes OK #4 - Consultando o banco Agora que sabemos que nossa conexao com o banco funciona, vamos criar nosso médulo de repositério para que possamos fornecer os dados do MongoDB da maneira que as chamadas ao nosso servigo esperam. Nao vou fazer um CRUD completo aqui pois j4 abordei CRUDs de Node com Mongo em outras oportunidades aqui no blog, é sé procurar. Dentro do nosso case de exemplo levarei em conta que precisamos implementar apenas o R (Read) para fornecer dados de filmes especificos (por id) e dos filmes que sdo langamentos nos cinemas (langados nos wltimos 30 dias). Para criar nosso médulo de repositério (que por sua vez usaré o médulo mongodbjs) entre na pasta movie-service/sre/repository e crie dois arquivos, 0 repository;s e 0 repository.testjs, sendo que o primeiro deve ter o contetido abaixo: ‘const mongodb = require("../config/mongodb"); ] function getAlMovies(caltback){ | mmongodb.connect(Cerr, db) => { db. collection(’movies"). Find(). toRrray(cal back); | yp ¥ | | 9 | function getMovieByIdCid, callback){ | 10| — mongodb.connect(Cerr, db) => { jan db. collection("movies"). findOneC{_id: require("mongodb").ObjectIdcia)}, | 2) ops |13)3 | u | 15 | function getMoviePremiers(cal lback){ | 16 a7 var monthAgo = new Date(); | 18] monthAgo. setMonth(ronthAgo.getMonth() - 1); 19] monthAgo.setHours(0, 9, 0); 20| —_monthAgo. setMiLiseconds(2); | 2 22| — mongodb. connect(Cerr, ab) => { (3B db.collection("movies").find({ dataLancamento: { $gte: monthAgo } }).toA 24) os | 2s] | | 22] function disconect | 28 return mongodb.disconnect(); j2a|} | 30 | 31|module.exports = { getAllMovies, getMovieByld, getMoviePremiers, disconnect } | Aqui temos uma fungio para cada um dos trés métodos elementares que precisamos ter na APie uma tiltima para desconectar 0 repositério do banco de dados, fungao esta que sera usada em certas ocasiGes como em testes unitdrios. Eno segundo arquivo, repository.testjs, colocamos os testes do primeiro, de maneira andloga ao que fizemos com 0 médulo mongodb.test js: 1 | const test = require(" tape") ] 2 | const repository = require('./repository') | 3 _f |finetion eutestsot | 5 6 var id = null; 17 | 8 test('Repository GetALIMovies', (t) => { le repository. getAllMovies(Cern, movies) | [32 if(movies 8& movies.length > 0) id = movies[0]._id; | un wz teassert(lerr 8& movies & movies.length > 0, “ALL Movies Returned") 1B tend); 4 ps jas » | 16 jaz test('Repository GetMovieByld", Ct) => { | 18 if(lid) { i119 tassert(false, "Movie by Id Returned’ | 20 trend; jan return; | 22 } |23 | 24 repository. getMovieByldCid, err, movie) => { [25 t.assert(lerr 8& movie, "Movie by Id Returned | 26 teendQ); (2 Ds | 2s} |29 | 30 test('Repository GetMoviePremiers', (t) => { 31 repository. getMoviePreniers(Cerr, movies) | 32 tvassert(!err 8& movies 8& movies. length > 0, "Wovie Premiers Returr (33 t.endQ); | 34 Di [35] op | 36 |37] test¢'Repository Disconnect", (4) => { | 38 t.assert(repository.disconnect(), "Disconnect 0k"); (39 tendO; | 4/3) vat|t | 42 | 43 | module.exports = { runTests } | E por fim, adicione mais uma linha em nosso movie-service/sre/index.testjs para que rode também este novo médulo de teste: 1 2 3 require(”dotenv-safe"). load(); require(”. /config/mongodb. test"). runTestsC); require(”./repository/repository. test"). runTests(); Obviamente que estes tiltimos testes ndo passardo se vocé rodar um ‘npm test’ no terminal, mas isso porque nosso banco de dados ndo possui qualquer informagdo de filme, que vocé pode resolver abrindo uma instancia do utilitario ‘mongo’ no terminal (executando um use no banco ‘movie-service’) e inserindo 0 comando abaixo para adicionar uma carga de filmes: do. movies. insertCL{ titulo: "Os Vingadores: Guerra Infinite”, sinopse: "Os herdis mais poderosos da Marvel enfrentando o Thanos", duracao: 120, dataLancamento: IS0Date("2018-05-01100:00:002"), imagem: “http://www. m.br/vingadores-gi.jpa", categorias: titulo: "0s Vingadores: Era de Ultron", Sinopse: "Os herdis mais poderosos da Marvel enfrentando o Ultron", duracao: 110, dataLancamento: TS0Date("2016-05-01700:00:002" imagem: “http://www. Luiztools.com.br/vingadores: categorias: ["Aventura", "Acao"] i t titulo: "Os Vingadores", sinopse: “Os herdis mais poderosos da Marvel enfrentando o Loki", duracao: 100, dataLancamento: IsoDate("2014-05-01700:00:002"), imagem: "http://w. luiztools.com.br/vingadores. jpq", categorias: ["Aventura”, "Agao"] sap) Agora sim, ao rodar o ‘npm test’, seus testes unitarios devem passar com sucesso: *cMacBook-Pro-de-Luiz:sre luizfduartejrS npm test 1 > movie-service@1.9.0 test /Users/luizfduartejr/NodeProjects/cinema-microservice/m ovies-service/sre > node index.test | tap-spec MongodB Connection ¥ Connection estal MongoDB Disconnection ¥ Disconnect Repository GetallMovies ’ eturned abaxs , on y Td Returned Movie?) Repository Tests - OK Com estes testes todos passando, temos a certeza de que a parte do banco de dados da nossa futura API estaré 100% operacional, cabendo agora programarmos a API em si, que ir trabalhar com estes dados que viemos “brincando” até entao. No entanto, a programacao da API movie-service ficou para a terceira parte desta série de artigos! Curtiu 0 post? Entéo clica no banner abaixo e dé uma conferida no meu livro sobre programagao web com Node,js! Procjrelmeleelo o Ensino completo e pratico, do front-end a6 back-end! 0 que achou desse artigo? al (Total: 6 Média: 5] (0 09/05/2018 & Luiz Duarte Desenvolvimento, Nodejs _# mongodb, nodejs 2 COMENTARIOS —_LuizTools @ Iniciar sessao © Recomendar & Partihar Mostrar primeiro os mais votados @ Excreva o seu comentario _ IWICIESESSAO COMO gu reGisTE-SE NO DISaus (2) Nome Davi Gomes «ha 5 cias Opa Luiz, parabéns pelo artigo. Estou aprendendo muito com vocé!! Eu fique com 2 diividas, se voeé puder me ajudar agradeceria muito. 1. Em alguns livros jé li que micro servigos devem fazer apenas 1 coisa muito bem, ‘mas no seu exemplo vocé colocou 1 CRUD completo no micro servico. Essa abordagem é apenas para ilustrar o exemplo ou no dia a dia voeé faz desse jeito? 2, Se eu rodar esse micro servigo dentro de um container, o banco de dados ficara dentro do container, caso o container caia eu perco todas as informagies? Como vocé trata isso no dia a dia? Desde jé agradego muito pela contribui ‘Av + Responder + Partihar» Luiz Fernando Jr > Davi Gomes » ha 5 dias Bom dia Davi. 1. Cada micro servigo deve ter uma ‘inica responsabilidade (SRP), nao exatamente uma tinica function, caso contrario vocé deveria estar usando arquitetura serverless (que aliés também é bacana) ao invés de microservigos. O quao "micro" deve ser um microservice é uma discus tio longa e desnecesséria quanto discutir qual é a melhor linguagem de programagdo, mas entendo sua diwvida, hehehe. 2. Depende da sua estratégia, gosto de deixar junto, mas também jé vi rodando separado. A forma de garantir alta disponibilidade de dados e de aplicagao continua sendo a mesma: replicagao. ‘AY + Responder + Partihar> 0 TAWBEM NO LUZTOOLS De 1 centavo a 1kimés em 1 ano: Como criar um bot de compra e construindo renda passiva com venda de Bitcoin usando Node,js — iw Lucas De Oliveira Goncalves — Inspirado na experiéncia do Henrique eu iia produzir apps ¢ jogar na loja. 0 do meu livro de Node.js Orgulhosamente mantida com WordPress Luiz Fernando Jr— Bom dia Fernando. A ideia é essa mesmo, que cada um ajuste a légica conforme as regras que Tutorial MongoDB para iniciantes em NoSQL - Parte 4

You might also like