How to apply layered architecture of Java/Spring in NodeJs? - javascript

I have been trying to learn NodeJS for quite some time now. All the books and tutorials seems to follow similar pattern of structuring their code. Example -
const express = require('express');
const app = express();
app.set('view engine','hbs');
app.get('/', (req, res) =>{
res.render('index');
});
app.get('/getName', (req, res) =>{
// Mock DB call to fetch Name
res.render('displayName');
});
app.listen(3000, () => {
console.log("Started server on port : 3000");
});
As you can see above, the /getName controller is performing DB call as well as returning the view. So the business logic as well as the CRUD operation is being done at the same place.
I come from the world of JAVA and there we do it slightly differently. For example, a Spring Boot application would contain the following structure -
DTO
Repository
Service
Controller
So, controller classes are the actual endpoints which do not perform any business logic but call the underlying service class to handle all that. The service classes implement the business logic and persist/fetch data required for it with the help of the repository classes. The repository on the other hand handles the CRUD operations.
This seemed like a sane way to develop software. Given that each class has their defined roles, it becomes really easy to handle any change.
I understand the NodeJs is a dynamic but -
1. Is there a way to separate out the functionality like we do in Spring ? If not,
2. How to structure large projects having multiple endpoints and DB transactions.
Regards
EDIT -
Consider the following scenario -
I have a requirement, where I need to fetch a list of users from the database whose status is True ( assume status is a boolean field in the model ).
In Java -
#Service
public class UserService {
#Autowired
UserDetailRepository userDetailRepository;
#Override
public UserDetail getUserDetail (boolean status) {
UserDetail userDetail = UserDetailRepository .findUserDetailByStatus(status);
return userDetail ;
}
Controller.java -
#GetMapping("/getUserDetails")
public ArrayList<UserDetail> getUserDetail(){
return UserService.getUserDetail(true);
}
Now, if the requirement changes and there needs to be a new endpoint that returns only top 10 user details whose status is true. In that case, we can add a new controller and just limit the returned results to 10. We can make use of the same business logic/ service class.
Controller.java
#GetMapping("/getUserDetailsTop10")
public ArrayList<UserDetail> getUserDetail(){
List<UserDetails> users = UserService.getUserDetail(true);
// Filter the list to send top 10
// return users .
}
If I have to implement the same use case in NodeJS, I'll have to write the business logic to fetch the user twice -
const express = require('express');
const app = express();
app.set('view engine','hbs');
app.get('/getUserDetails', (req, res) =>{
// Call DB and get users whose status is True
res.send(userdetails);
});
app.get('/getUserDetailsTop10', (req, res) =>{
// Call DB and get users whose status is True
// Filter the returned results and limit the result to 10 values only
res.send(userdetails);
});
app.listen(3000, () => {
console.log("Started server on port : 3000");
});
At best, I can abstract away this logic into a function, that will return a list of users with status True but then again this approach is not very scalable. There has to be a complete separation of Business logic from controller.

Not really an answer... but some thoughts, as I come from C# and started as a NodeJs developer 3 years ago.
Dependency Injection
This is (sadly) rarely used in many NodeJs projects that I see. There are some npm modules that provide this feature, but none of them have convinced me with a proper approach and API. Dependency Injection is still perfectly possible, only, a bit uglier because of some boilerplate code you have to write. So sadly no easy #autowire approaches. So yes you can do Dependency Injection like with Spring, but not as convenient. But let my opinion not stop you from researching Dependency Injection libraries for node.
Architecture
You can still use the concepts of DTOs, Repositories, Services and Controllers. Only for some (odd) reason, the majority of tutorials and guides out there forget about common sense architecture and just throw everything within a single controller. Don't let them seduce you in forgetting about the concepts you learned in Java. Not that OOP and Java doesn't have flaws, but there is a difference in "writing JavaScript style code" and "let's forget about proper architecture all together".
Do note that you might see some more "functional programming" patterns rise in the NodeJs community, which is not bad (but neither is OOP).
How to structure large projects having multiple endpoints and DB
transactions.
Ignore the bad examples you see out there, just follow your gut feeling from your Java experience how to structure your code, with the right layers, responsibilities and Design Patterns. Only keep in mind that you don't have interfaces and there is no easy alternative to that. So you'll have to learn to work your way around that, but you can still write elegant code without them.
Middleware
Middleware is a common concept for NodeJs applications, they are similary-ish to the proxy/aspect oriented programming style. You might find example projects with excessive amount of middleware, don't let yourself be tempted by that either. Middleware is good for auth/serialisation/headers/security but not for business logic. I've seen middleware hell and middleware driven development, it ain't pretty.
Repository
A lot of people use directly the MongoDb/Mongoose/SQL client without using the adapter pattern like Repository, leaking MongoDb queries all over their solution.
My advice for you would be, just use the same structure and approach you are familiar with, but with the tools that JavaScript gives you. I genuinely love JavaScript, but it makes me cringe when I look at the lack of design and architecture that I see in the mainstream resources, sure small projects and PoC's don't need extensive design, but often when project grow, they don't clean up. How you structure a project should be agnostic to the language and framework you are writing in.
Happy coding 😄

An idea:
const express = require('express');
const app = express();
const { userDetails } = require('./usersBusinessLogic.js')
app.set('view engine','hbs');
app.get('/getUserDetails', async (req, res) =>{
const limit = parseInt(req.query.limit || '0')
if (IsNaN(limit)) {
res.status(400)
return res.end('limit expected to be a number')
}
const detailsResponse = await userDetails({ limit })
res.send();
});
And then you write your logic as function userDetails({ limit, ...rest } = { limit: 0 }).
In another file i.e. usersBusinessLogic.js:
async function userDetails({ limit, ...restOfTheOptions } = { limit: 0 }) {
console.log({ limit, restOfTheOptions }) // TODO logic here.
// const result = await db.find('users', {...})
// return serialize(result)
}
// function serialize(result) {
// return JSON.stringify(result)
// }
module.exports = {
userDetails
}
After which you can call GET /userDetails or GET /userDetails?limit=10.
Let me know what do you think.

UserService.js
import { userDetailRepository } from '../models/user';
class UserService {
getUserDetail (status) {
const userDetail = UserDetailRepository.findUserDetailByStatus(status);
return userDetail;
}
}
export const userService = new UserService();
UserController.js
import { userService } from '../services/user';
#RouterAdapter
class UserController {
#GetMapping("/getUserDetails")
getUserDetail() {
return userService.getUserDetail(true);
}
#GetMapping("/getUserDetailsTop10")
getUserDetailsTop10() {
return userService.getUserDetail(true).slice(10);
}
}
export const userController = new UserController();
app.js
import * as express from 'express';
import { userController } from './controllers/user';
const app = express();
app.set('view engine','hbs');
app.use(userController);
app.listen(3000, () => {
console.log("Started server on port : 3000");
});
I intentionally "warp" my js code to match with java style so perhaps you feel at home. Personally I don't write js this way, I prefer using function way more than class.
I did not include the implementation of two decorators (#RouterAdapter, #GetMapping) I used, that's a detail not related to the discussion. I can assure you this is doable in js. The only thing missing is that js doesn't have runtime method overload, so I have to name 2 diff controller methods.
The point I'm making here is that design patterns are mostly language agnostic. If you're familiar with the lang, you can always find a way to handle separation of concern well.
Now I intentionally use class in above example, but a function acting as a UserService doesn't make it less reusable. I don't see why you think "it doesn't scale".

Related

explanation for the exported express module, it's structure and the difference between app object and express object

I am trying to learn expressjs, however, I managed to confuse myself out. I would appreciate if someone can shed some light. basically how can the exported express module be both a function and an object??
I console.log(express)1 in my app.js and came up to this. Having looked at the expressjs source code I got even more confused on what is going on under the hood. I think I shouldnt have looked very deep inside. My main questions being:
is express a constructor function, if so why dont we use '
what is the difference between the "app" and the "express"? (I just cant seem to grasp what they mean by express() being a top-level
function...)
Many Thanks!
express is a function. When called, it returns an express router instance (That's what you've stored in app). As functions are objects in JavaScript, the express function has additional properties, e.g. express.static.
function express() {
return { get(url, handler) { /*...*/ }, /*...*/ };
}
express.static = function() { /*...*/ };
const app = express();
app.get("path", (req, res) => { /*...*/ });

ExpressJS/NodeJS and Object Orientated Programming

I'm new to the industry, but it seems like most companies seem to test/emphasize OOP knowledge heavily during the interview process even when their stack is based mostly in javascript/NodeJS/typescript.
I'm confused how this would be applicable to developing backend applications/APIs using a framework like ExpressJS?
For example, a sample GET route in pseudo code:
app.get('/', async(req, res) => {
const exampleData = await database.querySomething()
res.send(exampleData)
})
It seems to me the coding style for creating these REST APIs is somewhat procedural. E.g. receive a request, make some database action or query based on the parameters, add in calculations/data wrangling, send back the final data.
Where does OOP come into play? What would be a use case for OOP in back end nodejs applications?
You need to focus on the language, i.e Javascript, and not Express when you come to OOP concept.
One of the best ressource on the web is the MDN website for JS language. Start here
You can write your code in es6 or use frameworks which fully supports oops approach.Check out frameworks like - fortjs, nestjs etc.
e.g - A controller in fortjs would look like -
export class UserController extends Controller {
#defaultWorker()
async getUsers() {
const service = new UserService();
return jsonResult(service.getUsers());
}
#worker([HTTP_METHOD.Post])
#route("/")
async addUser() {
const user = {
name: this.body.name,
gender: this.body.gender,
address: this.body.address,
emailId: this.body.emailId,
password: this.body.password
};
const service = new UserService();
const newUser = service.addUser(user);
return jsonResult(newUser, HTTP_STATUS_CODE.Created);
}
}

SailsJS policy on request method rather than route

Is there any way I can trigger a policy on a specific request method (e.g. DELETE) rather than on specific routes?
I'd imagine something like this:
module.exports.policies = {
'DELETE *': 'isAdmin'
}
My goal here is to expose the blueprint api to admins only, so that I can keep it in production, as it's a very useful tool for allowing third party scripts to add extra functionality.
I'm on Sails 1.0 right now.
One way to do that might be to add the check for the request method to the actual admin policy, however that doesn't quite seem like the best solution to me.
You can override the blueprint for all models for a particular method. You can do this for DELETE by creating a file destroy.js in /api/blueprints/ and then adding your code for what you want to do when a DELETE comes through:
module.exports = function(req,res, next) {
if(ACLService.hasPermission(req.user.acl, 'admin')) {
//Ok to allow delete here
} else {
return res.unauthorized();
}
};
This is how I've done it in the past, but looking at the docs for the just released SailsJS 1.0:
https://sailsjs.com/documentation/reference/blueprint-api
You may need to add this hook for overriding blueprints in 1.0
https://www.npmjs.com/package/sails-hook-custom-blueprints
Here is one method that you can use, I am not claiming that it is the right way, but you can consider it:
You can write your own hook. How to do this: https://sailsjs.com/documentation/concepts/extending-sails/hooks/project-hooks
Basically here is the solution with a hook:
1 Create a hooks folder under your api folder.
2 In the hooks folder create another folder - the name will be the name of your hook (say my-hook).
3 In api/hooks/my-hook create a file index.js and in it put the following code:
module.exports = function myHook(sails) {
return {
routes: {
before: {
'/*': function (req, res, next) {
if (req.method.toUpperCase() === 'DELETE') {
return sails.hooks.policies.middleware.isadmin(req, res, next); // note - your policy function name must be called here with all lowercase, otherwise it will not work.
}
return next();
}
}
}
};
};
Then in your isAdmin.js policy you can check if your user is an admin and if not:
return res.forbidden();
if it is admin:
return next();

Express : Does mysql queries in app.js make sense

Does app.js get processed only once?. In that case, does it make sense to run setup queries in app.js so that I don't have to run it on every request?.
I have a table to countries and regions that I need in several requests.
I am looking at an implementation something like this https://stackoverflow.com/a/21133217/406659
No putting this in app.js does not make sense. I would create a models folder and encapsulate all of your database logic in a module within that folder. Then on startup of the app (ie on app.listen() callback) I would call a method from that module which does the setup for me. For example:
Some DB module in models
module.exports = {
setup: () => {
// Set up logic here - checking if tables exist etc
}
}
Then in app.js or equal
require <SOME DB MODULE>
app.listen(<PORT>, () => {
<SOME DB MODULE>.setup();
})
Note this is a generalised approach as I don't know the specifics of your setup - This approach just ensures encapsulated and reusable code. I've also used ES6 syntax where possible.
Hope this helps
Dylan

How can I use Y.mojito.models in middleware?

I'm using nodejs with the mojito mvc framework.
Do I have access to some global variables from middleware? How can I use Y.mojito.models in middleware?
// ./middleware/mymiddleware.js
module.exports = function (req, res, next) {
// How to use Y?
//Y.log('fails');
//Y.mojito.models['MyModel'].fetch(function(err, data) {
// next();
//});
};
I strongly recommend not to try to do that. Those are express middleware, they are suppose to do a quick job, sometimes async job but generally, if the request is meant to be processed by mojito dispatcher engine, you should not try to access runtime components in a middleware.
That been said, there is an internal API (that again I strongly recommend not to use) that will give you access to the global Y which holds all the YUI modules at the server side. Something like this:
module.exports = function (config) {
// then `config.Y.mojito.models['MyModel'].fetch()` is available here
return function (req, res, next) {
next();
}
};
Aside from that, if what you're looking for is a way to share models, or expose global models, you should look at mojito-models-addon, expose method.
Again, the config.Y thing is private and we might change that at any time.
update: in mojito there are two types of middleware, the traditional express middleware and the mojito middleware, which will have to be prefixed with mojito-, and these are the one that should expose a function that receives config and returns a transitional express middleware. Without the prefix, it will just be called per requests without preparation.

Categories

Resources