Data

Driven

Front-End

// Whoami

Alex Albu

Software Engineer @FlowX.ai

Trainer @JSLeague

// Why

// Inspiration

Back-end architecture

Micro-service architecture

Layered Architecture

// Inspiration

Software Design Techniques

reactive programming

low coupling

high cohesion

// Solution

The architecture is composed of layers each communicating only with its neighbours.

We are not going to care about stack, framework, libraries or specific implementation details.

Changes should only affect the layers above (children).

Data Handling

Responsibility to handle data from outside sources, transforming it and making it accessible to our app

// Solution

Business Logic

It holds the actual logic of how things work.

Rules, constraints, logical thinking goes into here.

// Solution

UI Components

These are reusable, generic, abstract, bits of UI that we can leverage to fast prototype the interface.

// Solution

Views

User interaction gets handled here but without the logic. Just handling events, triggers, rendering, ui actions. All logic gets delegated to the business logic.

// Solution

// Data Driven Front-end

// Data Driven Front-end

// Data Driven Front-end

// Data Driven Front-end

// Demo

Movies App

// Demo -- Data Sources

// Demo -- Models

// Demo -- Models

// rating.enum.ts
export enum Rating {
  G,
  PG,
  PG13,
  R,
  M,
}
// genre.enum.ts
export enum Genre {
  Comedy,
  Romance
}
// movie.entity.ts
import { Genre } from "@movieApp/entities";
import { Rating } from "@movieApp/entities";

export type Movie = {
  title: string;
  year: number;
  plot: string;
  posterUrl: string;
  genre: Genre[];
  rating: Rating;
};
// someplace.ts
import { Movie } from "@movieApp/entities";

// Demo -- APIs

// Demo -- APIs

// movies.services.ts
export enum APIService {
  OMDb = "https://omdb.example.com/api",
  IMDB = "https://imdb.example.com/api",
  Netflix = "https://netflix.example.com/api",
  Disney = "https://disney.example.com/api"
}
// movies.api.ts
import axios from "axios";

import { APIService } from "@movieApp/api/services";
import { Movie } from "@movieApp/entities";

type MovieDetailsFilters = {/* data model */};

type MovieDetailsRequest = {/* data model */};
type MovieDetailsResponse = {/* data model */};

export async function getMovieDetails(
  movieId: Pick<Movie, "id">,
  filters?: MovieDetailsFilters
): Promise<Movie> {
  const omdbFilters: MovieDetailsRequest = filters
    .map(/* map filters data to omdb model */);

  return axios
    .get(`${APIService.OMDb}/movies/${movieId}`, omdbFilters)
    .then((data: MovieDetailsResponse) => {
      /* map data here to movie model */
    });
}
// someplace.ts
import * as MoviesAPI from "@movieApp/api/movies";

MoviesAPI.getMovieDetails(/* movieId */)

// Demo -- Queries

// Demo -- Queries

// movies.queries.ts
import Store from "@moviesApp/queries/store";

import * as MoviesAPI from "@movieApp/api/movies";
import * as UsersAPI from "@movieApp/api/users";

import { Movie, StreamPlatform } from "@movieApp/entities";

type MovieSummary = Pick<Movie, "id" | "title" | "posterUrl" | "plot">;
type MovieDetails = Movie;

type MovieId = Pick<Movie, "id">;

// Demo -- Queries

// movies.queries.ts
export async function getSummary(movieId: MovieId): Promise<MovieSummary> {
  if (!Store.movies[movieId]) {
    const movieData = await MoviesAPI.getSummary(movieId);

    Store.movies[movieId] = movieData;
  }

  return Store.movies[movieId];
}

export async function getDetails(movieId: MovieId): Promise<MovieDetails> {
  return MoviesAPI.getDetails(movieId);
}

export async function getNetflixStream(movieId: MovieId): Promise<string> {
  return MoviesAPI.getStream(movieId, StreamPlatform.Netflix);
}

// Demo -- Queries

// movies.queries.ts
export async function setLike(movieId: MovieId): Promise<boolean> {
  return UsersAPI.likeMovie(movieId)
    .then(() => {
      Store.user["likes"].push(movieId);
    })
    .then(() => true)
    .catch(() => false);
}

export async function setRating(movieId: MovieId): Promise<boolean> {
  return UsersAPI.setRating(movieId)
    .then((data) => {
      Store.user["rantings"][movieId] = data;
    })
    .then(() => true)
    .catch(() => false);
}

// Demo -- Big Picture

// Conclusions -- PROS

When dealing with codebase changes, it will reduce complexity and make it easier to test and catch bugs early.

It makes it easier to transition between libraries, frameworks or sharing between teams.

It allows to adapt with observables easily and move from a passive programming style to a more reactive one

// Conclusions -- CONS

Codebase ends up being larger and having more dependencies (each layer becomes a dependency)

Requires more engineering discipline and guidelines to keep consistency

Raises cognitive complexity

// Conclusions

Always take everything with a grain of salt, there is not silver bullet or magical wand that is going to make things perfect. Change is constant.

Thank you!

Data Driven Frontend

By Alex Albu

Data Driven Frontend

  • 195