├── README.md ├── app_docker_compose ├── README.md ├── api │ ├── api.products │ │ ├── Dockerfile │ │ ├── app.js │ │ ├── app │ │ │ ├── commands │ │ │ │ └── app.product.command.js │ │ │ ├── controllers │ │ │ │ └── app.products.controller.js │ │ │ ├── events │ │ │ │ ├── app.product.publish.event.js │ │ │ │ ├── app.products.subscribe.event.js │ │ │ │ └── app.sale.subscribe.event.js │ │ │ ├── models │ │ │ │ └── app.product.model.js │ │ │ └── repository │ │ │ │ └── app.products.repository.js │ │ ├── config │ │ │ ├── amqp.config.js │ │ │ ├── db.config.js │ │ │ └── microservices.db │ │ └── package.json │ └── api.sales │ │ ├── Dockerfile │ │ ├── app.js │ │ ├── app │ │ ├── commands │ │ │ └── app.sale.command.js │ │ ├── controllers │ │ │ └── app.sale.controller.js │ │ ├── events │ │ │ ├── app.sale.publish.event.js │ │ │ └── app.sale.subscribe.event.js │ │ ├── models │ │ │ └── app.sale.model.js │ │ └── repository │ │ │ └── app.sale.repository.js │ │ ├── config │ │ ├── amqp.config.js │ │ ├── db.config.js │ │ └── microservices.db │ │ └── package.json ├── docker-compose.yml └── web │ ├── Dockerfile │ ├── app.js │ ├── app │ ├── app.js │ ├── controllers │ │ ├── app.home.ctrl.js │ │ └── app.sales.ctrl.js │ ├── index.html │ ├── services │ │ └── app.home.srv.js │ └── views │ │ ├── home.html │ │ └── sales.html │ ├── package.json │ └── public │ └── css │ └── style.css └── app_dockerfile ├── Dockerfile ├── app.js ├── dev-config.json ├── index.js ├── package.json ├── public ├── images │ ├── cat.ico │ └── grass.svg └── stylesheets │ └── style.css ├── routes └── index.js └── views ├── index.jade └── layout.jade /README.md: -------------------------------------------------------------------------------- 1 | # example-course-containers 2 | 3 | Letscode repository for container examples with Dockerfile and Docker Compose 4 | -------------------------------------------------------------------------------- /app_docker_compose/README.md: -------------------------------------------------------------------------------- 1 | #Arquitetura de Microsserviços. 2 | 3 | Código fonte da palestra sobre Arquitetura de Microsserviços, Overview, Implementação e Deploy . 4 | 5 | ## Pré-configuração e instalação do ambiente. 6 | 7 | Node.js - Tecnologia de servidor utilizada 8 | [Download](https://nodejs.org/en/download/) 9 | 10 | RabbitMQ - Tecnologia utilizada para a troca de mensagens entre serviços 11 | [Download](https://www.rabbitmq.com/download.html) 12 | 13 | Docker 14 | [Download](https://www.docker.com/community-edition) 15 | 16 | Visual Studio Code - Opcional 17 | [Download](https://code.visualstudio.com/download) 18 | 19 | ## Rodando a aplicação 20 | 21 | Interface web 22 | ./web 23 | 24 | > npm install 25 | 26 | > node app 27 | 28 | API - Produtos 29 | ./api/api.products 30 | 31 | > npm install 32 | 33 | > node app 34 | 35 | API - Vendas 36 | ./api/api.sales 37 | 38 | > npm install 39 | 40 | > node app 41 | 42 | ## Docker compose 43 | > docker-compose up --build 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10.23 2 | 3 | RUN mkdir -p /api.products 4 | 5 | WORKDIR /api.products 6 | 7 | COPY package.json /api.products 8 | 9 | RUN npm install 10 | 11 | COPY . /api.products 12 | 13 | EXPOSE 3000 14 | 15 | CMD [ "node", "app" ] -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/app.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var app = express(); 3 | 4 | var bodyParser = require("body-parser"); 5 | var router = express.Router(); 6 | var http = require('http'); 7 | var productController = require('./app/controllers/app.products.controller'); 8 | var subscriberProduct = require('./app/events/app.products.subscribe.event'); 9 | var subscriberSale = require('./app/events/app.sale.subscribe.event'); 10 | var db = require('./config/db.config'); 11 | var cors = require("cors"); 12 | 13 | subscriberProduct.subscribe(); 14 | subscriberSale.subscribe(); 15 | 16 | app.set('PORT', process.env.PORT || 3000); 17 | 18 | // aqui resolvemos o problema do cors 19 | app.options('*', cors()); 20 | 21 | app.all('/*', function (req, res, next) { 22 | res.header("Access-Control-Allow-Origin", "*"); 23 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); 24 | res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type"); 25 | next(); 26 | }); 27 | 28 | app.use(bodyParser.json()); 29 | app.use(bodyParser.urlencoded({ 30 | "extended": false 31 | })); 32 | 33 | app.use('/', productController); 34 | 35 | db 36 | .authenticate() 37 | .then(() => { 38 | console.log('Conectado ao sqlite com sucesso!'); 39 | db.sync().then(function () { 40 | console.log('Sincronizando banco de dados'); 41 | }); 42 | }) 43 | .catch(err => { 44 | console.error('Erro ao conectar com sqlite:', err); 45 | }); 46 | 47 | http.createServer(app).listen(app.get('PORT'), function () { 48 | console.log('Rodando na porta ' + app.get('PORT')); 49 | }); -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/app/commands/app.product.command.js: -------------------------------------------------------------------------------- 1 | var poublisher = require("../events/app.product.publish.event"); 2 | const db = require('../../config/db.config'); 3 | const Product = require("../models/app.product.model"); 4 | 5 | module.exports = { 6 | 7 | create: function (model, callback) { 8 | db.transaction().then(function (t) { 9 | Product.create({ 10 | code: model.code, 11 | description: model.description, 12 | unitPrice: model.unitPrice, 13 | quantity: model.quantity 14 | }, { 15 | transaction: t 16 | }).then(function (product) { 17 | t.commit(); 18 | poublisher.created(product.dataValues); 19 | callback({ 20 | isValid: true 21 | }); 22 | }).catch(function (error) { 23 | t.rollback(); 24 | callback({ 25 | isValid: false, 26 | error: error 27 | }); 28 | }); 29 | }); 30 | }, 31 | update: function (id, model) { 32 | db.transaction().then(function (t) { 33 | Product.findById(id, { 34 | transaction: t, 35 | }).then(product => { 36 | product.update({ 37 | code: model.code, 38 | description: model.description, 39 | unitPrice: model.unitPrice, 40 | quantity: model.quantity 41 | }, { 42 | transaction: t, 43 | }).then(function () { 44 | t.commit(); 45 | if (typeof callback === 'function') { 46 | callback({ 47 | isValid: true 48 | }); 49 | } 50 | }).catch(function (error) { 51 | t.rollback(); 52 | callback({ 53 | isValid: false, 54 | error: error 55 | }); 56 | }); 57 | }, error => { 58 | callback({ 59 | isValid: false, 60 | error: 'Prodto não encontrado' 61 | }); 62 | }); 63 | 64 | 65 | }); 66 | 67 | }, 68 | 69 | delete: function (id, ) { 70 | db.transaction().then(function (t) { 71 | Product.findById(id, { 72 | transaction: t, 73 | }).then(product => { 74 | product.destroy({ 75 | transaction: t, 76 | }).then(function () { 77 | t.commit(); 78 | if (typeof callback === 'function') { 79 | callback({ 80 | isValid: true 81 | });} 82 | }).catch(function (error) { 83 | t.rollback(); 84 | callback({ 85 | isValid: false, 86 | error: error 87 | }); 88 | }); 89 | }, error => { 90 | callback({ 91 | isValid: false, 92 | error: 'Prodto não encontrado' 93 | }); 94 | }); 95 | 96 | }); 97 | 98 | }, 99 | 100 | updateStockByEvent: function (data) { 101 | db.transaction().then(function (t) { 102 | Product.findById(data.productId, { 103 | transaction: t, 104 | }).then(product => { 105 | console.info('Produto antes da alteração', product.dataValues); 106 | product.update({ 107 | quantity: (product.dataValues.quantity - data.quantity) 108 | }, { 109 | transaction: t, 110 | }).then(function () { 111 | t.commit(); 112 | }).catch(function (error) { 113 | t.rollback(); 114 | }); 115 | }, error => {}); 116 | }); 117 | 118 | } 119 | }; -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/app/controllers/app.products.controller.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var commands = require("../commands/app.product.command"); 4 | var repository = require("../repository/app.products.repository"); 5 | router.route("/") 6 | .get(function (req, res) { 7 | repository.all(response => { 8 | res.json(response); 9 | }); 10 | 11 | }) 12 | .post(function (req, res) { 13 | commands.create(req.body, response => { 14 | res.json(response); 15 | }); 16 | }); 17 | router.route("/:id") 18 | .get(function (req, res) { 19 | repository.get(req.params.id, response => { 20 | res.json(response); 21 | }); 22 | }) 23 | .put(function (req, res) { 24 | commands.update(req.params.id, req.body, response => { 25 | res.json(response); 26 | }); 27 | }) 28 | .delete(function (req, res) { 29 | commands.delete(req.params.id, response => { 30 | res.json(response); 31 | }); 32 | }); 33 | module.exports = router; -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/app/events/app.product.publish.event.js: -------------------------------------------------------------------------------- 1 | const connector = require('../../config/amqp.config'); 2 | const amqp = require('amqp'); 3 | const conn = amqp.createConnection(connector.conn); 4 | var exchange; 5 | conn.on('error', function (e) { 6 | console.log("Erro ao conectar no RabbitMQ: ", e); 7 | }); 8 | conn.on('ready', function (data) { 9 | console.log("Conectado com o RabbitMQ"); 10 | exchange = conn.exchange('product', { 11 | type: 'fanout' 12 | }); 13 | }); 14 | module.exports = { 15 | created: function (data) { 16 | console.info("[Evento] Novo evento disparado"); 17 | console.info("Novo produto", data); 18 | exchange.publish('', { 19 | "data": data, 20 | "action": "CREATE" 21 | }, {}) 22 | }, 23 | updated: function (data) { 24 | console.info("[Evento] Novo evento disparado"); 25 | console.info("Produto atualizado", data); 26 | exchange.publish('', { 27 | "data": data, 28 | "action": "UPDATE" 29 | }, {}) 30 | }, 31 | deleted: function (data) { 32 | console.info("[Evento] Novo evento disparado"); 33 | console.info("Produto removido", data); 34 | exchange.publish('', { 35 | "data": data, 36 | "action": "DELETE" 37 | }, {}) 38 | } 39 | }; -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/app/events/app.products.subscribe.event.js: -------------------------------------------------------------------------------- 1 | const connector = require('../../config/amqp.config'); 2 | const amqp = require('amqp'); 3 | const conn = amqp.createConnection(connector.conn); 4 | 5 | module.exports = { 6 | subscribe: function () { 7 | conn.on('error', function (e) { 8 | console.log("Erro ao se conectar com o RabbitMQ ", e); 9 | }); 10 | conn.on('ready', function () { 11 | console.log('Conectado com o RabbitMQ'); 12 | conn.exchange("product", options = { 13 | type: 'fanout' 14 | }, function (exchange) { 15 | console.log('Inscrito no product exchange'); 16 | conn.queue("product.event.queue", function (queue) { 17 | queue.bind(exchange, ''); 18 | queue.subscribe(function (message) { 19 | console.log("Novo evento recebido"); 20 | console.info('Recebendo evento de produto', message); 21 | }); 22 | }); 23 | 24 | }); 25 | }); 26 | } 27 | } -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/app/events/app.sale.subscribe.event.js: -------------------------------------------------------------------------------- 1 | const connector = require('../../config/amqp.config'); 2 | const amqp = require('amqp'); 3 | const conn = amqp.createConnection(connector.conn); 4 | const command = require("../commands/app.product.command"); 5 | module.exports = { 6 | subscribe: function () { 7 | conn.on('error', function (e) { 8 | console.log("Erro ao se conectar com o RabbitMQ ", e); 9 | }); 10 | conn.on('ready', function () { 11 | console.log('Conectado com o RabbitMQ'); 12 | conn.exchange("sale", options = { 13 | type: 'fanout' 14 | }, function (exchange) { 15 | console.log('Assinando a fila de vendas'); 16 | conn.queue("sale.event.queue", function (queue) { 17 | queue.bind(exchange, ''); 18 | queue.subscribe(function (message) { 19 | console.log("Novo evento recebido"); 20 | console.info('Recebendo evento de venda', message); 21 | command.updateStockByEvent(message.data); 22 | }); 23 | }); 24 | 25 | }); 26 | }); 27 | } 28 | } -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/app/models/app.product.model.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const db = require('../../config/db.config'); 3 | const Product = db.define('Product', { 4 | id: { 5 | autoIncrement: true, 6 | primaryKey: true, 7 | type: Sequelize.INTEGER 8 | }, 9 | code: { 10 | type: Sequelize.STRING, 11 | 12 | }, 13 | description: { 14 | type: Sequelize.STRING 15 | }, 16 | unitPrice: { 17 | type: Sequelize.DECIMAL 18 | }, 19 | quantity: { 20 | type: Sequelize.INTEGER 21 | }, 22 | minimumQuantity: { 23 | type: Sequelize.INTEGER 24 | }, 25 | }); 26 | module.exports = Product; -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/app/repository/app.products.repository.js: -------------------------------------------------------------------------------- 1 | const Product = require("../models/app.product.model"); 2 | module.exports = { 3 | all: function () { 4 | if (typeof callback === 'function') { 5 | Product.all().then(products => { 6 | callback(products); 7 | }, error => { 8 | callback({ 9 | error: error 10 | }); 11 | }); 12 | } 13 | }, 14 | get: function (id) { 15 | if (typeof callback === 'function') { 16 | Product.findById(id).then(product => { 17 | callback(product); 18 | }) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/config/amqp.config.js: -------------------------------------------------------------------------------- 1 | const conn = { 2 | host: process.env.RMQ_HOST || 'localhost' || '192.168.99.100', 3 | port: 5672, 4 | login: 'guest', 5 | password: 'guest', 6 | ssl: { 7 | enabled: false 8 | } 9 | }; 10 | const options = { 11 | defaultExchangeName: 'events', 12 | reconnect: true, 13 | reconnectBackoffStrategy: 'linear', 14 | reconnectExponentialLimit: 120000, 15 | reconnectBackoffTime: 1000 16 | }; 17 | exports.conn = conn; 18 | exports.options = options; 19 | -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/config/db.config.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const sequelize = new Sequelize('', '', '', { 3 | dialect: 'sqlite', 4 | storage: './config/microservices.db' 5 | }); 6 | module.exports = sequelize; -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/config/microservices.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talits/example-course-containers/2b06f380d6dceebf640620d1971ccfa71ba81958/app_docker_compose/api/api.products/config/microservices.db -------------------------------------------------------------------------------- /app_docker_compose/api/api.products/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api.products", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Talita Bernardes", 10 | "license": "ISC", 11 | "dependencies": { 12 | "amqp": "^0.2.6", 13 | "cors": "^2.8.4", 14 | "express": "^4.16.1", 15 | "body-parser": "^1.18.0", 16 | "sequelize": "6.0.0-beta.1", 17 | "sqlite3": "^5.0.2" 18 | } 19 | } -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10.23 2 | 3 | RUN mkdir -p /api.sales 4 | 5 | WORKDIR /api.sales 6 | 7 | COPY package.json /api.sales 8 | 9 | RUN npm install 10 | 11 | COPY . /api.sales 12 | 13 | EXPOSE 3001 14 | 15 | CMD [ "node", "app" ] -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/app.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var app = express(); 3 | 4 | var bodyParser = require("body-parser"); 5 | var router = express.Router(); 6 | var http = require('http'); 7 | var saleController = require('./app/controllers/app.sale.controller'); 8 | var subscriber = require('./app/events/app.sale.subscribe.event'); 9 | var db = require('./config/db.config'); 10 | var cors = require("cors"); 11 | 12 | subscriber.subscribe(); 13 | 14 | app.set('PORT', process.env.PORT || 3001); 15 | 16 | // resolvendo problema com o CORS 17 | app.options('*', cors()); 18 | 19 | app.all('/*', function (req, res, next) { 20 | res.header("Access-Control-Allow-Origin", "*"); 21 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); 22 | res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type"); 23 | next(); 24 | }); 25 | 26 | app.use(bodyParser.json()); 27 | 28 | app.use(bodyParser.urlencoded({ 29 | "extended": false 30 | })); 31 | 32 | app.use('/', saleController); 33 | 34 | db 35 | .authenticate() 36 | .then(() => { 37 | console.log('Conectado ao sqlite com sucesso!'); 38 | db.sync().then(function () { 39 | console.log('Sincronizando banco de dados'); 40 | }); 41 | }) 42 | .catch(err => { 43 | console.error('Erro ao conectar com sqlite:', err); 44 | }); 45 | 46 | http.createServer(app).listen(app.get('PORT'), function () { 47 | console.log('Rodando na porta ' + app.get('PORT')); 48 | }); -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/app/commands/app.sale.command.js: -------------------------------------------------------------------------------- 1 | var poublisher = require("../events/app.sale.publish.event"); 2 | const db = require('../../config/db.config'); 3 | const Sale = require("../models/app.sale.model"); 4 | module.exports = { 5 | create: function (model, callback) { 6 | db.transaction().then(function (t) { 7 | Sale.create({ 8 | productId: model.productId, 9 | quantity: model.quantity, 10 | total: model.total 11 | }, { 12 | transaction: t 13 | }).then(function (sale) { 14 | t.commit(); 15 | poublisher.created(sale.dataValues); 16 | callback({ 17 | isValid: true 18 | }); 19 | }).catch(function (error) { 20 | t.rollback(); 21 | callback({ 22 | isValid: false, 23 | error: error 24 | }); 25 | }); 26 | }); 27 | }, 28 | update: function (id, model, callback) { 29 | db.transaction().then(function (t) { 30 | Sale.findById(id, { 31 | transaction: t, 32 | }).then(sale => { 33 | sale.update({ 34 | productId: model.productId, 35 | quantity: model.quantity, 36 | total: model.total 37 | }, { 38 | transaction: t, 39 | }).then(function () { 40 | t.commit(); 41 | callback({ 42 | isValid: true 43 | }); 44 | }).catch(function (error) { 45 | t.rollback(); 46 | callback({ 47 | isValid: false, 48 | error: error 49 | }); 50 | }); 51 | }, error => { 52 | callback({ 53 | isValid: false, 54 | error: 'Prodto não encontrado' 55 | }); 56 | }); 57 | 58 | 59 | }); 60 | 61 | }, 62 | delete: function (id) { 63 | db.transaction().then(function (t) { 64 | Sale.findById(id, { 65 | transaction: t, 66 | }).then(sale => { 67 | sale.destroy({ 68 | transaction: t, 69 | }).then(function () { 70 | t.commit(); 71 | if (typeof callback === 'function') { 72 | callback({ 73 | isValid: true 74 | }); 75 | } 76 | }).catch(function (error) { 77 | t.rollback(); 78 | callback({ 79 | isValid: false, 80 | error: error 81 | }); 82 | }); 83 | }, error => { 84 | callback({ 85 | isValid: false, 86 | error: 'Prodto não encontrado' 87 | }); 88 | }); 89 | 90 | }); 91 | 92 | } 93 | }; -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/app/controllers/app.sale.controller.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var commands = require("../commands/app.sale.command"); 4 | var repository = require("../repository/app.sale.repository"); 5 | router.route("/") 6 | .get(function (req, res) { 7 | repository.all(response => { 8 | res.json(response); 9 | }); 10 | 11 | }) 12 | .post(function (req, res) { 13 | commands.create(req.body, response => { 14 | res.json(response); 15 | }); 16 | }); 17 | router.route("/:id") 18 | .get(function (req, res) { 19 | repository.get(req.params.id, response => { 20 | res.json(response); 21 | }); 22 | }) 23 | .put(function (req, res) { 24 | commands.update(req.params.id, req.body, response => { 25 | res.json(response); 26 | }); 27 | }) 28 | .delete(function (req, res) { 29 | commands.delete(req.params.id, response => { 30 | res.json(response); 31 | }); 32 | }); 33 | module.exports = router; -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/app/events/app.sale.publish.event.js: -------------------------------------------------------------------------------- 1 | const connector = require('../../config/amqp.config'); 2 | const amqp = require('amqp'); 3 | const conn = amqp.createConnection(connector.conn); 4 | var exchange; 5 | conn.on('error', function (e) { 6 | console.log("Erro ao conectar no RabbitMQ: ", e); 7 | }); 8 | conn.on('ready', function (data) { 9 | console.log("Conectado com o RabbitMQ"); 10 | exchange = conn.exchange('sale', { 11 | type: 'fanout' 12 | }); 13 | }); 14 | module.exports = { 15 | created: function (data) { 16 | console.info("[Evento] Novo evento disparado"); 17 | console.info("Nova venda", data); 18 | exchange.publish('', { 19 | "data": data, 20 | "action": "CREATE" 21 | }, {}) 22 | }, 23 | updated: function (data) { 24 | console.info("[Evento] Novo evento disparado"); 25 | console.info("venda atualizada", data); 26 | exchange.publish('', { 27 | "data": data, 28 | "action": "UPDATE" 29 | }, {}) 30 | }, 31 | deleted: function (data) { 32 | console.info("[Evento] Novo evento disparado"); 33 | console.info("venda removido", data); 34 | exchange.publish('', { 35 | "data": data, 36 | "action": "DELETE" 37 | }, {}) 38 | } 39 | }; -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/app/events/app.sale.subscribe.event.js: -------------------------------------------------------------------------------- 1 | const connector = require('../../config/amqp.config'); 2 | const amqp = require('amqp'); 3 | const conn = amqp.createConnection(connector.conn); 4 | 5 | module.exports = { 6 | subscribe: function () { 7 | conn.on('error', function (e) { 8 | console.log("Erro ao se conectar com o RabbitMQ ", e); 9 | }); 10 | conn.on('ready', function () { 11 | console.log('Conectado com o RabbitMQ'); 12 | conn.exchange("sale", options = { 13 | type: 'fanout' 14 | }, function (exchange) { 15 | conn.queue("sale.event.queue", function (queue) { 16 | queue.bind(exchange, ''); 17 | queue.subscribe(function (message) { 18 | console.log("Novo evento recebido"); 19 | console.info('Recebendo evento de venda', message); 20 | }); 21 | }); 22 | 23 | }); 24 | }); 25 | } 26 | } -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/app/models/app.sale.model.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const db = require('../../config/db.config'); 3 | const Sale = db.define('Sale', { 4 | id: { 5 | autoIncrement: true, 6 | primaryKey: true, 7 | type: Sequelize.INTEGER 8 | }, 9 | productId: { 10 | type: Sequelize.INTEGER, 11 | 12 | }, 13 | quantity: { 14 | type: Sequelize.STRING 15 | }, 16 | total: { 17 | type: Sequelize.DECIMAL 18 | } 19 | }); 20 | module.exports = Sale; -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/app/repository/app.sale.repository.js: -------------------------------------------------------------------------------- 1 | const Product = require("../models/app.sale.model"); 2 | module.exports = { 3 | all: function () { 4 | if (typeof callback === 'function') { 5 | Product.all().then(products => { 6 | callback(products); 7 | }, error => { 8 | callback({ 9 | error: error 10 | }); 11 | }); 12 | } 13 | }, 14 | get: function (id) { 15 | if (typeof callback === 'function') { 16 | Product.findById(id).then(product => { 17 | callback(product); 18 | }) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/config/amqp.config.js: -------------------------------------------------------------------------------- 1 | const conn = { 2 | host: process.env.RMQ_HOST || 'localhost' || '192.168.99.100', 3 | port: 5672, 4 | login: 'guest', 5 | password: 'guest', 6 | ssl: { 7 | enabled: false 8 | } 9 | }; 10 | const options = { 11 | defaultExchangeName: 'events', 12 | reconnect: true, 13 | reconnectBackoffStrategy: 'linear', 14 | reconnectExponentialLimit: 120000, 15 | reconnectBackoffTime: 1000 16 | }; 17 | exports.conn = conn; 18 | exports.options = options; 19 | -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/config/db.config.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const sequelize = new Sequelize('', '', '', { 3 | dialect: 'sqlite', 4 | storage: './config/microservices.db' 5 | }); 6 | module.exports = sequelize; -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/config/microservices.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talits/example-course-containers/2b06f380d6dceebf640620d1971ccfa71ba81958/app_docker_compose/api/api.sales/config/microservices.db -------------------------------------------------------------------------------- /app_docker_compose/api/api.sales/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api.sales", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Talita Bernardes", 10 | "license": "ISC", 11 | "dependencies": { 12 | "amqp": "^0.2.6", 13 | "cors": "^2.8.4", 14 | "express": "^4.16.1", 15 | "body-parser": "^1.18.0", 16 | "sequelize": "6.0.0-beta.1", 17 | "sqlite3": "^5.0.2" 18 | } 19 | } -------------------------------------------------------------------------------- /app_docker_compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | services: 3 | products: 4 | restart: always 5 | build: ./api/api.products/. 6 | ports: 7 | - "3000:3000" 8 | container_name: products 9 | depends_on: 10 | - rmq 11 | environment: 12 | PORT: 3000 13 | RMQ_HOST: 'rmq' 14 | DEBUG: "true" 15 | 16 | sales: 17 | restart: always 18 | build: ./api/api.sales/. 19 | depends_on: 20 | - rmq 21 | ports: 22 | - "3001:3001" 23 | container_name: sales 24 | environment: 25 | PORT: 3001 26 | RMQ_HOST: 'rmq' 27 | DEBUG: "true" 28 | 29 | rmq: 30 | image: rabbitmq:management 31 | container_name: rmq 32 | hostname: rmq 33 | ports: 34 | - '15672:15672' 35 | - '5672:5672' 36 | 37 | web: 38 | restart: always 39 | build: ./web/. 40 | ports: 41 | - "80:4004" 42 | container_name: web 43 | environment: 44 | PORT: 4004 45 | DEBUG: "true" 46 | 47 | -------------------------------------------------------------------------------- /app_docker_compose/web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:argon 2 | 3 | RUN mkdir -p web 4 | 5 | WORKDIR /web 6 | 7 | COPY package.json /web 8 | 9 | RUN npm install 10 | 11 | COPY . /web 12 | 13 | EXPOSE 4004 14 | 15 | CMD [ "node", "app" ] 16 | -------------------------------------------------------------------------------- /app_docker_compose/web/app.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var app = express(); 3 | var path = require("path"); 4 | app.use("/public", express.static(path.join(__dirname, 'node_modules'))); 5 | app.use('/img', express.static(path.join(__dirname, 'public/images'))); 6 | app.use('/js', express.static(path.join(__dirname, 'public/js'))); 7 | app.use('/css', express.static(path.join(__dirname, 'public/css'))); 8 | app.use('/app', express.static(path.join(__dirname, 'app'))); 9 | app.get('/', function (req, res) { 10 | res.sendFile(path.join(__dirname + '/app/index.html')); 11 | }) 12 | app.listen(4004); 13 | console.log("Rodando na porta 4004"); -------------------------------------------------------------------------------- /app_docker_compose/web/app/app.js: -------------------------------------------------------------------------------- 1 | angular.module('app', [ 2 | 'app.home.controllers', 3 | 'app.sales.controllers', 4 | 'app.services', 5 | 'ngRoute' 6 | ]) 7 | .config(function ($routeProvider) { 8 | $routeProvider 9 | .when("/", { 10 | templateUrl: "app/views/home.html", 11 | controller: "app.home.controller" 12 | }) 13 | .when("/sales", { 14 | templateUrl: "app/views/sales.html", 15 | controller: "app.sales.controller" 16 | }); 17 | }); -------------------------------------------------------------------------------- /app_docker_compose/web/app/controllers/app.home.ctrl.js: -------------------------------------------------------------------------------- 1 | angular.module('app.home.controllers', []) 2 | .controller('app.home.controller', ['$scope', 'api.products', function ($scope, api) { 3 | $scope.product = {}; 4 | $scope.busy = { 5 | list: false, 6 | form: false 7 | }; 8 | $scope.list = function () { 9 | $scope.busy.list = true; 10 | api 11 | .query() 12 | .then(function (response) { 13 | $scope.products = response.data; 14 | $scope.busy.list = false; 15 | }) 16 | } 17 | $scope.submit = function (form) { 18 | if (form.$valid) { 19 | $scope.busy.form = true; 20 | api 21 | .save($scope.product) 22 | .then(function (response) { 23 | $scope.products = response.data; 24 | $scope.busy.form = false; 25 | $scope.product = {}; 26 | $scope.list(); 27 | }) 28 | } 29 | } 30 | 31 | $scope.validateStock = function (product) { 32 | if (product.quantity > product.minimumQuantity) 33 | return 'Saldável'; 34 | else if (product.quantity == product.minimumQuantity) 35 | return 'Ideal'; 36 | else 37 | return 'Baixo' 38 | } 39 | 40 | $scope.list(); 41 | }]); -------------------------------------------------------------------------------- /app_docker_compose/web/app/controllers/app.sales.ctrl.js: -------------------------------------------------------------------------------- 1 | angular.module('app.sales.controllers', []) 2 | .controller('app.sales.controller', ['$scope', 'api.sales', 'api.products', '$location', function ($scope, sales, products, $location) { 3 | $scope.sale = {}; 4 | $scope.product = {}; 5 | $scope.busy = { 6 | list: false, 7 | form: false 8 | }; 9 | $scope.list = function () { 10 | $scope.busy.list = true; 11 | sales 12 | .query() 13 | .then(function (response) { 14 | $scope.sales = response.data; 15 | $scope.busy.list = false; 16 | }) 17 | } 18 | $scope.listProducts = function () { 19 | 20 | products 21 | .query() 22 | .then(function (response) { 23 | $scope.products = response.data; 24 | $scope.calculate(); 25 | $scope.list(); 26 | }) 27 | } 28 | $scope.submit = function (form) { 29 | if (form.$valid) { 30 | $scope.busy.form = true; 31 | $scope.sale.productId = parseInt($scope.sale.productId); 32 | sales 33 | .save($scope.sale) 34 | .then(function (response) { 35 | // $location.path("/"); 36 | $scope.busy.form = false; 37 | $scope.sale = {}; 38 | $scope.list(); 39 | }) 40 | } 41 | } 42 | 43 | $scope.findProdcutById = function (id) { 44 | if (!$scope.products) return undefined; 45 | return $scope.products.filter(function (item) { 46 | return item.id == id; 47 | })[0]; 48 | } 49 | 50 | $scope.getProdcutDescriptionById = function (id) { 51 | var product = $scope.findProdcutById(id); 52 | if (!product) return " - "; 53 | return product.description; 54 | } 55 | 56 | $scope.calculate = function () { 57 | var product = $scope.findProdcutById($scope.sale.productId); 58 | $scope.sale.total = ((!$scope.sale.quantity ? 0 : $scope.sale.quantity) * (!product ? 0 : product.unitPrice)); 59 | } 60 | 61 | $scope.listProducts(); 62 | }]); -------------------------------------------------------------------------------- /app_docker_compose/web/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |Arquitetura de Microsserviços, by Talita Bernardes Pereira.
38 |# | 50 |Código | 51 |Descrição | 52 |Preço unitário | 53 |Quantidade | 54 |Situação do estoque | 55 |Total em estoque | 56 |
---|---|---|---|---|---|---|
{{product.id}} | 61 |{{product.code}} | 62 |{{product.description}} | 63 |{{ product.unitPrice | currency }} | 64 |{{product.quantity}} | 65 |{{ validateStock(product) }} | 66 |{{ (product.quantity * product.unitPrice) | currency }} | 67 |
# | 41 |Produto | 42 |Quantidade | 43 |Total | 44 |Data | 45 |
---|---|---|---|---|
{{sale.id}} | 50 |{{ getProdcutDescriptionById(sale.productId) }} | 51 |{{sale.quantity}} | 52 |{{ sale.total | currency }} | 53 |{{ sale.createdAt | date: 'dd/MM/yyyy HH:mm' }} | 54 |