Software Architecture of Document Builder

Roshani Ayu Pranasti
pepeel
Published in
8 min readApr 27, 2020

Hello! This might be my last written article related to PPL course for this term. In this post, I will try to explain our project’s, Document Builder, software architecture.

Before we jump right into the topic, I just want to warn you that I have limit knowledge regarding this topic, and I think I don’t have the expertise yet to dictate you anything. But, for the competence of being a developer, I’ll try to explore the material and tell you about my understanding as best as I can.

Reading from the title, some of you might not know about Document Builder. Document Builder is a project that my team and I (our team’s name is pepeel) assigned to finish by the end of PPL course. For now, the application itself called by the name of a document builder. It’s a web-based application that’s developed to ease the document process in Faculty of Law, University of Indonesia.

Software Architecture

The software architecture of a program or computing system is the structure or structures of the system, which comprise software elements, the externally visible properties of those elements, and the relationships among them. —Software Architecture in Practice, 2003

In Document Builder, it consists of four major components, which are Front-end, Back-end, Database, and Docker. The whole idea can be seen in the illustration below that made by my teammates, Cahya and Firman.

Software Architecture of Document Builder

Front-end

For front-end, we use React.js

What is React.js? React.js is one of the most popular JavaScript libraries for building user interfaces. As for the implementation detail, we use React Hooks which allows us to use functional components instead of class-based components. In return, it makes our React App (arguably) lighter, more readable, and easily understood. Now see the differences below.

Before using React Hooks
After using React Hooks

As seen above, the class-based component is inherently longer than hooks due to class-related functions, such as render, construction and lifecycle. The class-based component also seems harder to understand for beginners because of numerous restrictions and use cases of that class component functions.

Then from these interfaces, users will do various tasks, for example in Document Builder case, they will upload documents, input data, see the documents, and so on. Those are served through our back-end knowing that the front-end can’t access the data directly. This kind of behavior is done to limit malicious users’ actions, not burden the client’s browser, and serve only one source (back-end) that can access the database.

The flow goes like this:

  1. Front-end receives input from users.
  2. Then, it will make an HTTP request to back-end.
  3. From there, the front-end will receive a response and render the interface according to the received response.

I’ll try to give an example.

The API constant
The code for signup/register user function

The actionSignupUser function above will be called when the user clicks the submit button on the Register Page. The front-end will make an HTTP request to the defined back-end’s URL, in this case, called API_AUTH_SIGNUP. If successful, the front-end will receive a success data response and pop the swal function telling the user that his/her account is successfully created. If not, the front-end will be showing an error.

Back-end

We use the Express.js framework for our back-end services. Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. Our back-end acts as a REST API which defines a set of constraints to be used for creating Web services then serves as a mediator between front-end and database.

Most of our business logic for the system is located here. The flow goes like this:

  1. Back-end will receive an HTTP request from front-end.
  2. Then, it will do the business logic.
  3. Finally, it will reply to the request (front-end) with an HTTP response.

See the example below:

The route
The function it calls

The code above will receive a request from front-end at the specified endpoint. Then, it will run the logic based on the corresponding service, in this case, UserService. If successful, it will return HTTP status 201 (CREATED) with JSON contains user's data as a response.

The service

The code above is the logic code for the service that was called in the previous code. The findByEmail function queries the database to get the required data based on the parameter email. You may notice that the query is not done by writing raw SQL like we usually do, but it is done through the ORM (Object Relational Mapping) library, for our case, it’s Sequelize ORM.

Database

The DBMS (Database Management System) we use is PostgreSQL. PostgreSQL is a powerful, open source object-relational database system. As mentioned before, we use Sequelize ORM as an intermediary for doing interaction with the DBMS. ORM is an abstraction of the database, so we no longer think of the database as.. database, but as objects like in Object-Oriented Programming. Sequelize is also used to facilitate migration since it has one.

The model

Above is an example of a model written in Sequelize. We can write and save any fields contained in the model, and their relationships with other models. With this, we don’t need to query the database to see the inside of the table we’re using. In addition, Sequelize also simplifies the query by providing some built-in functions as seen in the previous code, namely findOne. The function will take one object in the model (table) with the given condition as an argument.

Docker

Docker is the most popular container platform today. Docker makes it easy for us to deploy our application to the servers because every application that runs through Docker will guarantee the same environment. The workflow is pretty simple. All we need to do is:

  1. Create and design a Dockerfile for each service.
  2. Build the image with a tag.
  3. Push the image to a privately hosted docker registry.
  4. Deploy the image we just pushed.

The three main components as explained before are packaged and served with Docker. Front-end and back-end services as one service, and the database as another. The build from front-end and back-end services will then be used when building our docker image.

A docker container runs a mini version of an OS. It called an Image. A docker image created by building the image according to defined implementation in a Dockerfile. An example of Dockerfile can be seen above.

In our project, we didn’t use any container orchestration tool like Kubernetes or Docker Swarm. Instead, we use Docker Compose. Docker Compose is a simple orchestration tool, where we create a configuration file (yml or json) containing arguments that usually attached to the docker run command. To run multiple docker service, we can type:

docker-compose up

For our project, the following configuration file looks like this:

As we can see, our docker-compose file consists of two services, database and another service that combined front-end and back-end.

The database service is running a Postgres image from the docker registry, and the other service (back-end and front-end) is running an image that we build earlier. By creating this compose file, our back-end service will have a dependency on database service, so if the database hasn’t started yet, back-end service will not run. We defined links to connect both services in the code.

Some of you may have questions about this architecture style, why does it looks like this or why we prefer this style of choice?

The main certain reason for this architecture style is because it’s our client’s requirement to use React.js, Node.js, and Docker. This also follows our needs to have a separation concern between front-end and back-end. The front-end takes care of UI and the back-end manages interaction with the database.

Why React.js? Aside from being our client’s requirement, it’s obvious that once we use React.js, we can separate the back-end because of the fact that React.js is a single page application. Now, back-end can mind their business to be a REST API server.

Next question, why we choose to use Docker? From the explanation in the Docker section above, it’s stated that every application that runs through Docker will guarantee the same environment. So, this thing really eased the deployment process of our application. What do you mean by “eased the deployment process”? It means when we want to deploy our application, we don’t longer need to set up things on a staging server. The way it’s deployed is already in the docker file, so we just need to simply run it and have our application deployed.

When building our front-end and back-end, we separate both processes so they can act modular which concerns on their own responsibility. This build process can be found on our application package.json. Furthermore, on production, we run the docker-compose up command with a configuration that runs two services which are the database and another service that combined front-end and back-end.

The container for front-end and back-end rolled into one so it would act as one service with different paths (Front-end via port 3000 and back-end through port 5000). Why one service? Honestly, it was a decision made by my teammate, Firman, and he said that the front-end must be served statically with the back-end.

That’s all I have to say regarding the Document Builder’s software architecture.

Thank you for reading!❤️ I always appreciate your claps and feedbacks.

--

--

Roshani Ayu Pranasti
pepeel
Editor for

Computer Science Student @ University of Indonesia