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);
}
}
Related
I started developing with angular / typescript and created a service for my .NET Core API and I like to know what's the best way to get a clean and reliable object from my service.
I have an .NET CORE REST API returning a json result represented by the class definition below.
Service:
demoservice.getDemo().subscribe((val) => new Demo(val));
Demo-Class is the following code:
export class Demo {
public id : number;
public name : string;
public subDemo: SubDemo;
constructor(demo: Demo) {
this.id = demo.id;
this.name = demo.name;
this.subDemo = new SubDemo(demo.subDemo);
}
}
export class SubDemo {
public demoList : ListDemo[]
constructor(subDemo: SubDemo) {
this.demoList = new Array<ListDemo>();
subDemo.demoList.forEach(dl => {
this.demoList.push(new ListDemo(dl))
});
}
}
export class ListDemo {
constructor(listdemo : ListDemo) {
this.birthday = listdemo.birthday;
this.smoker = listdemo.smoker;
}
get birthDayFormatted() : Date {
return new Date(this.birthday);
}
public birthday : string;
public smoker : boolean;
}
I this the best way (full implement all constructors) to create a object. Please note I like to use the "getter" - functionality of my ListDemo Class.
Is there no better way? I just found some Object.clone / Object.assign / Object.create.
But none of this solution is comprehensive...
I am really interested in your experience..
Since you're using better & best I will answer with my, probably unwanted, opinion. Disclaimer: I'm no guru, this answer is based on opinion, feel free to disregard.
Don't do it. Your server has a set of objects in its domain, probably some kind of problem solving or data storage domain.
Your client has a set of objects in its domain, typically focused on presenting the data to the user and allowing the user to understand and manipulate it.
Both of these domains may have objects that have the same name or are based on the same real world concept. It can be tempting to feel like they are the same domain with the same objects. They are not. If they were the same you would not be writing a client and a server you would be writing two of the same thing. The two should communicate with pure data objects. In TS this means you should only create an interface, not a class, for the objects you receive from the server.
Instead, start over. Create a new domain based on what you want to appear in the API. If you design your API from the top (UI) down to the bottom (access services) you'll likely find that you don't have a one-to-one mapping between objects. On the occasions you do then you can probably get away with the occasional assign / merge (see below) but I've personally never found reason to do more than what you posted above.
Should you persist you may come across the option to reassign the prototype of the JSON literal from the base object to the class the data represents but that is a contentious topic and potential performance pitfall.. Your best bet is probably to just do a recursive/deep assign like Lodash's merge.
Just use interfaces not classes.
export interface Demo {
id: number;
name: string;
subDemo: SubDemo;
}
export interface SubDemo {
demoList: ListDemo[];
}
export interface ListDemo {
birthday: string;
smoker: boolean;
}
and your api should return the same shape, you should just be able to go
getDemo(): Observable<Demo> {
return this.http.get<Demo>('url');
}
in your service and in your component assign it to a property
demo$ = this.service.getDemo();
and then use the observable with the async pipe in your template.
<ng-container *ngIf="demo$ | async as demo">
{{ demo | json }}
</ng-container>
The less you have to manipulate your data the better. I have a VS pluging that allows you to paste C# classes into TS files and it converts them to TypeScript interfaces on the fly.
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".
I'm using a library developed by Chris Esplin (firebase-paginator) but i get FirebasePaginator is not a constructor when i try to use it. I'm actually developing a PWA in Vue.js 2.0, the script where this is getting called it's in app-name/src/store/index.js which is the main file for manage vuex states, mutations, actions and getters.
var FirebasePaginator = require('firebase-paginator')
var options = {
pageSize: 20,
finite: true,
auth: 'SomeAuthToken',
retainLastPage: false
}
var ref = firebase.database().ref('track')
var paginator = new FirebasePaginator(ref, options)
var collection = null
paginator.once('value')
.then(() => {
collection = paginator.collection
console.log(paginator, collection)
})
I've tried with a custom paginator that found googling, i adapted to eslint rules and i get the same constructor error when i make the call with new
I'm unfamiliar with Vue.js, but I only ever designed FirebasePaginator to work with script tags for the browser or require calls for Node.js. The "not a constructor" error indicates that the library wasn't imported correctly.
Try adding /node_modules/firebase-paginator/firebase-paginator.js to the page in a script tag. I'm sure there's a way to get require('firebase-paginator') to work with require statements in the browser, but I don't use them that way myself, so if you figure it out, I'd welcome a pull request.
Also, make sure that you don't use your legacy admin auth token in the browser. I think the REST API will work with auth tokens from your authenticated user, but I haven't played with that feature in a while, so I'm uncertain. Infinite pagination may be easier to implement.
This could be tagged opinion based. But I am looking for standard/best-practise. I am building an Angular 2 application and I have to manipulate the data from the API before I show it in the template. For example, if my service looks like:
getData(id: number): Observable<Data> {
return this.http
.get(this.url + '/' + id)
.map((res) => {
return res.json().data;
});
}
prepareData(data) {
// manipulate and return the data
}
And on my component, I could call the service like this:
getData(id: number): void {
this.dataService.getData(id)
.subscribe((data: Data) => {
this.showData = this.dataService.prepareData(data)
};
}
But is this the standard approach? Or should the prepareData function be included in the component instead?
Another way to phrase it is, should the service be heavy when compared to components or should it be light and only act as an interface to get the data?
Simple, generic transformations everyone will need (such as res => res.json().data) should go in the service.
View-specific transformations that depend on presentation logic (such as data => data.user.firstName + ' ' + data.user.lastName) should go in your components.
The service should be able to provide data without knowing what will be rendered. The Component should be able to render data without knowing where it came from.
Think of your angular application from the n-layer architecture perspective. Respect the DRY principle and and least some of the SOLID points - in this case the S in your services. Think of "Components" as view-presenter pairs (where the model is somewhere else), rather than ASP.NET's webForms (markup coupled with code behind).
There are two basic possibilities that will influence details of your design - is your service server endpoint aware of your view models or not. Some teams take the approach, where little transformation is needed in your angular application, because the server-side models are very close to angular view models. In those cases, anything that is not view-specific is okay to be in your service, with the view-specific transformations being okay in the component.
On the other hand, if you need to map a more generic service/server response into your angular view model, you don't want to do this in the service. Nor you want to do this in the component if there is the possibility to reuse this model (think of it as business class, not just DTO) in other views. Since mapping may involve business rules, it's better to isolate a dedicated model and mapper layer in your angular application and keep services and components DRY and "S". Creating a separate model/business layer is also good, because it may actually help you easily replace UI framework later.
You can manipulate and return the data in getData(). You can write your code as following-
getData(id: number): Observable<Data> {
return this.http
.get(this.url + '/' + id)
.map((res) => {
let data= res.json().data;
return this.prepareData(data);
});
}
prepareData(data) {
// manipulate and return the data
}
I hope this will help you if you have specific condition you can describe in brief and i'll help you.
I develop a web api server and recently started devloping the client side also, which is developed in Angular-js and typescript.
I have something I'm not sure about regarding the design of my client side code.
Say for example that I have a view that presents list of items and this view's controller is created many times, everytime a button is clicked (for example). This view presents a list of items and therefore this array is receieved throguh Http GET in the controller each time it is created, and the view has
ng-repeat="item in vm.items"
However, it seems to me that if I save this list on a service, which from my understanding is created once per client only, I could use a single GET request once when the service is created and use it in the view like this:
ng-repeat="item in vm.service.items"
And updates to this list of items will happen through usage of signalr only.
Does the second approach has it's flaws? Because right now I can't see a reason for not doing so.
Yes, this sounds like a classic DAL-service for me. I would recommend you to work with a promise there so you are able to track when the desired data is resolved. Here is an example:
export class EntityProvider {
static serviceName: string = "entityProvider";
private entity = null;
static $inject : string[] = ["YourService", "$q"];
constructor(yourService: YourService, private mQService: ng.IQService) {
}
get getEntity(): ng.IPromise<ConnectionInfoDto[]> {
if (this.entity === null) {
return this.yourService.getEntity()
.then(result => {
this.entity = result.data;
return this.entity;
});
} else {
return this.mQService.when(this.entity);
}
}
static bootstrap(aModule: ng.IModule) {
return aModule.service(this.serviceName, EntityProvider);
}
}