Skip to content

Latest commit

 

History

History
298 lines (222 loc) · 7.98 KB

README.md

File metadata and controls

298 lines (222 loc) · 7.98 KB

Test CodeQL codecov CodeFactor Quality Gate Status Quality Gate Status License

Movies

Movie data aggregator based on the concepts of "Clean Architecture" and "Hexagonal Architecture".

Table of contents

Description

Nest.js hexagonal architecture implementation based on "Get Your Hands Dirty on Clean Architecture" by Tom Hombergs and "Clean architecture" by Robert C. Martin.

The service integrates with multiple external data sources:

  • TMDb movie REST API
  • Wikipedia web scraping
  • Wikidata Query Service
  • NLP question answering pretrained model
  • Persistence layer (PostgreSQL database)

Features

  • Movie browsing
  • List of favourites
  • User rating system
  • Authentication
  • Cross platform application
  • NLP movie plot question answering

Presentation

Overview

overview

Home

home

Popular

popular

Search

search

Question

question

Details

details

Favourite

favourite

Sign up

sign up

Technology Stack

Client

  • TypeScript
  • React Native
  • Expo
  • React Query
  • React Navigation
  • Tailwind
  • Formik
  • Jest
  • Mock Service Worker
  • React Testing Library

Backend

  • TypeScript
  • NestJs
  • PostgreSQL
  • Redis
  • Rxjs
  • Passport
  • fp-ts
  • Jest
  • Mock Service Worker

Architecture

Backend

Backend architecture was designed following the concepts of "Clean Architecture" and hexagonal architecture. The business logic is separated from client access (input port), and it does not rely on the representation of external data (output port). This separation is crucial as multiple external data sources are used in combination.

The project consists of two separated bounded contexts - user and movie. The contexts are entirely independent and can operate as separate services.

Each context is composed of the adapter (input and output port implementation), application (application services and port interfaces) and domain (domain objects) layer.

context
├── adapter (implementation of input/output ports)
├── application (application services and input/output ports interfaces)
└── domain (domain objects)

The use of input adapters separates the domain logic from the communication layer. If one was interested in integrating GraphQL into the application, all he would have to do is provide different input adapters.

Output adapters serve a similar purpose. They separate the details of data access and persistence. Even though movie data originates from multiple sources such as Wikipedia or TMBd, the application logic depends only on a particular interface and not on concrete data provider representation.

adapter
├── input   
|   └──── web 
|          ├── dto
|          └── controllers
|
└── output
      ├── movie-data (movie data provider)
      ├── persistance (Database persistance)
      ├── plot-details (plot details adapter)
      ├── question-answering (question answering adapter)
      └── data-aggregators (persistance and external data adapter)

Application services are separated depending on their responsibility -query and command. "Queries" usually don't include complex business logic and domain objects; therefore, they can entirely skip the application layer. "Commands", on the other hand, require complex constraint validation and domain rules. The responsibility of application services is to follow the business logic embedded into use-cases (input ports) by interacting with domain objects.

application
├── port (input and output ports)
|    ├── in
|    |   ├── command
|    |   └── query
|    |
|    └── out (input ports - specific use cases)
|         ├── command
|         └── query
| 
└── service
       ├── command
       └── query

Domain objects encapsulate business rules specific to the domain. They enhance the code with domain-specific ambiguous language.

domain 
├── model (models used by commands. They embed business logic)
└── read-model (data representation)

Client

Client architecture closely follows modern React trends. React context and React Query is used instead of the global state libraries. Such a design decision reduces clutter and allows the application to be tested with a mock REST API server making the tests more reliable and robust.

src
├── core (communication with backend)
├── pages (views with page specific components)
└── shared (shared functionality)
    ├── components (reusable components)
    ├── context (react context providers and hooks)
    ├── models (data models that encapsulate business logic)
    ├── services (classes that encapsulate business logic and mobile specific api)
    ├── types (TypeScript types)
    └── utils (reusable, pure functions)

REST API specification

Swagger API specification is available at http://localhost:3000/api-docs/. The server has to be up and running in for the documentation to be available.

Installation

Shared

Install (node)[https://nodejs.org/en], (npm)[https://www.npmjs.com] and yarn. You should be able to run the following commands.

node --version
npm --version
yarn --version

Backend

Install docker and docker-compose. You should be able to run the following commands.

docker --version
docker-compose --version

Setup

Run the following commands before proceeding to the sections below.

Shared

docker-compose -f ./docker/docker-compose.test.yml up -d

Frontend

cd frontend
yarn install
yarn run start

Backend

cd backend
yarn install
yarn run start

Tests

In order to manually run tests, follow the instructions below.

Client

cd client
yarn run test

Backend

docker-compose -f ./docker/docker-compose.test.yml up -d
cd backend
yarn run test
yarn run test:e2e

License

This project is licensed under the MIT License - see the LICENSE.md file for details.