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.