What is Onion Architecture ?
Onion Architecture is a project structural pattern that favors us with maintainable and testable code for enterprise systems. It enables us to have a different layer for a different level like Model, DA, Service, Controller, View.
- Loose Coupling
- Better maintainability at a different layer
- Better testability as projects are divided into layers
- Modulour concept
The Layers :
- DA Layer: DataAccess Layer which will specifically deal with Database operation like Read, Insert, Update, and Delete.
- Service Layer: This layer will contain our business logic for every api.
- Controller Layer: This layer will have all routes defined and call to a specific service layer’s function.
- Types: This layer will have all types, interface defined.
We will be implementing Onion Architecture in Node.JS having an approach towards the OOPs concept. We will use Typescript here as it provides many more features for OOPs.
Database Setup :
As we will be using MySQL database, you should have phpMyAdmin installed then open
- Create a database named onion
- Inside onion create a table named student with the following query
Project Setup :
Install Typescript globally in your system with this command
npm i -g typescript
Open your command prompt, go to the project directory, and do tsc –init. This will create a tsconfig.json file which contains a Typescript configuration for this project.
Place the following code in tsconfig.json
Now do npm init and let’s install couple of package.
npm i dotenv express mysql shortid
npm i -D @types/express @types/mysql @types/shortid typescript
As we are using typescript, @types/package will have types defined of all specified packages. Now install ts-node-dev, it will watch changes in the project.
npm i -D ts-node-dev
Change scripts command to following twos
- npm run dev will start the server and watch for file changes
- npm run build will build the production version of our project in es5 as the Node.JS engine doesn’t understand typescript.
We will use MySql Database. So, create a .env file and have database server’s USER, HOST, PASSWORD, and DATABASE defined like the following.
Now create a folder named src, inside create folders named DA, routes, service, types, and create a file server.ts, our project entry file. The structure will be like the following.
Here we finish our project setup, now let’s move to the implementation part.
- Let’s create DBManager.ts and DBConnection.ts inside DA folder
- Create types.ts inside types inside types folder.
Define an interface IDBManager, IStudent, and type MySqlType inside types.ts. IDBManager will have our DBManager’s function’s types defined. Place the following code.
All these functions will receive a query of string type and paramCollection as Array of the number, string, boolean and undefined type as argument and returns Promise of MySqlError or any type. Although ReadData function will have paramCollection as Optional Parameter because it may have a select query without where clause.
Now go to DBConnection.ts, place the following code.
Now go to DBManager.ts, place the following code.
These functions will reject if there is any error otherwise resolve if the query successfully executes. In this project, we will implement a simple CRUD operation for the student database.
Create a file StudentDA.ts inside the DA folder and have the following code. Let me describe what this file contains instead of code. Although you will get Code Implementation in my GitHub repo.
We will have a class named StudentDA which will extend DBManager class. The followings are functions defined in StudentDA class. DBManager’s functions return a promise, we will use async/await with try/catch in the calling function. On successful execution of a query, StudentDA’s will return a result or on error, it will throw an error to it’s calling function or service layer.
- GetStudents: This function will fetch all students’ records. It passes a SQL query to the ReadData method of DBManager class. On successful execution of a query, it will return the result, and or on error, it will throw an error to it’s calling function or service layer.
- GetStudent(id) : This function will fetch single student’s record. It receives the id of string type from the service layer or it’s calling function. It passes SQL query with the student’s id as paramColection as Array to ReadData method DBManager class.
- CreateStudent(data:IStudent) : This function will create student record. It receives a student’s records as IStudent type. It passes SQL query with student’s details as paramCollection as Array to InsertOrUpdateData method of DBManager class.
- UpdateStudent(data:IStudent) : This function will update record of a single student. It receives a student’s record of IStudent along with id. It passes SQL query with student’s details as paramCollection as Array to InsertOrUpdate method of DBManager class.
- DeleteStudent(id:string) : This function will delete a single student record. It receives the student’s id. It passes SQL query with student’s id as paramCollection as Array to DeleteData method of DBManager class.
Now Create index.ts file inside the DA folder which will be our root or common file for exporting all modules from the DA folder. Import StudentDA class and export it from index.ts.
Now create student.service.ts inside the service folder. This file will contain all business logic. Here we will not write any business logic although we will use it for taking data from routes and passing to the DA layer. Following is the code for student.service.ts.
Now create index.ts inside the service folder, import student.service.ts, and export it. Any file you will create inside the service folder import in index.ts and export fro here. It is applicable for the DA and routes folder also.
Now create student.routes.ts inside the routes folder and paste the following code.
What it does actually it receives two parameters route and studentService object and defines all routes accordingly. From every route, we call their corresponding service function and get the result.
Now open server.ts, main entry file of server. Paste the following code.
We are importing all required packages and modules in server.ts.
Here we will be using dependency injection by passing DA and service to service and controller layer. By this process, all the modules stay in loose coupled. Modules are not dependent on one another.
In the StudentRouter function, we are passing the router and studentService object which again takes the StudentDA object in the constructor.
Our project is setup. Open the command prompt and navigate to the project directory.
There is two scripts to run dev and build.
When we will be developing our project, we will use npm run dev.
When we will deploy our project to production, we will use npm run build
Now run npm run dev, hit every API from POSTMAN. You will get the respective result.
And that’s the end.
I have tried the best to explain. Although if you guys have any doubts please comment down or connect me.
Check put GitHub repo for source code
You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
Full Stack Developer, Code Quotient | Tech Writer and Social Media Handler at BlogMarch