precursor Δημοσ. 20 Απριλίου 2019 Δημοσ. 20 Απριλίου 2019 (επεξεργασμένο) Καλησπέρα Θα ήθελα τη βοήθειά σας επειδή όσο και αν ψάχνω στο Google δεν έχω καταφέρει κάτι. Φτιάχνω ένα web app σε Node.js και χρησιμοποιώ το Sequelize ORM για να συνδεθώ στη βάση δεδομένων (PostgreSQL, αν και δεν έχει ιδιαίτερη σημασία αυτό). Το φτιάχνω με αρχιτεκτονική MVC και επειδή είμαι συνηθισμένος στις κλάσεις (από Java και C#) και μπερδεύομαι εύκολα με κώδικα χωρίς κλάσεις, θέλω τα πάντα να είναι αυστηρά μέσα σε κλάσεις! Δηλαδή κάθε module (δηλ. αρχείο .js) να έχει πάνω-πάνω τα requires του μετά να ακολουθεί όλος ο κώδικας μέσα σε class Example {...} και τέλος ένα module.exports = Example; Το πρόβλημα είναι ότι δεν μπορώ σχεδόν ποτέ να βρω ολοκληρωμένα παραδείγματα με κλάσεις - πάντα κάτι θα λείπει ή θα υπάρχει σπαγγέτι κώδικας και μπερδεύομαι και δεν μπορώ να ενώσω τα κομμάτια... Αν κάποιος γνωρίζει λοιπόν, θα ήθελα οδηγίες για να πετύχω το εξής: "use strict"; const Sequelize = require("sequelize"); class Database { constructor() { } ... } module.exports = Database; 1) Πού και πώς πρέπει να βάλω το db = new Sequelize(...); ώστε να έχω μόνο ένα instance και δηλ. μόνο μία σύνδεση με τη βάση δεδομένων και να μην καταλήγω να κάνω new σύνδεση σε κάθε request;;; 2) Και πέρα από την κλάση Database, στις υπόλοιπες κλάσεις πώς καλώ και παίρνω το db (διατηρώντας φυσικά μία σύνδεση);;; Υπόψη ότι όλες οι υπόλοιπες κλάσεις θα έχουν την ίδια μορφή. Σκεφτόμουν αρχικά μήπως έβαζα το new Sequelize μέσα σε μια μέθοδο static get db() αλλά μετά κατάλαβα ότι έτσι θα κάνω νέα σύνδεση κάθε φορά που καλώ την Database.db 3) Τέλος, όσον αφορά τις ρυθμίσεις options που βάζω μέσα στο new Sequelize, με λίγα λόγια τι κάνουν τα min, max και acquire του pool π.χ. αν περιμένω να έχω 1000 users online τι πρέπει να προσέξω για να έχω αρκετά διαθέσιμα connections; Επεξ/σία 20 Απριλίου 2019 από precursor
PC_MAGAS Δημοσ. 20 Απριλίου 2019 Δημοσ. 20 Απριλίου 2019 Για το 1 μπορείς να κάνεις το λεγόμενο Dependency Injection δηλαδή η σύνδεση σου να περνιέται είτε σαν όρισμα στον constructor της κλάσης πχ. έστω η κλάση DatabaseConsumingStuff που διαθέτει μεθόδους που καλούν την Βάση δεδομένων. class DatabaseConsumingStuff { /** * @param {Sequelize} database */ constructor(database){ //Νομίζω η ES6 έχει καλύτερο τρόπο για τέτοιες αναθέσεις this.database=database; } } Πλέον πρέπει κάπου να κάνεις το νέο instance της κλάσης Database και να το περάσεις σαν παράμετρο στο instance στην νέα σου κλάση. Εναλλακτικά μπορείς στις instance μεθόδους της κλάσης αυτής να το βάζεις σαν dependency στο παράδειγμα με την DatabaseConsumingStuff εάν ακολουθήσεις αυτήν την προσέγγιση τότε μπορεί να γίνει: class DatabaseConsumingStuff { constructor(){} /** * @param {Sequelize} database */ callDatabaseToDoSomething(database){ //Do stuff } } Για το 2 απλά κάνεις όπως το 1 έχεις κάπου ένα Instance της database σύνδεσής σου και περνάς την σύνδεση μέσω παραμέτρων. Για το pooling τότε το καλύπτει το ίδιο το Sequalize http://docs.sequelizejs.com/manual/getting-started.html#note--connection-pool--production- μπορείς να το τεστάρεις το poolμέσω ενός stress test αξιοποιόντας εργαλεία όπως το LOIC (Low Orbit Ion Cannon, απλά DoSάρεις την εφαρμογή σου σε ένα ελεγχόμενο περιβάλλον) και μπορείς να δείς τις ενεργές συνδέσεις βάση αυτού . Το ελεγχόμενο περιβάλλον μπορεί να είναι ένα docker image. Ακόμη πέραν του LOIC μπορείς να κάνεις ΚΑΙ δικά σου scripts που να προσομοιώνουν συνθήκες που θα φέρουν την εφαρμογή στα άκρα. Με αυτό και πειράζοντας τις παραμέτρους του pool βάση του παραπάνω link ελέγχεις το μέγεθος του pool. 1
precursor Δημοσ. 20 Απριλίου 2019 Μέλος Δημοσ. 20 Απριλίου 2019 @PC_MAGAS ευχαριστώ για την απάντηση. Αρχικά θέλω να καταλάβω πως να φτιάξω την κλάση Database που έχω βάλει παραπάνω, που κάπου θα κάνει new Sequelize(). Μπορείς να μου δώσεις ένα παράδειγμα; Όσον αφορά το pool το έχω διαβάσει εκείνο το manual αλλά δεν καταλαβαίνω... τι πάει να πει max: 5 δηλ. ότι μέχρι 5 users παράλληλα μπορούν να κάνουν requests;;;
PC_MAGAS Δημοσ. 20 Απριλίου 2019 Δημοσ. 20 Απριλίου 2019 (επεξεργασμένο) 13 λεπτά πριν, precursor είπε @PC_MAGAS ευχαριστώ για την απάντηση. Αρχικά θέλω να καταλάβω πως να φτιάξω την κλάση Database που έχω βάλει παραπάνω, που κάπου θα κάνει new Sequelize(). Μπορείς να μου δώσεις ένα παράδειγμα; Όσον αφορά το pool το έχω διαβάσει εκείνο το manual αλλά δεν καταλαβαίνω... τι πάει να πει max: 5 δηλ. ότι μέχρι 5 users παράλληλα μπορούν να κάνουν requests;;; Την new Sequalize() την κάνεις εκτός κλάσης, κυρίως στο πρώτο αρχείου που καλείς για να σηκώσεις την εφαρμογή. Όσο για το 5 πιστεύω πως εννοεί 5 ενεργά connections στην βάση δεδομένων, τελείως άσχετο από τους χρήστες που συνδέονται στην web εφαρμογή σου. Επεξ/σία 20 Απριλίου 2019 από PC_MAGAS
precursor Δημοσ. 20 Απριλίου 2019 Μέλος Δημοσ. 20 Απριλίου 2019 3 λεπτά πριν, PC_MAGAS είπε Την new Sequalize() την κάνεις εκτός κλάσης, κυρίως στο πρώτο αρχείου που καλείς για να σηκώσεις την εφαρμογή. Μα αυτό είναι ένα από τα βασικά μου προβλήματα - θέλω για να διατηρήσω την οργάνωση τα πάντα να είναι μέσα σε κλάσεις. Είχα κάνει προηγουμένως κάτι τέτοιο: "use strict"; const Sequelize = require("sequelize"); class Database { constructor(DB_DIALECT, DB_USERNAME, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME) { this.sequelize = new Sequelize(DB_DIALECT + "://" + DB_USERNAME + ":" + DB_PASSWORD + "@" + DB_HOST + ":" + DB_PORT + "/" + DB_NAME, { operatorsAliases: false, logging: false, define: { charset: 'utf8' } }); // Successful connection or not? this.sequelize.authenticate().then(() => { console.log("Successfully connected to the database."); }).catch((err) => { console.log("Unable to connect to the database: " + err); }); } ... } module.exports = Database; Όμως κάθε φορά που γίνεται ένα request καταλήγω να γίνεται και από ένα new Database και δηλ. μια νέα σύνδεση. Οπότε προσπαθώ να καταλάβω με ποιο τρόπο και δομή θα μπορώ να καλώ αυτήν την κλάση που θα κάνει το new Sequelize() και ταυτόχρονα θα έχω μόνο μία σύνδεση (χωρίς Singleton pattern)...
PC_MAGAS Δημοσ. 20 Απριλίου 2019 Δημοσ. 20 Απριλίου 2019 2 ώρες πριν, precursor είπε Μα αυτό είναι ένα από τα βασικά μου προβλήματα - θέλω για να διατηρήσω την οργάνωση τα πάντα να είναι μέσα σε κλάσεις. Είχα κάνει προηγουμένως κάτι τέτοιο: "use strict"; const Sequelize = require("sequelize"); class Database { constructor(DB_DIALECT, DB_USERNAME, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME) { this.sequelize = new Sequelize(DB_DIALECT + "://" + DB_USERNAME + ":" + DB_PASSWORD + "@" + DB_HOST + ":" + DB_PORT + "/" + DB_NAME, { operatorsAliases: false, logging: false, define: { charset: 'utf8' } }); // Successful connection or not? this.sequelize.authenticate().then(() => { console.log("Successfully connected to the database."); }).catch((err) => { console.log("Unable to connect to the database: " + err); }); } ... } module.exports = Database; Όμως κάθε φορά που γίνεται ένα request καταλήγω να γίνεται και από ένα new Database και δηλ. μια νέα σύνδεση. Οπότε προσπαθώ να καταλάβω με ποιο τρόπο και δομή θα μπορώ να καλώ αυτήν την κλάση που θα κάνει το new Sequelize() και ταυτόχρονα θα έχω μόνο μία σύνδεση (χωρίς Singleton pattern)... Γιατί σε εμποδίζει κανείς να το κάνεις εκτός; Το bootstraping γίνετε εκτός κλάσεων πχ. σε symfony php framework ορίζεις τα dependencies σε άλλο αρχείο. Εκτός εάν κάνεις Adapter για την κλάση σου και πάλι σε εμποδίζει στο unit testing και πάλι στον Adapter ή στο Ακόμα εάν θες ντε και καλά κλάσεις μπορείς να ελέγχεις εάν το Object σου έχει το this.sequelize είναι defined αξιοποιώντας την function κατά την κλήση οποιασδήποτε μεθόδου: const isDefined=(object) => { if(typeof object === 'undefined' || object===null){ return false; } return true; }; (Την παραπάνω μέθοδο εάν θες όρισε την σαν static μέθοδο σε κλάση) Ακόμη "τα πάντα σε κλάση" προσέγγιση όπως το εννοείς εσύ σε εμποδίζει στο pooling και ένα connection για όλο το app, εκτός εάν κάνεις μια κλάση για Dependency Ιnjection που και πάλι θα πρέπει να υπάρχει κώδικας εκτός κλάσεων, λόγο ότι η Javascript δεν έχει κάποια "εκτελέσιμη" κλάση όπως αυτήν που έχει την public static void main(String []args) όπως η Java. Που και πάλι όταν την κάνεις σε .jar τότε πρέπει να ορίζεις ποια είναι η main Class σου που με το διπλό κλίκ θα πρέπει να τρέξεις. 1
ajaxmonkey4hire Δημοσ. 20 Απριλίου 2019 Δημοσ. 20 Απριλίου 2019 'use strict' const mongodb = require('mongodb'); const MongoClient = mongodb.MongoClient; class Mongo{ constructor(server, dbName){ this.client = null; } connect(cb, opts){ opts = opts||{poolSize: 10, autoReconnect:true, useNewUrlParser: true}; MongoClient.connect(this.server, opts, (e, dbClient)=>{ this.client = e?null:dbClient; cb(e, this.client); }); } use(opts, cb){ } find{opts, cb} } ... } Κάπως έτσι. Το παράδειγμα είναι για Mongo βέβαια αλλά η μεθοδολογία δεν αλλαζει ότι db και να έχεις. 1
precursor Δημοσ. 20 Απριλίου 2019 Μέλος Δημοσ. 20 Απριλίου 2019 3 ώρες πριν, PC_MAGAS είπε Γιατί σε εμποδίζει κανείς να το κάνεις εκτός; Το bootstraping γίνετε εκτός κλάσεων πχ. σε symfony php framework ορίζεις τα dependencies σε άλλο αρχείο. Εκτός εάν κάνεις Adapter για την κλάση σου και πάλι σε εμποδίζει στο unit testing και πάλι στον Adapter ή στο Ακόμα εάν θες ντε και καλά κλάσεις μπορείς να ελέγχεις εάν το Object σου έχει το this.sequelize είναι defined αξιοποιώντας την function κατά την κλήση οποιασδήποτε μεθόδου: const isDefined=(object) => { if(typeof object === 'undefined' || object===null){ return false; } return true; }; (Την παραπάνω μέθοδο εάν θες όρισε την σαν static μέθοδο σε κλάση) Ακόμη "τα πάντα σε κλάση" προσέγγιση όπως το εννοείς εσύ σε εμποδίζει στο pooling και ένα connection για όλο το app, εκτός εάν κάνεις μια κλάση για Dependency Ιnjection που και πάλι θα πρέπει να υπάρχει κώδικας εκτός κλάσεων, λόγο ότι η Javascript δεν έχει κάποια "εκτελέσιμη" κλάση όπως αυτήν που έχει την public static void main(String []args) όπως η Java. Που και πάλι όταν την κάνεις σε .jar τότε πρέπει να ορίζεις ποια είναι η main Class σου που με το διπλό κλίκ θα πρέπει να τρέξεις. Αυτό για την main που λες το καταλαβαίνω. Έχω λίγο σπαγγέτι κώδικα στο "πρώτο" αρχείο της εφαρμογής, εκείνο που εκτελείται όταν ξεκινάει για πρώτη φορά (επίσης έχω και αρχείο config με τα στοιχεία της Βάσης Δεδομένων που επίσης δεν είναι κλάση), δηλαδή: server.js /** * This is the entry-point file. * It is only called once at startup of the application. */ // Load the Bootstrap! var app = require("./system/core/bootstrap"); app.start(); Από εκείνο το σημείο και μετά όμως υπάρχουν μόνο κλάσεις. Στο "δεύτερο" αρχείο, δηλαδή στο bootstrap, υπάρχει γραμμένη η λογική του ExpressJS server και μέχρι να κάνει το listen() όλα εκεί είναι ρυθμίσεις που εκτελούνται μία μόνο φορά (δηλ. σε κάθε request εκτελείται μόνο ο κώδικας που είναι τοποθετημένος μέσα σε routes). Ένας λόγος - πέρα από την οργάνωση - που θέλω τα πάντα με κλάσεις είναι επειδή ήδη την έχω κάνει την εφαρμογή και δουλεύει και τώρα κάνω refactoring. Έτσι όπως την έχω τώρα, σε κάθε request τρέχει και ένα new Database και συνεπώς ένα new Sequelize, και αυτό είναι πολύ κακό, οπότε αυτό είναι που προσπαθώ να αλλάξω - διατηρώντας τις κλάσεις. Όλα αυτά που μου λες μου φαίνονται ωραία αλλά έχω δυσκολία στο να τα καταλάβω για αρχή (έλλειψη γνώσεων) - πρώτα χρειάζομαι λίγο χεράκι-χεράκι με την κλάση Database όπου εκεί θα βάλω το new Sequelize, και ιδανικά θα την καλώ στο bootsrap για να γίνει το connection μία φορά και μετά κάπως θα παίρνω ένα db object σε άλλες κλάσεις (χωρίς να ξεκινάω νέες συνδέσεις) ώστε να κάνω CRUD... 1 ώρα πριν, ajaxmonkey4hire είπε 'use strict' const mongodb = require('mongodb'); const MongoClient = mongodb.MongoClient; class Mongo{ constructor(server, dbName){ this.client = null; } connect(cb, opts){ opts = opts||{poolSize: 10, autoReconnect:true, useNewUrlParser: true}; MongoClient.connect(this.server, opts, (e, dbClient)=>{ this.client = e?null:dbClient; cb(e, this.client); }); } use(opts, cb){ } find{opts, cb} } ... } Κάπως έτσι. Το παράδειγμα είναι για Mongo βέβαια αλλά η μεθοδολογία δεν αλλαζει ότι db και να έχεις. Η class Mongo είναι η αντίστοιχη class Database που χρειάζομαι, σωστά; (χαζή ερώτηση αλλά θα ήθελα την επιβεβαίωση) Τα use, find κ.λπ. έχουν να κάνουν με CRUD υποθέτω, οπότε για την ώρα μάλλον δεν με απασχολούν. Και τα στοιχεία της Βάσης βλέπω τα παίρνεις με τον constructor. Οπότε, αυτό που πρέπει να κάνω είναι μια μέθοδο ας την πούμε connect και να κάνω εκεί μέσα το new Sequelize(). Και υποθέτω ότι μετά θα κάνω require την κλάση Database κάπου στην αρχή της εφαρμογής (bootstrap) και μετά db= new Database() και db.connect() . Σωστά μέχρι εδώ; Από εκεί και πέρα πώς θα έχω πρόσβαση στο db object στην υπόλοιπη εφαρμογή; Θα ήθελα να αποφύγω να το περνάω σε constructors (αυτήν τη στιγμή όταν χτυπάει κάποιο route και καλώ έναν controller - δηλ. μια κλάση - του περνάω μόνο τα γνωστά req, res και next του ExpressJS). Υπάρχει κάποιος τρόπος να καλώ μετά την κλάση Database από ένα δεύτερο μέρος (π.χ. από μια base model class) και να παίρνω εκείνον τον db handler χωρίς να φτιάχνω νέα σύνδεση; Υπάρχει μήπως νόημα να κάνω την connect και όλες τις μεθόδους static; Δηλαδή static connect() 😳
PC_MAGAS Δημοσ. 21 Απριλίου 2019 Δημοσ. 21 Απριλίου 2019 (επεξεργασμένο) Εκεί που έχεις το πρώτο αρχείο της εφαρμογής ή σε ένα αρχείο Bootstraping κάνει setup και το connection. Μετά στην κλάση mongoDB κάνε το CRUD Logic με παράμετρο το MongοDB connection. Άρα έχε ένα αρχείο για να κάνεις Bootstrap το connection πχ. στο ./system/core/bootstrap όμως ναι το bootstraping είναι spaghetti. //Other System Bootstrapping const mongodb = require('mongodb'); const Mongo = require('mongo'); const settings=require('./system/settings'); const mongodbConnection=null; let mongoLogic=null; const MongoClient = mongodb.MongoClient; MongoClient.connect(settings.server, settings.mongodbopts, (e, dbClient)=>{ const mongoLogic=Mongo(MongoClient; mongoLogic = new Mongo(dbClient); }); Βάση αυτού που λές είναι όπως την περιγράφεις με ένα μικρό twist ότι ελέγχω εάν έχω connection: 'use strict' const mongodb = require('mongodb'); const MongoClient = mongodb.MongoClient; class Mongo{ constructor(server, dbName){ this.client = null; } connect(cb, opts){ opts = opts||{poolSize: 10, autoReconnect:true, useNewUrlParser: true}; MongoClient.connect(this.server, opts, (e, dbClient)=>{ this.client = e?null:dbClient; cb(e, this.client); }); } use(opts, cb){ if(!this.client){ return cb(err); } // Do what the f*** you want. } find(opts, cb){ if(!this.client){ return db(err); } // Same here } } Όμως στην προσέγγιση αυτή έχεις τo πρόβλημα του ποιο tight coupling της λογικής της εφαμρογής με την database. Για να το χαλαρώσεις σου προτείνω: 'use strict' const mongodb = require('mongodb'); const MongoClient = mongodb.MongoClient; class Mongo{ constructor(server, dbName){ this.client = null; } connect(cb, opts){ opts = opts||{poolSize: 10, autoReconnect:true, useNewUrlParser: true}; MongoClient.connect(this.server, opts, (e, dbClient)=>{ this.client = e?null:dbClient; cb(e, this.client); }); } /** * @param cb {Function} Callback providing database cnsuming logic. */ use(cb){ if(!this.client){ return cb(err); } cb(null,this.client); } } module.exports.Mongo=Mongo. Το Bootstrapping θα γίνετε: const options=require('./system/options'); const Mongo=require('./Mongo'); const DbConsumingLogic=require('DbConsumingLogic'); const mongoClient = new Mongo(options.mongodd.server,options.mongodb.db); const databaseConsumingLogic= new DbConsumingLogic(mongoClient); Έτσι εντός του DbConsumingLogic καλείς τις κατάλληλες μεθόδους. Τέλος διάβασε το Dependency Injection Pattern https://en.wikipedia.org/wiki/Dependency_injection Επεξ/σία 21 Απριλίου 2019 από PC_MAGAS 1
precursor Δημοσ. 21 Απριλίου 2019 Μέλος Δημοσ. 21 Απριλίου 2019 (επεξεργασμένο) @PC_MAGAS επειδή με μπερδεύουν όλα αυτά περί mongoDB ας το προσεγγίσουμε με τον δικό μου κώδικα (για να καταλάβω καλύτερα τι θα αλλάξω/προσθέσω). Συγνώμη για το πρήξιμο 😳 1) Στο πρώτο βήμα, όταν ξεκινήσει η εφαρμογή θα κληθεί το server.js (το οποίο δεν μας ενδιαφέρει). /** * This is the entry-point file. * It is only called once at startup of the application. */ var app = require("./system/core/bootstrap"); app.start(); 2) Έπειτα ακολουθεί το bootstrap στο περίπου, και όπως όλα τα modules (με εξαίρεση το από πάνω) έχει αυστηρά τη δομή: πρώτα όλα τα require, μετά η class {} και τέλος module.exports την κλάση. "use strict"; const express = require("express"), app = express(); // Invoke an instance of an Express application. // ... const Database = require("../core/database"); class Bootstrap { static start() { Bootstrap.setup(); Bootstrap.dispatch(); Bootstrap.listen(); } static setup() { //... const db = new Database("..."); db.connect("..."); } /** * This is the dispatcher. * Figures out the right controller to render the view. */ static dispatch() { app.route("...").all((req, res, next) => { //... Bootstrap.loadController(req, res, next); }); // other routes... } static listen() { app.listen(app.get("port"), () => console.log(`App started on port ${app.get("port")}`)); } //... static loadController(req, res, next) { //... let ChosenController = new ChosenControllerClass(req, res, next); //... } } module.exports = Bootstrap; Οπότε για να καταλάβεις την πορεία των πραγμάτων, το προηγούμενο αρχείο τελείωσε καλώντας την start(), οπότε στον παραπάνω κώδικα συνεχίζουμε μέσα στην static start() η οποία όπως φαίνεται θα καλέσει στη σειρά τρεις μεθόδους. Η πρώτη, η static setup() θα κάνει μεταξύ άλλων και τη σύνδεση στη βάση δεδομένων χρησιμοποιώντας την κλάση Database. Η δεύτερη, η static dispatch(), θα ρυθμίσει όλα τα routes ώστε μετά σε κάθε http request να "χτυπάει" κάποιο από αυτά και να εκτελείται ο κατάλληλος κώδικας που θα καλεί τον κατάλληλο controller. Η τρίτη, η static listen(), θέτει σε λειτουργεία τον Express server ώστε να ακούει για requests. Οπότε δύο σημεία είναι σημαντικά για να κρατήσουμε. Πρώτον, η σύνδεση με τη βάση δεδομένων γίνεται μία μόνο φορά στην setup(), και αυτό γιατί το setup γίνεται μόνο μία φορά (τα μόνα τμήματα του παραπάνω κώδικα που ξανατρέχουν είναι μόνο μέσα στα routes δηλ. στα app.route(){ } ). Και δεύτερον, η εφαρμογή συνεχίζει καλώντας κάποια κλάση controller περνώντας της όπως φαίνεται τα res, req και next που είναι γνωστά objects του Express Server. Θα ήθελα να αποφύγω να περνάω και το db object/handler στους controllers που καλώ... ώστε αν μπορούν εκείνοι να καλούν την κλάση Database και να παίρνουν από εκεί το handler. Ίσως να το έκανα κάπως globally διαθέσιμο στην εφαρμογή;;; Ίσως χρειάζεται κάποιον getter η κλάση Database;;; (το Dependency Injection σαν pattern το ξέρω και σε ένα βαθμό το κατανοώ, αλλά μου είναι κάπως advanced για την ώρα, το έχω κάνει σε PHP επειδή είχα βρει καλά, ξεκάθαρα και εύκολα tutorials αλλά σε JavaScript δεν βρίσκω σχεδόν τίποτα καλό και ολοκληρωμένο με κλάσεις, άσε που μου φαίνονται όλα μπερδεμένα γιατί ο καθένας χρησιμοποιεί ένα σωρό διαφορετικά libraries, ORMs κ.λπ. και γίνεται χαμός από ακαταλαβίστικο κώδικα) 3) Για να συμπληρωθεί η όλη εικόνα, η κλάση Database όπως την περιγράψαμε: "use strict"; const Sequelize = require("sequelize"); class Database { constructor(...) { } connect(DB_DIALECT, DB_USERNAME, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME) { this.sequelize = new Sequelize(DB_DIALECT + "://" + DB_USERNAME + ":" + DB_PASSWORD + "@" + DB_HOST + ":" + DB_PORT + "/" + DB_NAME, { operatorsAliases: false, logging: false, define: { charset: 'utf8' } }); // Successful connection or not? this.sequelize.authenticate().then(() => { console.log("Successfully connected to the database."); }).catch((err) => { console.log("Unable to connect to the database: " + err); }); } // CRUD } module.exports = Database; Οπότε που πρέπει να "κουμπώσω" και τι για να είναι προσβάσιμο από όλα τα controllers (και συνεπώς και από models) το Database object που έκανε το connection 🤔 Επεξ/σία 21 Απριλίου 2019 από precursor
skiabox Δημοσ. 21 Απριλίου 2019 Δημοσ. 21 Απριλίου 2019 Πως και δεν χρησιμοποιείς typescript μιας και έρχεσαι από c# / java ?
precursor Δημοσ. 21 Απριλίου 2019 Μέλος Δημοσ. 21 Απριλίου 2019 (επεξεργασμένο) 19 λεπτά πριν, skiabox είπε Πως και δεν χρησιμοποιείς typescript μιας και έρχεσαι από c# / java ? Πέρα από την απουσία πεδίων κλάσεων, για τα οποία υπάρχει workaround με getters και setters και μεθόδους γενικότερα, δεν βρήκα κάποια άλλη έλλειψη στην ES6 που να με ενόχλησε αρκετά ώστε να πάω σε typescript. Προτιμώ να γράψω κώδικα που θα μείνει στο πρότζεκτ που φτιάχνω (για χόμπυ) από το να κάνω πισωγύρισμα βγάζοντας την typescript σε 5-6 χρόνια όταν και αν κάνει catch up η JavaScript. Την επικοινωνία μεταξύ των modules, το class autoloading κ.λπ. το έχω ως πρόβλημα και με PHP, όχι μόνο με NodeJS. Δεν είναι σαν την Java που το JVM τα βρίσκει όλα για εμάς... Επεξ/σία 21 Απριλίου 2019 από precursor
skiabox Δημοσ. 21 Απριλίου 2019 Δημοσ. 21 Απριλίου 2019 Πιστεύω πάντως ότι θα σου αρέσει η typescript. Επίσης ανεβαίνει συνέχεια σε popularity και σε δυνατότητες. Δες και αυτό :http://www.collectionsjs.com/ 1
precursor Δημοσ. 21 Απριλίου 2019 Μέλος Δημοσ. 21 Απριλίου 2019 4 λεπτά πριν, skiabox είπε Πιστεύω πάντως ότι θα σου αρέσει η typescript. Επίσης ανεβαίνει συνέχεια σε popularity και σε δυνατότητες. Δες και αυτό :http://www.collectionsjs.com/ Μου αρέσει ήδη. Θα ήθελα να ήταν η standard scripting γλώσσα, built-in στα engines των browser. Δηλαδή μου αρέσει (και την ψιλοχρησιμοποιώ αλλού), αλλά μόνο αν θα μπορούσε να βρίσκεται εκείνη στη θέση της JavaScript. Από τη στιγμή όμως που το φτιάχνω για χόμπυ (και δεν θέλω απλά να βγάλω μια δουλειά) προτιμώ να μαθαίνω την JavaScript που ξέρω ότι θα μείνει εκεί που είναι και θα βελτιώνεται. Σε TypeScript, CoffeeScript κ.λπ. δεν θέλω να πιστεύω και να επενδύσω χρόνο - παρόλο που μου αρέσει η typescript - μιας και υπάρχουν για να συμπληρώσουν κενά της JavaScript και σε λίγα χρόνια μπορεί να είναι obsolete. Άσε που γίνεται πια κακός χαμός από εργαλεία στην JavaScript (frameworks, libraries, κ.λπ.) που έρχονται και φεύγουν από τη μόδα και που έχουν το δικό τους καινούργιο βρωμοσυντακτικό. Πόση πληροφορία να αντέξει ένας εγκέφαλος; Δεν με ενοχλεί όταν κάτι από αυτά λειτουργεί συμπληρωματικά και σου κάνει τη δουλειά που θες ώστε να μην εφευρίσκεις ξανά τον τροχό, αλλά ξενερώνω όταν το συντακτικό τους μολύνει τις raw γλώσσες που χρησιμοποιούμε στο Web (HTML, JavaScript, CSS κ.λπ.). Για παράδειγμα, την βιβλιοθήκη jQuery την θεωρώ εντάξει μιας και απλά ορίζει ένα δικό της function με το δολάριο και μας δίνει έτσι πολλές έτοιμες λειτουργίες - ενώ παράλληλα συνεχίζω να νιώθω ότι γράφω JavaScript και όχι με κανά καινούργιο εξωγήινο συντακτικό (το ότι έχει τα δικά της APIs που πρέπει να μαθευτούν, αυτό εντάξει εξυπακούεται). Άλλο παράδειγμα, ο Express Server, είναι επίσης μια χαρά, γιατί απλά δημιουργείς ένα instance και χρησιμοποιείς τις μεθόδους του χωρίς να σου γα**ει την σύνταξη της Node.js. Άλλο παράδειγμα, τα template expressions π.χ. του EJS ή του Handlebars, και αυτά είναι μια χαρά γιατί βάζεις τα δικά τους tags μέσα στην HTML (σαν να έβαζες tags της PHP) και ξέρεις ότι θα μετατραπούν σε ευανάγνωστη HTML. Από την άλλη, τα ReactJS και AngularJS με το δικό τους κωλοσυντακτικό που μολύνει την HTML δεν τα μπορώ καθόλου.
ajaxmonkey4hire Δημοσ. 21 Απριλίου 2019 Δημοσ. 21 Απριλίου 2019 4 ώρες πριν, precursor είπε Αυτό για την main που λες το καταλαβαίνω. Έχω λίγο σπαγγέτι κώδικα στο "πρώτο" αρχείο της εφαρμογής, εκείνο που εκτελείται όταν ξεκινάει για πρώτη φορά (επίσης έχω και αρχείο config με τα στοιχεία της Βάσης Δεδομένων που επίσης δεν είναι κλάση), δηλαδή: server.js /** * This is the entry-point file. * It is only called once at startup of the application. */ // Load the Bootstrap! var app = require("./system/core/bootstrap"); app.start(); Από εκείνο το σημείο και μετά όμως υπάρχουν μόνο κλάσεις. Στο "δεύτερο" αρχείο, δηλαδή στο bootstrap, υπάρχει γραμμένη η λογική του ExpressJS server και μέχρι να κάνει το listen() όλα εκεί είναι ρυθμίσεις που εκτελούνται μία μόνο φορά (δηλ. σε κάθε request εκτελείται μόνο ο κώδικας που είναι τοποθετημένος μέσα σε routes). Ένας λόγος - πέρα από την οργάνωση - που θέλω τα πάντα με κλάσεις είναι επειδή ήδη την έχω κάνει την εφαρμογή και δουλεύει και τώρα κάνω refactoring. Έτσι όπως την έχω τώρα, σε κάθε request τρέχει και ένα new Database και συνεπώς ένα new Sequelize, και αυτό είναι πολύ κακό, οπότε αυτό είναι που προσπαθώ να αλλάξω - διατηρώντας τις κλάσεις. Όλα αυτά που μου λες μου φαίνονται ωραία αλλά έχω δυσκολία στο να τα καταλάβω για αρχή (έλλειψη γνώσεων) - πρώτα χρειάζομαι λίγο χεράκι-χεράκι με την κλάση Database όπου εκεί θα βάλω το new Sequelize, και ιδανικά θα την καλώ στο bootsrap για να γίνει το connection μία φορά και μετά κάπως θα παίρνω ένα db object σε άλλες κλάσεις (χωρίς να ξεκινάω νέες συνδέσεις) ώστε να κάνω CRUD... Η class Mongo είναι η αντίστοιχη class Database που χρειάζομαι, σωστά; (χαζή ερώτηση αλλά θα ήθελα την επιβεβαίωση) Τα use, find κ.λπ. έχουν να κάνουν με CRUD υποθέτω, οπότε για την ώρα μάλλον δεν με απασχολούν. Και τα στοιχεία της Βάσης βλέπω τα παίρνεις με τον constructor. Οπότε, αυτό που πρέπει να κάνω είναι μια μέθοδο ας την πούμε connect και να κάνω εκεί μέσα το new Sequelize(). Και υποθέτω ότι μετά θα κάνω require την κλάση Database κάπου στην αρχή της εφαρμογής (bootstrap) και μετά db= new Database() και db.connect() . Σωστά μέχρι εδώ; Από εκεί και πέρα πώς θα έχω πρόσβαση στο db object στην υπόλοιπη εφαρμογή; Θα ήθελα να αποφύγω να το περνάω σε constructors (αυτήν τη στιγμή όταν χτυπάει κάποιο route και καλώ έναν controller - δηλ. μια κλάση - του περνάω μόνο τα γνωστά req, res και next του ExpressJS). Υπάρχει κάποιος τρόπος να καλώ μετά την κλάση Database από ένα δεύτερο μέρος (π.χ. από μια base model class) και να παίρνω εκείνον τον db handler χωρίς να φτιάχνω νέα σύνδεση; Υπάρχει μήπως νόημα να κάνω την connect και όλες τις μεθόδους static; Δηλαδή static connect() 😳 Ναι. Αν θέλεις να το χρησιμοποιείς και από άλλα modules μπορείς να κάνεις το εξής: 'use strict' const mongodb = require('mongodb'); const MongoClient = mongodb.MongoClient; var mongo; class Mongo{ constructor(server){ this.server = server; this.client = null; } connect(cb, opts){ opts = opts||{poolSize: 10, autoReconnect:true, useNewUrlParser: true}; MongoClient.connect(this.server, opts, (e, dbClient)=>{ this.client = e?null:dbClient; cb(e, this.client); }); } use(cb, dbName){ if (cb){ if (this.client&&this.client.isConnected()){ cb(this.client.db(dbName)); }else{ this.connect((e, client)=>{ cb(e?null:client.db(dbName)); }); } } } } module.exports.getMongoDb = function(server){ mongo = mongo||new Mongo(server); return mongo; } κάνεις ένα require εκεί που θέλεις να το χρησιμοποιήσεις και καλείς το getMongoDb('το σερβερ σου') αν υπάρχει το mongo σου το επιστρέφει ειδάλλως καλεί το constructor, το φτιάχνει και μετά σου το επιστρέφει. 1
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα