Football Data UI¶
As many other people all over the world I like football. Some time ago I found free public API with football results which are provided by football-data.org site and decided to write UI for it.
Site owner provides access token to the API which obviously has some limit for requests frequency. In order to avoid possible bottleneck and token locking I decided to implement full-stack solution with separate database, which will store all the data that were requested previously. Next time these data will be already returned from our own database. In such way we will be able to eliminate potential threshold problem imposed by token.
Development & CI/CD processes for full-stack application could be quite challenging, so maximum automatization and management simplicity was one of the primary goals. But thanks to Docker and it's integration particularly with Bitbucket we can address all these tasks gracefully
App Structure¶
Application is completely Dockerized and consists of 5 nodes:
- Postgres database
- Java web services for user authorization written by Vasyl Halushchynskyi
- NodeJS web services which communicate with football-data.org REST API
- Angular 6 based frontend
- Nginx web server for app entrypoint and orchestration
- Docker platform is used for application bundles preparation
Let's overview each of these parts
Database¶
Application logic operates over several main entities such as Competition, Match, Team. They perfectly align with parent-child relation. Actual data is mostly Matches results, which are small in size statistical data. For such pattern relational Postgres database is an ideal candidate for backend storage.
Authorization¶
User authorization made on Java Spring Boot platform and supports OAuth protocol. Session generation is done on Apache web server. For OAuth tokens handling there was custom logic implemented with Postgres database as storage media.
Web Services¶
Core middleware logic was implemented on NodeJS based Express web framework. Web services implemented shield logic to decrease number of football-data.org API calls. All data returned by the source services were transformed to normalized 3NF structures common for OLTP database applications and stored in local database.
Next time upon receiving request from UI data were checked in local database. If not found, source football-data.org web services were called. Obtained data were again stored in local database and returned to client already as result of read operation from local database. Schematically it can be depicted as "GET local DB -> if not found -> GET footbal-data.org -> upsert into DB -> GET local DB" chain
Such approach also prevents outages that could occur on source API. Football-data.org is project held by a private enthusiast and obviously API availability couldn't compete with giants like Google or Facebook.
Apart from that there is a cron job which refreshes active Competitions and live results. It allows to be in sync with source data and provide almost realtime experience for end users.
Frontend¶
UI is implemented on Angular 6 framework with Angular Material as a main and Bootstrap 4 as helping libraries
List of used Angular Material's modules: - MatSidenavModule - MatTreeModule - MatIconModule - MatButtonModule - MatProgressBarModule - MatSelectModule - MatGridListModule - MatCardModule - MatTableModule - MatSortModule - MatExpansionModule
Homepage¶
Page root element is MatSidenavModule which consist of configurable navigation pane and main content area
When focus is on navigation pane, main content could be optionally grayed out to differentiate more between navigation and main content. This is usefull for smaller screen sizes and mobile devices.
Navigation pane on the left features MatTreeModule module, which supports dynamic data load for individual list entry. Main content area is MatGridListModule. This module allows dynamic change in the number of columns in a grid row for different responsive breakpoints.
For example, to achieve the same result in Bootstrap you can show/hide columns with d-block/d-none classes. And if your purpose was not to hide but to re-arrange content you will have to redraw hided content in another cell.
You could potentially use stacked to horizontal approach, but that switch will work on a single breakpoint. Neighbour mix and match approach can do the trick but still it will not work as expected for odd number of columns per row.
Material grid do it for you automatically with a single cols attribute binding which is much more simpler
<mat-grid-list cols="{{cols}}">
<mat-grid-tile *ngFor="let node of nodes">
...
</mat-grid-tile>
</mat-grid-list>
Competition¶
Competition page features MatExpansionModule which could be grouped into accordion. Scores and Teams are part of an accordion with default setting where only one pane could be expanded at a time. Table pane is independent one.
Each section displays different data via MatTableModule. This module allows dynamic subsription via RxJS Observable interface to data source and features pluggable sorting and paging via MatSortModule and MatPaginatorModule modules. If your data could be logically grouped on some attribute such as competition Round, then instead of using paging module you can apply filtering to table data.
This is how it was implemented for Competition. Pagination is done per Round level and behind the scenes there is a dynamic filtering criteria that is applied.
UI supports display for both regular and cup tournaments such as England Premier League or World Cup.
Matches / Teams / Tables¶
It's possible to check details for competition table/groups standings, list of matches, teams and team players. Breadcrumbs navigation allows quick access to any level from the initial homepage.
If error happens on data provider side it's communicated to end-user. Internal application errors are suppressed for security reason
Other components¶
A set of "smaller" Angular Material components were used for UI elements such as select dropdown box, loading progress bar, button, icons. As a summary it could be stated that Angular Material is a powerfull library which is native for Angular applications. But as for today it couldn't completely substitute Bootstrap and best outcome you can get if you will mix both of them
Nginx proxy¶
Lightweight and easy configurable Nginx web server is used as proxy for all application nodes. It serves Angular application itself and after it became launched serves requests from it to either authorization. Java web services on Tomcat server or to NodeJS web services on Express server. Such configuration allows avoid CORS issues.
Dockerization & Deployment¶
Managing 5 application nodes each in different manner is time and resource consuming practice. Thanks to Docker platform each application node is served in container. Application itself is launched and managed via Docker compose file.
Due to Docker integration with Bitbucket it's easy to setup web hooks which will trigger docker automated build on commit event into the Git repository.
For the demo purposes deployment is done on free Amazon AWS EC2 micro instance. Because of this, slow response and outages are possible.
Footnote¶
Chumaky IT provides full-stack development services including deployment and production support.