Skip to content

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.

Main UI dashboard

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:

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

Main UI dashboard

Main UI dashboard

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 shadow

Automatic main content shadowing on smaller screen sizes

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.

Grid auto wrapping

Dynamic columns per row setting. Cells re-arranged with order preserving

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.

Competition homepage

Competition view. Each section is clickable to corresponding child view.

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.

Cup tournament

Cup tournament view with groups breakdown.

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

Team details with timeout error

Team details and data provider error notification.

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.

Comments