admin管理员组

文章数量:1390192

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 e 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 bees 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 plete separation of Business logic from controller.

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 e 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 bees 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 plete separation of Business logic from controller.

Share Improve this question edited Apr 9, 2019 at 17:34 Boudhayan Dev asked Apr 9, 2019 at 16:28 Boudhayan DevBoudhayan Dev 1,0296 gold badges22 silver badges44 bronze badges 23
  • 2 The format of the question isn't the problem, the content of the question is. There are hundreds of different possible equally-valid answers. The Node.js ecosystem is rich and varied, there's no one way you do this. Please do see the links above, in particular this one. It's not that it's not a perfectly reasonable question to ask, it's just not suited to the format of this site. – T.J. Crowder Commented Apr 9, 2019 at 16:37
  • 1 similar question is discussed to some extent here stackoverflow./questions/5178334/… – 1565986223 Commented Apr 9, 2019 at 16:54
  • 1 Hmm, I'm not the best guy to answer your question. Not that experienced in backend js, but I'll try. Js munity embrace things that's light-weighted. We got runtime that has native support for first class function, event loop and such. This way we tend to naturally seek solution in patterns like pipeline chaining, observable stream, pub/sub. We too have separation of concern, but just not like java style. – hackape Commented Apr 9, 2019 at 17:03
  • 1 "Only way is to create a function which can be referenced by multiple endpoints." - yes, that's precisely what you would do. (When need arises, otherwise YAGNI). You can put such functions in their own modules, files, or even write them as classes if you prefer that. I don't understand why you think this is not sufficient or scalable, or why you don't consider it to separate concerns. – Bergi Commented Apr 9, 2019 at 17:56
  • 2 I think you are feeling conceptual disfort facing multi-paradigm goodness in js land. Java does have some burden going functional programming, but not in js. FP or OOP, I just go with whichever fits, depending on use case. – hackape Commented Apr 10, 2019 at 4:17
 |  Show 18 more ments

5 Answers 5

Reset to default 3

Not really an answer... but some thoughts, as I e 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 mon 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 munity, 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 mon 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

本文标签: javascriptHow to apply layered architecture of JavaSpring in NodeJsStack Overflow