The place you'll find the best deals for movies, guaranteed!
- Description
- Context
- Instructions
- Architecture (MVC)
- Assumptions
- Trade-offs
- Opportunities of improvement
This application renders a group of classic movies and compares the prices of two well-known streaming providers, "Filmworld" and "Cinemaworld". It displays movie posters, titles, actors, and the most important thing: prices! The cheapest streaming providers are highlighted in green, and other ones have a strikethrough and are red coloured.
This app was deployed to Heroku:
https://prince-theatre-app.herokuapp.com/
🧰 Programming Languages
🧰 Frameworks/Libraries
Other worth mentioning frameworks/libraries/gems used are:
- RSpec (Ruby testing tool)
- Jest (JavaScript testing framework)
- HTTParty (Remote URLs fetching tool for Ruby)
This application is fully responsive. It adapts to any screen size.
- The API key is safely stored in an environment variable in both development and production modes.
- If you are going to clone this repository, make sure you have a valid API Key.
-
The core customer problem to solve in this project is to allow users to see which of the two streaming providers are streaming a particular movie at a cheaper price.
-
The information about the movies is available upon authentication from an external API. A secret API key is required.
-
React was the chosen JavaScript library for building the user interface (Front-End) as declarative views make code more predictable and easier to debug.
-
Since an API key is required, it's crucial to account for security concerns. That was an important reason it was decided to have a full-stack application and store the API key in the Back-End side through an environment variable.
-
Ruby on Rails (or simply Rails) was the chosen Ruby framework for the Back-End as it can help build structured Model-View-Controller (MVC) architectures. Rails handles the models and controllers for this app, and React takes care of the views as mentioned above.
-
All the communication between the app and the external API is performed by Rails. The models handle the external API's information and prepare the required information to be sent to the Front-End from the controllers.
-
The Back-End takes care of the entire logic, including finding the lowest prices. The job from the Front-End is rendering the information from the Rails API generated by the server.
-
Material UI was the React UI library for layouts and stylings.
-
Clone this repository.
-
Please make sure you are using Ruby 2.7.4; otherwise, go to Gemfile and change the ruby version to the one you currently have (I encourage you to use Ruby 2.7.4 to avoid incompatibility issues). Then please run the following to install Rails and all the dependencies needed for this project:
bundle install
- To install all the React dependencies needed for this project, please run from the root directory:
npm install --prefix client
- Create a .env file in the root directory to store your API key in an environment variable. Name it API_KEY.
API_KEY = your_api_key
- To call the server run:
rails server
- Run the following to display the app in the browser:
npm start --prefix client
Ports from server and client are already set to be different, so there is no need to specify them. Port 3000 is assigned to Rails and Port 4000 to React.
- Most of the relevant unit tests are in Rails since the entire logic is handled from the server
- For Ruby on Rails (server), RSpec was the designated testing tool.
- Test files for ruby and configuration files are located in spec/
To execute the tests from Ruby on Rails, please run from the root directory:
rspec
It will automatically run all the tests where green means a test passed and red means it didn't pass.
- For React (client), Jest was the designated testing tool.
- Test files for React and configuration files are located in app/client/src/
- Tests for specific components are located in the same component's folder
To execute the tests from React, please run from the root directory:
npm test --prefix client
If an error is thrown before the watcher is set up and tests are run, please execute the following:
npm i -D --exact jest-watch-typeahead@0.6.5
- The Rails server handles the logic of the app from the GetMovies (app/models/get_movies.rb) and Movie (app/models/movie.rb) models.
- The movies details are sent to the Client from Api::V1::MoviesController (app/controllers/api/v1/movies_controller.rb) as JSON.
- The Rails API routes are stored in config/routes.rb
- All the files (components and tests) are grouped by features and are located in app/client/src/components/. The components structure is the following:
App.js
|
|-->Header.js
|
|-->Movies.js
|
|-->Movie.js
|
|-->CinemaMoviePrice.js
-
The Movies component can access the Rails API routes and pass props that contain the movies information to its children components.
-
Some stylings, layouts, and animations that are not implemented from Material UI are stored in client/src/index.css and client/src/App.css.
-
The general information such as Movie titles and actors are the same at both endpoints. Therefore, those two attributes are rendered from Filmworld, assuming that the Cinemaworld attributes will be the same.
-
The core problem to solve is to compare prices, so it is assumed that prices might not be the same.
-
Since the API is unreliable, it is assumed that sometimes it will not return the movie's information. However, there is already an implementation that retries fetching the data ten times in case a non-desired status code is returned. It handles errors if the API is still unable to return the movie's information after ten retries.
-
Initially, only one endpoint was needed from the Rails API to retrieve everything required, such as the movie's details, prices by cinema, and display the lowest price. However, the way the API was generated wasn't efficient, so the rendering of the movie cards in production was slow. The trade-off for this situation was excluding the prices from the initial endpoint and adding a second endpoint that retrieves them. Now the rendering time is much more efficient in production, although now two endpoints are required.
-
For the ease of the user experience, pagination was decided as the means to render the movie cards. The current amount of data is low, so client-side pagination would quickly render the data with just one query. However, if the dataset gets much more extensive, switching to server-side pagination for stability and scalability will be necessary.
-
The app is scalable, which means it is possible to add more streaming providers (endpoints), and the app will still find the lowest prices. However, it relies on the assumption mentioned above of sharing the same data as the other streaming providers.
-
The tests from Jest can be more robust: at this point, these can test the essential information that should be rendered to the front-end.
-
The RSpec test to determine the cheapest prices could test all movies in one go: at this point, it can accept multiple endpoints (more than the two already provided), but it can do so as long as a single movie index is given.
-
Add a sort and filtering feature: by adding this feature, the user experience will improve as users won't have to look at every page to find the movie they're looking for.
-
Add a database:
- It could improve the site's performance as it won't have to request data from an external API every time a user loads the page. Periodic database updates from the background might be needed if implemented.
- It would allow users to track how much money they have saved by selecting the cheapest cinemas.
-
Use Redux for state management: thinking about scalability, having Redux for state management could be helpful as it manages states in a single place.
Thank you for your time!