Contexte 'this' et Circular object = mauvaise architecture ?
Bonjour!
Je dois faire une api json rpc et j'ai deux problèmes:
- un circular object (est-ce vraiment un probleme ?)
- plusieurs classes (des repos) et j'ai des difficulté pour utiliser 'this' dans le repo
Je penses que ces problèmes viennent du fait que j'ai mal architecturer l'appli.. ?
Comment mon serveur fonctionne :
Pour le l'architecture DB je me suis inspiré de pg-promise-demo
- ./server/server.js récupère les méthode exposée dans ./db/index.js
- ./db/index.js 'regroupe' toutes les methods de chaque repo dans une objet avec 'Object.assign()'
- Chaque repo possede une propriete 'methods' qui lui permet d'exposer ses methodes (je dois utiliser bind(this) sinon la méthode ne connait pas les propriétés de la classe)
Mon problème de circular object :
Dans chaque repo j'ai une propriete 'db' qui y est injectée depuis db/index.js je lui ajoute les repos grace a une fonction de pg-promise (lignes 9 à 12) du coup dans cette propriété j'ai :
- toutes les méthodes de pg-promise (logique)
- tous les repos (incluant lui-meme)
Exemple avec DevicesRepository.db :
- pg-promise functions
- les autres repos (RolesRepository, ...)
- DevicesRepository
Mon problème de contexte 'this' :
Dans la fonction 'add', 'this.db' est connu parce que je bind 'this' à la fonction (ligne 15)
Par contre dans le 'this.db.tx', le contexte devient 'this.db', donc par exemple this.Collections n'est plus connu
Je suppose que cela signifie que ces problèmes sont liés et que j'ai mal architecturé l'appli ?
Si oui comment auriez-vous fait ?
Merci d'avance! :D
Un peu de code :
./db/repos/devices.js
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| 'use strict';
const cs = {}; // Reusable ColumnSet objects.
var Collections = {};
class DevicesRepository {
constructor(db, pgp) {
this.db = db;
this.pgp = pgp;
this.Collections = createColumnsets(pgp);
this.methods = {
'devices.insert': this.add.bind(this),
}
}
async add(params) {
console.log(this); //here this = DevicesRepository
var device = params.data.device;
return this.db.tx(function* (transaction) {
console.log(this); // here this = transaction = DevicesRepository.db
//insert a System (returning the inserted ID)
const system = yield transaction.systems.add(params);
device.systemid = system.systemId;
const query = this.pgp.helpers.insert(device, this.Collections.insert);
if(params.return) query += " RETURNING *";
return transaction.one(query);
})
.then(data => {
Log(`INSERTED device ${JSON.stringify(data)}`);
return data;
})
.catch(ex => {
throw new Error(ex);
});
}
}
/* hidde, for brevity */
module.exports = DevicesRepository; |
./db/repos/index.js
Code:
1 2 3 4 5 6
| 'use strict';
module.exports = {
// other repo are required with the same way
Devices: require('./devices'),
}; |
./db/index.js
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 'use strict';
const repos = require('./repos'); // ./repos/index.js
const config = require('./conf');
// pg-promise initialization options:
const initOptions = {
extend(obj, dc) {
obj.roles = new repos.Roles(obj, pgp);
obj.shells = new repos.Shells(obj, pgp);
obj.systems = new repos.Systems(obj, pgp);
obj.devices = new repos.Devices(obj, pgp);
}
};
// Load and initialize pg-promise:
const pgp = require('pg-promise')(initOptions);
// Create the database instance:
const db = pgp(config);
const methods = Object.assign({},
db.roles.methods,
db.shells.methods,
db.systems.methods,
db.devices.methods,
);
module.exports = {
methods
} |
./server/server.js
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| const http = require('http');
const Database = require('../db'); // ./db/index.js
const methods = Database.methods;
const requestHandler = (req, res) => {
// some lines are hidden for brevity
const body = [];
req.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
const bodyStr = Buffer.concat(body).toString();
let request = JSON.parse(bodyStr);
requestProcessor(request).then((response) => {
sendResponse(res, response);
});
});
}
async function requestProcessor(request) {
let response = { /* some props */ };
try {
response.result = await Promise.resolve(methods[request.method](request.params));
} catch (err) {
// hidden for brevity
}
return response;
}
const server = http.createServer(requestHandler);
server.listen(port, (err) => {
if(err) {
return console.log(('something bad happened', err);
}
console.log((`server is listening on ${port}`);
}) |
Utilisation des classes obligatoire
Je re-post pour corriger et donner la "vraie" solution.
Après avoir avancé un peu plus il s'avère que l'utilisation des classes dans les repos est obligatoire sinon nous il y a un problème avec les transactions de pg-promise (problème qui bloque complétement l'API tant quelle n'est pas redemarrée...).
- ./db/repos/index.js require tout les fichiers
- ./db/index.js require le repos (chargement de tous les repos) dans la 'const repos'
2.1 La librairie pg-promise permet "d'étendre" la base avec des repos, cet événement est appelé à l'initialization de l'appli et à chaque fois qu'on effectue une tâche/transaction
2.2 Les taches/transactions utilisent des 'SAVEPOINT', je penses qu'utiliser 'new' permet d'éviter qu'un savepoint se retrouve dans un "mauvais contexte" et bloque la librairie
./db/index.js
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 'use strict';
const promise = require('bluebird');
const repos = require('./repos');
const config = require('./conf');
// pg-promise initialization options:
const initOptions = {
promiseLib: promise,
extend(obj, dc) {
obj.systems = new repos.Systems(obj, pgp);
obj.devices = new repos.Devices(obj, pgp);
}
};
const pgp = require('pg-promise')(initOptions);
const db = pgp(config);
const methods = /* hidden for brevity */
module.exports = {
methods
} |
./db/repos/index.js
Code:
1 2 3 4 5 6
| 'use strict';
module.exports = {
Systems: require('./systems'),
Devices: require('./devices'),
}; |
./db/repos/devices.js
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 'use strict';
class DevicesRepository {
constructor(db, pgp) {
this.Database = db;
this.pgp = pgp;
this.Collections = createColumnsets(pgp);
this.expose = {
'devices.insert': this.insertOne.bind(this),
'devices.getById': this.getOne.bind(this)
}
}
/* hidden for brevity */
insertOne(params) {
// this = DevicesRepository
return this.Database.tx('Insert-New-Device', async t => {
// this = t = pg-promise context
let system = null, disks = null, cpus = null;
const query = t.devices.makeInsertQuery(params.data.device) + " RETURNING *";
let device = await t.one(query);
if(params.data.system) {
params.data.system.deviceid = device.deviceid;
system = await t.systems.insertOne(params);
}
return {device, system, disks, cpus};
})
.catch(ex => {
throw ex;
});
}
}
function createColumnsets(pgp) { /* hidden for brevity */ }
module.exports = DevicesRepository |