Web Application - Backend Implementation

Requirements

The backend implementation of our application was a crucial component in ensuring the smooth functioning of the system. It needed to be robust and efficient. In addition, it needed to be able to push real-time to the frontend and process them accurately. We decided to use the Spring Boot framework and Google Cloud SQL PostgreSQL database to implement the backend, along with Google Cloud Run for deployment. In this article, we will discuss our approach to designing and implementing the backend, as well as the tools and technologies we used to ensure its functionality.

Must have:

  • Receive pings from porters' phones and push location updates to frontend
  • Inform and assign porters on delivery request
  • Store and query porters
  • Store and query beacons
  • Store and query locations

Should have:

  • Receive and interpret porter responses
  • User Authentication

Could have:

  • Ability to assign deliveries according to priority
  • See the live locations of all crash calls/integrate into Crash 2222 calls.
  • Ability to track individual high priority assets

Initial Approach

When we began planning the backend implementation of our application, we considered several options before ultimately deciding on using the Spring Boot framework with Google Cloud SQL PostgreSQL for our database and deploying our app with Google Cloud Run. Some team members had previous experience with Spring Boot and recommended it as a solid choice for our project. Additionally, our client recommended using the Google Cloud stack, which we found to be a reliable and efficient option for our needs. By choosing these technologies, we were able to develop a robust and scalable backend system that could handle real-time updates from the frontend and process them accurately.

Main Tools & Dependencies

Code Structure

To ensure a well-structured and organized codebase for our backend implementation, we decided to utilize the common Spring Boot code structure. This approach allowed us to create a clean, modular codebase that was easy to navigate and maintain. We followed the standard Spring Boot package structure, separating our code into packages for controllers, services, models, and repositories. This helped us to maintain a separation of concerns and ensure that each part of the application had a clear and distinct purpose. By utilizing this common code structure, we were able to develop a robust and reliable backend that was easy to maintain and scale as needed.

Twilio integration

As part of our backend implementation, we integrated Twilio's messaging API to send and receive messages to and from the porters. We chose Twilio because it provides a reliable and easy-to-use API for sending and receiving SMS messages, making it ideal for our needs. To integrate Twilio into our backend, we utilized the Twilio Java library and set up our Twilio account with an API key and authentication token. We also added configuration options in our application.yml file that allowed us to easily modify the account SID and auth token as needed. This integration allowed us to provide real-time messaging capabilities to the porters, enabling them to receive important updates and information about their tasks directly through the application. In addition to sending and receiving messages from the porters, we also utilized Twilio for authentication purposes by sending one-time PINs to the users. This ensured that only authorized personnel had access to the system.

Firebase Messaging

We also integrated Firebase Cloud Messaging (FCM) to send push notifications to the users' mobile devices. This allows users to receive real-time updates about their deliveries and other relevant information. The integration was seamless, as we were already using Firebase for hosting, and it allowed us to easily add this important feature to our application. The functionality is configured in FirebaseMessagingConfig.java and the Firebase service account is defined in the firebase-service-account.json file.

We send push notification whenever we are requesting a delivery. The logic can be found in DeliveryService.java

We identify user's mobile phone by the FCM token which is sent to us and then associated with the porter on POST "/porter/{id}/token/{token}" endpoint whenever user logs in.

Beacon Updates

We receive beacon pings on POST "/porterPings" from the mobile app. We then map the beacon MAC addresses to their respective locations on the floor plan. Once we have the location information, we update the porter's location in the database based on the nearest beacon.

Live Updates

We utilised JPA's EntityListeners to push live updates on any porter updates. We implemented a class named PorterEntityListener that listens for any changes in the Porter entity. Whenever there is a change in the Porter entity, it automatically sends a real-time update to the frontend via websockets, which updates the porter's location on the floor plan in real-time.

Database Integration

Our backend implementation utilizes Cloud SQL PostgreSQL for our database. Cloud SQL PostgreSQL is a fully managed relational database service that provides high performance, scalability, and availability. We chose this service because it is compatible with Spring Boot and provides automatic failover and data backup, which is critical for our application's reliability. With Cloud SQL, we were able to set up our database quickly and easily, and we didn't have to worry about infrastructure management. We have local configuration details in the application.yml file, while production configuration can be found in application-prod.yml. You can configure the database by modifying the datasource username, password, and instance-connection-name.

Domain Objects

We implemented CRUD operations with REST API for our porter, location, delivery, and beacon domain objects. This allows us to create, read, update, and delete records in our database through HTTP requests. For example, to create a new porter, a POST request can be sent to the "/porters" endpoint with the necessary information in the request body. To retrieve all porters, a GET request can be sent to the same endpoint. We implemented similar endpoints for other domain objects as well. By using REST API, we make it easier for other systems to integrate with our system and interact with our data.

The object definitions can be found in domain folder, endpoint listeners in controllers folder and logic in services folder, and database interactions in repositories folder.

Authentication

For authentication, the user sends a login request to the backend, which triggers the server to send a one-time pin (OTP) via SMS to the user's registered phone number. The user then inputs the OTP in the mobile app, which the app sends back to the server to verify if it is correct. If the OTP is valid, the user is authenticated and granted access to the relevant features of the application. This adds an extra layer of security to ensure that only authorized users can access the system.

Deployment

We chose Cloud Run for our backend implementation as it provides a serverless environment and auto-scales based on traffic, reducing the need for infrastructure management. Cloud Run is a fully managed compute platform by Google Cloud that allows us to run containerized applications securely and at scale. The configuration for deploying our application to Cloud Run can be found in the cloudbuild.yaml file. This file specifies the build steps and the Docker image that will be created and deployed to Cloud Run. The deployment process is fully automated and triggered on every code commit, ensuring that the latest changes are always deployed.

Final Implementation

We were able to successfully implement all the must-have and should-have features in our project's backend. However, due to time constraints, we were not able to complete the could-have features, which included the ability to assign deliveries according to priority, track individual high-priority assets, and integrate with Crash 2222 calls to display live locations of all crash calls. These features could be implemented in the future as per the client's requirements.