In this blog post series, we will explore how to leverage Angular and Firebase tools to quickly and cost-effectively create a relatively small web application. Although the application we will be developing has no business implications, the same methodologies can be used to build real-life business applications in a fraction of the time and cost.
Table of Contents
Application Requirements
TLDR; A web app that renders flags pinned by protests on a Google Maps view.
About a year ago, Yair Dickmann, who used to be my manager at Granot, published a post on his blog where he asked for help with an application that does the following:
- A view with a map where visitors can see all the flags pinned by protests.
- A view with the same map, where visitors can see the flags that were pinned in the last hour (live-map).
- These views should get updated upon new flags being added.
- Additionally to flags on the map, the following numbers should be presented:
- Total flags
- Total live-flags.
- Total unique protests.
The context of Yair’s request were protests against the Ex-Prime Minister threatening Israeli democracy. In those days Israel faced a high incidence of Coronavirus cases, which led to lockdowns and prevented many people from going out to demonstrate.
Although the context of his request might not be relevant anymore (luckily, the majority of voters voted against corruption and the destruction of democracy), I am now looking for a job and thought it could be a fun activity to develop such a little application in the meantime.
Objectives
The final result of this work is available at vdemo.kessler.tech, the code is available on GitHub, in this series of posts, we will go through the following steps:
Let’s start with software architecture.
Software Architecture
The software architecture should cover the following topics:
- Functional requirements – the behavioural parts of our software.
- Non-functional requirements – the quality requirements, such as device compatibility, accessibility, etc.
- Interface definition – the APIs expected from our software.
- Data model definition – a design of the application data and its relations.
- High-level graphic design.
Functional Requirements
The functional requirements were described earlier in the introduction, let’s repeat them in a slightly different way:
- When a visitor loads the app, they should see a map with all flags ever pinned.
- The visitor should be able to navigate between live flags, my flags, and all flags.
- The visitor should be able to choose a flag and pin it on the map.
- The visitor should be able to see the following usage statistics:
- Total flags,
- Total live flags,
- Total unique protests.
Non-Functional Requirements
There are not many non-functional requirements in this app, it’s not commercial software, and it is not intended to reach high traffic, the only things to consider as non-functional requirements are as follows:
- The app should be accessible through the internet.
- The design should be compatible with mobile devices.
- A user should be authenticated, either with email or in any other way, the idea is that we will want to count the number of unique protesters.
- A user can pin one flag within an hour – so we can tell the actual number of live flags.
Interface Definition
Given that we don’t need to do much about rendering the map (we will be using Google Maps API), our software concerns are only users and flags. The following are APIs expected from our software:
Flags API
- postFlag(id, userId, flagLocation, flagOptions, timestamp)
- updateFlag(id, flagLocation, flagOptions, timestamp)
- getAllFlags()
- getLiveFlags()
- getUserFlags(userId)
Users API
The only purpose for authenticating users is to produce a map with users’ flags, and to count the number of unique users, having that in mind, the following should provide us with the Users API interfaces:
- signInAnonymously(id, timestamp)
- signUpWithEmailAndPassword(id, email, password, passwordConfirmation, timestamp)
- signInWithEmailAndPassowrd(id, email, password, timestamp)
- signOut()
- getUserId()
The user interface also contains sign in / up with email and password, the reason we need it is that signInAnonymously can only track flags the user pinned from the current device. If the user wants to pin flags from different devices, and they want to track all of their flags, they must sign up with credentials.
Data Model Definition
- User(id!, email?, password?, createdAt, lastLoggedIn).
- Flag(id!, userId!, location!, icon!, createdAt, lastModified).
Data Relations Diagram
High-level Design
As said earlier, there is no reason to distribute our software into many tiers, since it’s not expected to get high traffic. Additionally, there are only two domains of responsibility, users and flags which can easily be dealt with in a 3 or 2-tier architecture. So before going into the detailed design, let’s assume an architecture of 3 tiers,
- GUI Client
- Mid-level API
- and Database
Our high-level design should look like this:
For the architecture this should be enough, next, we’ll go into lower-level details of our implementation.
Yair Dickmann
Nice, indeed
Moshe Kessler
Thanks for your blog post back then, it was a nice workaround!