Ich versuche zu verstehen, wie Unternehmensanwendungen mit Node / Express / Mongo strukturiert werden (tatsächlich mit MEAN-Stack).
Nachdem ich 2 Bücher gelesen und gegoogelt hatte (einschließlich ähnlicher StackOverflow-Fragen), konnte ich kein gutes Beispiel für die Strukturierung großer Anwendungen mit Express finden. Alle Quellen, die ich gelesen habe, schlagen vor, die Anwendung nach folgenden Entitäten aufzuteilen:
- Routen
- Steuerungen
- Modelle
Aber das Hauptproblem ich mit dieser Struktur sehen ist , dass Controller wie Gott Objekte sind, sie kennt req
, res
Objekte, verantwortlich für die Validierung und hat Business - Logik enthält in.
Auf der anderen Seite scheinen mir Routen ein Über-Engineering zu sein, da sie lediglich Endpunkte (Pfade) Controller-Methoden zuordnen.
Ich habe Scala / Java-Hintergrund, daher habe ich die Gewohnheit, die gesamte Logik in drei Ebenen zu trennen - Controller / Service / Dao.
Für mich sind folgende Aussagen ideal:
Die Controller sind nur für die Interaktion mit dem WEB-Teil verantwortlich, dh für das Marshalling / Unmarshalling, eine einfache Validierung (erforderlich, min, max, E-Mail-Regex usw.).
Die Service-Schicht (die ich in NodeJS / Express-Apps tatsächlich vermisst habe) ist nur für die Geschäftslogik verantwortlich, einige Geschäftsvalidierungen. Die Serviceschicht weiß nichts über den WEB-Teil (dh sie können von einem anderen Anwendungsort aus aufgerufen werden, nicht nur aus dem Webkontext).
In Bezug auf die DAO-Schicht ist mir alles klar. Mungomodelle sind eigentlich DAO, daher ist es mir hier am klarsten.
Ich denke, Beispiele, die ich gesehen habe, sind sehr einfach und zeigen nur Konzepte von Node / Express, aber ich möchte ein Beispiel aus der Praxis betrachten, bei dem ein Großteil der Geschäftslogik / -validierung eine Rolle spielt.
BEARBEITEN:
Eine andere Sache, die mir nicht klar ist, ist das Fehlen von DTO-Objekten. Betrachten Sie dieses Beispiel:
const mongoose = require('mongoose');
const Article = mongoose.model('Article');
exports.create = function(req, res) {
// Create a new article object
const article = new Article(req.body);
// saving article and other code
}
Dort wird das JSON-Objekt von req.body
als Parameter zum Erstellen des Mongo-Dokuments übergeben. Es riecht schlecht für mich. Ich würde gerne mit konkreten Klassen arbeiten, nicht mit rohem JSON
Vielen Dank.
quelle
Antworten:
Controller sind Gottobjekte, bis Sie nicht möchten, dass sie so sind ...
- Sie sagen nicht zurfyx (╯ ° □ °)))
Nur an der Lösung interessiert? Springe zum neuesten Abschnitt "Ergebnis" .
┬──┬◡ ノ (° - ° ノ)
Bevor ich mit der Antwort beginne, möchte ich mich dafür entschuldigen, dass diese Antwort viel länger als die übliche SO-Länge ist. Controller alleine machen nichts, es geht nur um das gesamte MVC-Muster. Daher hielt ich es für wichtig, alle wichtigen Details zum Router <-> Controller <-> Service <-> Modell durchzugehen, um Ihnen zu zeigen, wie Sie geeignete isolierte Controller mit minimalen Verantwortlichkeiten erreichen.
Hypothetischer Fall
Beginnen wir mit einem kleinen hypothetischen Fall:
Beginnen wir mit Express. Das ist einfach peasy, nicht wahr?
route.js
import * as userControllers from 'controllers/users'; router.get('/users/:username', userControllers.getUser);
controller / user.js
import User from '../models/User'; function getUser(req, res, next) { const username = req.params.username; if (username === '') { return res.status(500).json({ error: 'Username can\'t be blank' }); } try { const user = await User.find({ username }).exec(); return res.status(200).json(user); } catch (error) { return res.status(500).json(error); } }
Jetzt machen wir den Socket.io-Teil:
Da dies keine socket.io- Frage ist, überspringe ich das Boilerplate.
import User from '../models/User'; socket.on('RequestUser', (data, ack) => { const username = data.username; if (username === '') { ack ({ error: 'Username can\'t be blank' }); } try { const user = User.find({ username }).exec(); return ack(user); } catch (error) { return ack(error); } });
Ähm, hier riecht etwas ...
if (username === '')
. Wir mussten den Controller-Validator zweimal schreiben. Was wäre, wenn esn
Controller-Validatoren gäbe ? Müssten wir zwei (oder mehr) Kopien von jeder auf dem neuesten Stand halten?User.find({ username })
wird zweimal wiederholt. Das könnte möglicherweise eine Dienstleistung sein.Wir haben gerade zwei Controller geschrieben, die den genauen Definitionen von Express und Socket.io zugeordnet sind. Sie werden höchstwahrscheinlich während ihres Lebens niemals kaputt gehen, da sowohl Express als auch Socket.io in der Regel abwärtskompatibel sind. ABER sie sind nicht wiederverwendbar. Express für Hapi wechseln ? Sie müssen alle Ihre Controller wiederholen.
Ein weiterer schlechter Geruch, der vielleicht nicht so offensichtlich ist ...
Die Controller-Antwort ist handgefertigt.
.json({ error: whatever })
APIs in RL ändern sich ständig. In Zukunft möchten Sie vielleicht, dass Ihre Antwort
{ err: whatever }
oder etwas Komplexeres (und Nützlicheres) wie:{ error: whatever, status: 500 }
Fangen wir an (eine mögliche Lösung)
Ich kann es nicht die Lösung nennen, weil es unendlich viele Lösungen gibt. Es liegt an Ihrer Kreativität und Ihren Bedürfnissen. Das Folgende ist eine anständige Lösung; Ich verwende es in einem relativ großen Projekt und es scheint gut zu funktionieren, und es behebt alles, worauf ich zuvor hingewiesen habe.
Ich gehe zu Modell -> Service -> Controller -> Router, um es bis zum Ende interessant zu halten.
Modell
Ich werde nicht auf Details des Modells eingehen, da dies nicht Gegenstand der Frage ist.
Sie sollten eine ähnliche Mungo-Modellstruktur wie die folgende haben:
models / User / validate.js
export function validateUsername(username) { return true; }
Weitere Informationen zur geeigneten Struktur für Mungo 4.x-Validatoren finden Sie hier .
models / User / index.js
import { validateUsername } from './validate'; const userSchema = new Schema({ username: { type: String, unique: true, validate: [{ validator: validateUsername, msg: 'Invalid username' }], }, }, { timestamps: true }); const User = mongoose.model('User', userSchema); export default User;
Nur ein einfaches Benutzerschema mit einem Benutzernamenfeld und
created
updated
mungogesteuerten Feldern.Der Grund, warum ich das
validate
Feld hier eingefügt habe, ist, dass Sie feststellen, dass Sie die meisten Modellvalidierungen hier und nicht im Controller durchführen sollten.Das Mungo-Schema ist der letzte Schritt vor dem Erreichen der Datenbank. Wenn jemand MongoDB nicht direkt abfragt, können Sie immer sicher sein, dass jeder Ihre Modellvalidierungen durchläuft, was Ihnen mehr Sicherheit bietet, als sie auf Ihrem Controller zu platzieren. Nicht zu sagen, dass Unit-Test-Validatoren wie im vorherigen Beispiel trivial sind.
Lesen Sie hier und hier mehr darüber .
Bedienung
Der Dienst fungiert als Prozessor. Bei akzeptablen Parametern werden diese verarbeitet und ein Wert zurückgegeben.
In den meisten Fällen (einschließlich dieses) werden Mongoose-Modelle verwendet und ein Versprechen zurückgegeben (oder ein Rückruf; aber ich würde ES6 definitiv mit Versprechen verwenden, wenn Sie dies nicht bereits tun).
services / user.js
function getUser(username) { return User.find({ username}).exec(); // Just as a mongoose reminder, .exec() on find // returns a Promise instead of the standard callback. }
An diesem Punkt wundern Sie sich vielleicht, kein
catch
Block? Nein, weil wir später einen coolen Trick machen werden und für diesen Fall keinen benutzerdefinierten brauchen.In anderen Fällen reicht ein einfacher Synchronisierungsdienst aus. Stellen Sie sicher, dass Ihr Synchronisierungsdienst niemals E / A enthält, da Sie sonst den gesamten Node.js-Thread blockieren .
services / user.js
function isChucknorris(username) { return ['Chuck Norris', 'Jon Skeet'].indexOf(username) !== -1; }
Regler
Wir möchten doppelte Controller vermeiden, daher haben wir nur einen Controller für jede Aktion.
controller / user.js
export function getUser(username) { }
Wie sieht diese Signatur jetzt aus? Schön, oder? Da wir nur am Parameter username interessiert sind, müssen wir keine nutzlosen Dinge wie nehmen
req, res, next
.Fügen wir die fehlenden Validatoren und den fehlenden Service hinzu:
controller / user.js
import { getUser as getUserService } from '../services/user.js' function getUser(username) { if (username === '') { throw new Error('Username can\'t be blank'); } return getUserService(username); }
Sieht immer noch ordentlich aus, aber ... was ist mit dem
throw new Error
, bringt das meine Anwendung nicht zum Absturz? - Shh, warte. Wir sind noch nicht fertig.An diesem Punkt würde unsere Controller-Dokumentation also ungefähr so aussehen:
/** * Get a user by username. * @param username a string value that represents user's username. * @returns A Promise, an exception or a value. */
Was ist der "Wert" in der angegeben
@returns
? Denken Sie daran, dass wir vorhin gesagt haben, dass unsere Dienste sowohl synchron als auch asynchron sein können (mitPromise
)?getUserService
ist in diesem Fall asynchron, derisChucknorris
Dienst jedoch nicht. Daher wird einfach ein Wert anstelle eines Versprechens zurückgegeben.Hoffentlich wird jeder die Dokumente lesen. Weil sie einige Controller anders behandeln müssen als andere, und einige von ihnen benötigen einen
try-catch
Block.Da wir Entwicklern (einschließlich mir) nicht vertrauen können, dass sie die Dokumente lesen, bevor sie es zuerst versuchen, müssen wir an dieser Stelle eine Entscheidung treffen:
Promise
Rückgabe zu erzwingen⬑ Dies löst die inkonsistente Controller-Rückgabe (nicht die Tatsache, dass wir unseren Try-Catch-Block weglassen können).
IMO, ich bevorzuge die erste Option. Weil Controller meistens die meisten Versprechen verketten.
return findUserByUsername .then((user) => getChat(user)) .then((chat) => doSomethingElse(chat))
Wenn wir ES6 Promise verwenden, können wir alternativ eine nette Eigenschaft nutzen,
Promise
um dies zu tun: Wir könnenPromise
Nicht-Versprechen während ihrer Lebensdauer verarbeiten und trotzdem immer wieder Folgendes zurückgebenPromise
:return promise .then(() => nonPromise) .then(() => // I can keep on with a Promise.
Wenn der einzige Dienst, den wir anrufen, nicht verwendet wird
Promise
, können wir selbst einen erstellen.return Promise.resolve() // Initialize Promise for the first time. .then(() => isChucknorris('someone'));
Wenn wir zu unserem Beispiel zurückkehren, würde dies Folgendes ergeben:
... return Promise.resolve() .then(() => getUserService(username));
Wir brauchen
Promise.resolve()
in diesem Fall nicht wirklich, dagetUserService
bereits ein Versprechen zurückgegeben wird, aber wir wollen konsistent sein.Wenn Sie sich über den
catch
Block wundern : Wir möchten ihn nicht in unserem Controller verwenden, es sei denn, wir möchten eine benutzerdefinierte Behandlung durchführen. Auf diese Weise können wir die beiden bereits integrierten Kommunikationskanäle (Ausnahme für Fehler und Rückgabe für Erfolgsmeldungen) nutzen, um unsere Nachrichten über einzelne Kanäle zu übermitteln.Anstelle von ES6 Promise
.then
können wir das neuere ES2017async / await
( jetzt offiziell ) in unseren Controllern verwenden:async function myController() { const user = await findUserByUsername(); const chat = await getChat(user); const somethingElse = doSomethingElse(chat); return somethingElse; }
Beachten Sie
async
vor demfunction
.Router
Endlich der Router, yay!
Wir haben dem Benutzer also noch nichts geantwortet. Wir haben lediglich einen Controller, von dem wir wissen, dass er IMMER einen
Promise
(hoffentlich mit Daten) zurückgibt . Oh!, Und das kann möglicherweise eine Ausnahme auslösen, wennthrow new Error is called
oder einige Service-Promise
Pausen.Der Router wird denjenigen, der sie in einer einheitlichen Art und Weise, Steuer Petitionen und Rückgabedaten zu Kunden, einige vorhandenen Daten sein,
null
oderundefined
data
oder ein Fehler.Der Router ist der EINZIGE, der mehrere Definitionen hat. Die Anzahl hängt von unseren Abfangjägern ab. Im hypothetischen Fall waren dies API (mit Express) und Socket (mit Socket.io).
Lassen Sie uns überprüfen, was wir tun müssen:
Wir möchten, dass unser Router in konvertiert
(req, res, next)
wird(username)
. Eine naive Version wäre ungefähr so:router.get('users/:username', (req, res, next) => { try { const result = await getUser(req.params.username); // Remember: getUser is the controller. return res.status(200).json(result); } catch (error) { return res.status(500).json(error); } });
Obwohl es gut funktionieren würde, würde dies zu einer enormen Menge an Codeduplizierungen führen, wenn wir dieses Snippet auf allen unseren Routen kopieren und einfügen würden. Wir müssen also eine bessere Abstraktion machen.
In diesem Fall können wir eine Art gefälschten Router-Client erstellen, der ein Versprechen und
n
Parameter einhält und dessen Routing undreturn
Aufgaben genau wie auf jeder der Routen ausführt./** * Handles controller execution and responds to user (API Express version). * Web socket has a similar handler implementation. * @param promise Controller Promise. I.e. getUser. * @param params A function (req, res, next), all of which are optional * that maps our desired controller parameters. I.e. (req) => [req.params.username, ...]. */ const controllerHandler = (promise, params) => async (req, res, next) => { const boundParams = params ? params(req, res, next) : []; try { const result = await promise(...boundParams); return res.json(result || { message: 'OK' }); } catch (error) { return res.status(500).json(error); } }; const c = controllerHandler; // Just a name shortener.
Wenn Sie mehr über diesen Trick erfahren möchten , können Sie die Vollversion in meiner anderen Antwort in React-Redux und Websockets mit socket.io (Abschnitt "SocketClient.js") nachlesen.
Wie würde Ihre Route mit dem aussehen
controllerHandler
?router.get('users/:username', c(getUser, (req, res, next) => [req.params.username]));
Eine saubere Zeile, genau wie am Anfang.
Weitere optionale Schritte
Controller verspricht
Dies gilt nur für diejenigen, die ES6-Versprechen verwenden. Die ES2017-
async / await
Version sieht für mich bereits gut aus.Aus irgendeinem Grund mag ich es nicht,
Promise.resolve()
Namen verwenden zu müssen, um das Initialisierungsversprechen zu erstellen. Es ist einfach nicht klar, was dort los ist.Ich möchte sie lieber durch etwas Verständlicheres ersetzen:
const chain = Promise.resolve(); // Write this as an external imported variable or a global. chain .then(() => ...) .then(() => ...)
Jetzt wissen Sie, dass dies
chain
den Beginn einer Kette von Versprechen markiert. Jeder, der Ihren Code liest, oder wenn nicht, geht er zumindest davon aus, dass es sich um eine Kette handelt, die ein Service funktioniert.Express-Fehlerbehandlungsroutine
Express verfügt über eine Standardfehlerbehandlungsroutine, mit der Sie mindestens die unerwartetsten Fehler erfassen sollten.
router.use((err, req, res, next) => { // Expected errors always throw Error. // Unexpected errors will either throw unexpected stuff or crash the application. if (Object.prototype.isPrototypeOf.call(Error.prototype, err)) { return res.status(err.status || 500).json({ error: err.message }); } console.error('~~~ Unexpected error exception start ~~~'); console.error(req); console.error(err); console.error('~~~ Unexpected error exception end ~~~'); return res.status(500).json({ error: '⁽ƈ ͡ (ुŏ̥̥̥̥םŏ̥̥̥̥) ु' }); });
Was mehr ist, sollten Sie wahrscheinlich etwas wie Debug oder Winston anstelle von verwenden
console.error
, die professionellere Methoden zum Umgang mit Protokollen sind.Und so stecken wir das in
controllerHandler
:... } catch (error) { return res.status(500) && next(error); }
Wir leiten jeden erfassten Fehler einfach an den Fehlerbehandler von Express weiter.
Fehler als ApiError
Error
wird als Standardklasse zum Einkapseln von Fehlern beim Auslösen einer Ausnahme in Javascript angesehen. Wenn Sie wirklich nur Ihre eigenen kontrollierten Fehler verfolgen möchten, würde ich wahrscheinlich denthrow Error
und den Express-Fehlerhandler vonError
auf ändernApiError
, und Sie können ihn sogar besser an Ihre Bedürfnisse anpassen, indem Sie ihm das Statusfeld hinzufügen.export class ApiError { constructor(message, status = 500) { this.message = message; this.status = status; } }
Zusätzliche Information
Benutzerdefinierte Ausnahmen
Sie können jede benutzerdefinierte Ausnahme jederzeit mit
throw new Error('whatever')
oder mithilfe von auslösennew Promise((resolve, reject) => reject('whatever'))
. Du musst nur damit spielenPromise
.ES6 ES2017
Das ist ein sehr einfühlsamer Punkt. IMO ES6 (oder sogar ES2017 , das jetzt über offizielle Funktionen verfügt) ist die geeignete Methode, um an großen Projekten zu arbeiten, die auf Node basieren.
Wenn Sie es noch nicht verwenden, schauen Sie sich die ES6- Funktionen sowie den ES2017- und Babel- Transpiler an.
Ergebnis
Dies ist nur der vollständige Code (bereits zuvor gezeigt) ohne Kommentare oder Anmerkungen. Sie können alles in Bezug auf diesen Code überprüfen, indem Sie zum entsprechenden Abschnitt scrollen.
router.js
const controllerHandler = (promise, params) => async (req, res, next) => { const boundParams = params ? params(req, res, next) : []; try { const result = await promise(...boundParams); return res.json(result || { message: 'OK' }); } catch (error) { return res.status(500) && next(error); } }; const c = controllerHandler; router.get('/users/:username', c(getUser, (req, res, next) => [req.params.username]));
controller / user.js
import { serviceFunction } from service/user.js export async function getUser(username) { const user = await findUserByUsername(); const chat = await getChat(user); const somethingElse = doSomethingElse(chat); return somethingElse; }
services / user.js
import User from '../models/User'; export function getUser(username) { return User.find({}).exec(); }
models / User / index.js
import { validateUsername } from './validate'; const userSchema = new Schema({ username: { type: String, unique: true, validate: [{ validator: validateUsername, msg: 'Invalid username' }], }, }, { timestamps: true }); const User = mongoose.model('User', userSchema); export default User;
models / User / validate.js
export function validateUsername(username) { return true; }
quelle
controllerHandler
Trick. Aber können Sie mir bitte einen Vorschlag machen, wenn ich eine Ansicht mit der Template-Engine rendern möchte? Ich meineres.render('index', {title: 'Hello'})
. Wie sollten Controller und Route in diesem Fall aussehen?{title: 'Hello'}
können die aktuellen Objektwerte sein, die bereits an die gesendet wurdenhandleController
. 'index' sollte wahrscheinlich ein zusätzlicher Parameter für diehandleController
Funktion sein, da kein Controller an der Ansicht interessiert ist, die das Ergebnis rendern wird. Abhängig von Ihren Anforderungen möchten Sie möglicherweise zwei oder mehr Ansichten übergeben, um verschiedene Arten von Ergebnissen oder Fehlern zu behandeln. Zunächst würde ich jedoch einfach einen Dateinamen für die einzige Ansicht übergeben und dann einen Standardfehler im "Express Error Handler" haben "(wo es derzeit ein istres.json
).app.emit('some event')
controllerHandler
ImplementierungJeder hat seine eigene Art, das Projekt in bestimmte Ordner zu unterteilen. Die Struktur, die ich benutze, ist
Der Konfigurationsordner enthält Konfigurationsdateien wie Datenbankverbindungseinstellungen für alle Entwicklungsphasen wie "Produktion", "Entwicklung", "Testen".
Beispiel
'use strict' var dbsettings = { "production": { //your test settings }, "test": { }, "development": { "database": "be", "username": "yourname", "password": "yourpassword", "host": "localhost", "connectionLimit": 100 } } module.exports = dbsettings
Der Protokollordner enthält Ihre Verbindungsprotokolle, Fehlerprotokolle zum Debuggen
Der Controller dient zur Validierung Ihrer Anforderungsdaten und Ihrer Geschäftslogik
Beispiel
const service = require("../../service") const async = require("async") exports.techverify = (data, callback) => { async.series([ (cb) => { let searchObject = { accessToken: data.accessToken } service.admin.get(searchObject, (err, result) => { if (err || result.length == 0) { callback(err, { message: "accessToken is invalid" }) } else { delete data.accessToken service.tech.update(data, { verified: true }, (err, affe, res) => { if (!err) callback(err, { message: "verification done" }) else callback(err, { message: "error occured" }) }) } }) } ]) }
Modelle dient zum Definieren Ihres Datenbankschemas
Beispiel mongoDb Schema
'use strict' let mongoose = require('mongoose'); let schema = mongoose.Schema; let user = new schema({ accesstoken: { type: String }, firstname: { type: String }, lastname: { type: String }, email: { type: String, unique: true }, image: { type: String }, phoneNo: { type: String }, gender: { type: String }, deviceType: { type: String }, password: { type: String }, regAddress: { type: String }, pincode: { type: String }, fbId: { type: String, default: 0 }, created_at: { type: Date, default: Date.now }, updated_at: { type: Date, default: Date.now }, one_time_password: { type: String }, forgot_password_token: { type: String }, is_block: { type: Boolean, default: 0 }, skin_type: { type: String }, hair_length: { type: String }, hair_type: { type: String }, credits: { type: Number, default: 0 }, invite_code: { type: String }, refered_by: { type: String }, card_details: [{ card_type: { type: String }, card_no: { type: String }, card_cv_no: { type: String }, created_at: { type: Date } }] }); module.exports = mongoose.model('user', user);
Dienste dienen zum Schreiben Ihrer Datenbankabfrage. Vermeiden Sie das Schreiben von Abfragen im Controller. Versuchen Sie, eine Abfrage in diesen Ordner zu schreiben, und rufen Sie sie im Controller auf
Abfragen mit Mungo
'use strict' const modelUser = require('../../models/user'); exports.insert = (data, callback) => { console.log('mongo log for insert function', data) new modelUser(data).save(callback) } exports.get = (data, callback) => { console.log('mongo log for get function', data) modelUser.find(data, callback) } exports.update = (data, updateData, callback) => { console.log('mongo log for update function', data) modelUser.update(data, updateData, callback); } exports.getWithProjection = (data, projection, callback) => { console.log('mongo log for get function', data) modelUser.find(data, projection, callback) }
utils ist für allgemeine Dienstprogrammfunktionen gedacht, die in Ihrem Projekt häufig verwendet werden, z. B. zum Verschlüsseln, Entschlüsseln von Kennwörtern usw.
Beispiel
exports.checkPassword = (text, psypherText) => { console.log("checkPassword executed") console.log(text, psypherText) return bcrypt.compareSync(text, psypherText) } exports.generateToken = (userEmail) => { return jwt.sign({ unique: userEmail, timeStamp: Date.now }, config.keys.jsonwebtoken) }
quelle
Die Antwort von rohit salaria erklärt im Grunde die gleiche App-Struktur, die Sie in Java gewohnt sind.
Ich habe jedoch ein paar Bemerkungen. Das erste und wichtigste ist, dass dies nicht Java ist. Es mag offensichtlich klingen, aber schauen Sie sich einfach Ihre Frage an und sehen Sie, dass Sie dieselbe Entwicklungserfahrung mit denselben Konzepten suchen, die Sie in der Java-Welt verwendet haben. Meine folgenden Bemerkungen sind nur die Erklärung dafür.
Fehlende DTOs. In Java sind sie nur erforderlich, Punkt. In einer Java-Webanwendung, in der Sie Ihre Daten in einer relationalen Datenbank speichern und Daten an das Front-End in JSON senden und empfangen, konvertieren Sie die Daten natürlich in ein Java-Objekt. In einer Node-App ist jedoch alles Javascript und JSON. Das ist eine der Stärken der Plattform. Da JSON das übliche Datenformat ist, ist es nicht erforderlich, Code zu schreiben oder von Bibliotheken abhängig zu sein, um zwischen dem Datenformat Ihrer Ebenen zu übersetzen.
Übergeben des Datenobjekts direkt von der Anforderung an das Modell. Warum nicht? Wenn Sie JSON als gemeinsames Datenformat vom Front-End bis zur Datenbank verwenden, können Sie das Datenmodell Ihrer App auf einfache Weise zwischen all Ihren Ebenen synchronisieren. Natürlich müssen Sie diesen Weg nicht gehen, aber er reicht meistens aus. Warum also nicht? Die Validierung erfolgt im Modell, wo es gemäß der MVC-Theorie gehört (und nicht im Controller, wo Faulheit und Pragmatismus es oft ausdrücken :)).
Für den letzten Gedanken möchte ich hinzufügen, dass dies nicht die beste Plattform ist, wenn es um die Skalierung der Projektgröße geht. Es ist überhaupt Nod Bat, aber Java ist in dieser Hinsicht besser.
quelle
einfache und grundlegende Regel
Ich entwickle Angular2-Anwendung mit NodeJS, Angular2 Ich werde Ihnen mit meiner Verzeichnisstruktur helfen.
Ich hoffe es hilft :)
quelle