How to Build Large Scale React Apps

Adam L Barrett

"BigAB"

Solutions that require developer discipline or documentation don’t scale

Developers

are

People

To Build Large Scale Applications

👨‍💼 Simple and Familiar Architecture

👷🏽‍♀️ Optimize for Change

👨🏻‍💻 Automation over Discipline

2 Ways to View Your Application

2 Ways to View Your Application

👨‍💼

Front-End
Architecture

The Secret to Building Large Scale Apps…

Persistence

Logic

Interface

Data

Application

Presentation

Model

Controller

View

Storage

UI

Domain

Models

Controllers

Views

Cart Component

ProductList Component

Login Component

Cart Component

ProductList Component

Login Component

Cart Component

ProductList Component

Login Component

Storage

Domain Logic/State

UI

Storage

Domain Logic/State

UI

APIs and Platform

Stores

React Components

Domain
Components

APIs and Platform

Stores

Domain
Components

UI Components

VS

Domain
Components

UI
Components

Domain Components

  • are aware of your domain entities
    • your Products, Carts, Orders, Accounts, etc
  • do NOT render platform UI (e.g. HTML Elements)
  • connect the store state to the UI
  • convert UI Actions/Events into DOMAIN actions/Events

UI
Components

  • are unaware of domain entities
  • may be stateful
  • render platform UI (HTML and styles)
  • emit UI actions/events
    • clicks, selects, input
  • render based on the data provided to them
const Documents = ({ userId }) => {
  const [
    { documents },
    { create, update, destroy }
  ] = useDocuments({ userId });

  return (
    <ItemList
      items={documents}
      onCreate={() => create({ documentData: {} })}
      onUpdate={data => update({ documentData: data })}
      onDelete={document => destroy(document.id)}
    />
  );
};

Domain Component

const ItemList = ({ items, onCreate, onUpdate, onDelete }) => {
  return (
    <>
      {items.map(item => {
        <div key={item.id}>
          <Item
            name={item.name}
            onChange={data => onUpdate(data)}
          />
          <button onClick={() => onDelete(item)}>X</button>
        </div>;
      })}
      <AddButton onClick={() => onCreate()} />
    </>
  );
};

UI Component

UI Components

APIs and Platform

Stores

Domain
Components

Services

The Pieces

UI: UI Components (React Components)

Adapter: Domain components (also React Components)

Domain Logic/State: Stores

Adapter: Services (Class instances, functions or  simple objects)

Storage: APIs and Platform (REST + GraphQL endpoints, etc)

Personal Preferences

UI Components - Function Components, Hooks, Styled Components

Domain components - Custom Hooks / Providers

Stores - RxJS BehaviorSubjects (or Redux + Redux-Observable)

Services - Simple classes that return promises or RxJS observables

APIs and Platform - GraphQL and localForage

UI Component

const AlarmClock = ({ title, state, onSnooze, onDismiss }) => (
  <>
    <h3>{title}</h3>
    <div className="display">{state}</div>
    <button className="snooze" onClick={onSnooze}>
      Snooze
    </button>
    <button className="dismiss" onClick={onDismiss}>
      Dismiss
    </button>
  </>
);

Domain Component

export const CreditCardForm = () => {
  const [{ values, errors, status } = {}, dispatch] = useStore(CreditCardStore);

  return (
    <FormComponent
      values={values}
      errors={errors}
      status={status}
      onSubmit={e => {
        e.preventDefault();
        dispatch(PAY);
      }}
      onChange={e => dispatch(UPDATE, { [e.target.name]: e.target.value })}
      onBlur={e => e.target.name && dispatch(TOUCH, e.target.name)}
    />
  );
};

Store

const createStore = (action$, creditCardService) => {
  const values$ = action$.pipe(ofType(UPDATE), map(creditCardService.validate));

  const status$ = action$.pipe(
    ofType(actions.PAY),
    exhaustMap(({ values }) => {
      from(creditCardService.pay(values)).pipe(
        mapTo({ status: 'success' }),
        catchError(error => of({ message: error.message }))
      );
    })
  );

  return combineLatest(values$, errors$, status$, (values, errors, status) => ({
    values,
    errors,
    status
  }));
};

Services

class creditCardService {
  construtor({ apiUrl }) {
    this.apiUrl = apiUrl;
  }

  async pay(data) {
    const response = await fetch(this.apiUrl, { body: JSON.stringify(data) });
    return response.data;
  }
  
  async validate(ccData) {
    await checkValidNumber(ccData);
    checkIsExpired(ccData);
  }
};

APIs and Platform

Stores

UI Components

Services

Domain
Components

Benefits

👩🏿‍💻

Monorepos & Modern Tools

npx create-nx-workspace myorg
npx create-nx-workspace myorg

npx: installed 180 in 7.764s
? What to create in the new workspace 
  angular           [a workspace with a single Angular application] 
  angular-nest      [a workspace with a full stack application (Angular + Nest)]
 
❯ react             [a workspace with a single React application] 
  react-express     [a workspace with a full stack application (React + Express)
] 
  next.js           [a workspace with a single Next.js application] 
(Move up and down to reveal more choices)

...

cd myorg

Nrwl's Nx

Nrwl's Nx

apps directory

  • holds all your applications
  • also has e2e test apps (Cypress)

Nrwl's Nx

libs directory

  • holds all your shared libraries
  • can be organized into subdirectories
import { libraryModule } from '@myorg/library-name'
npx nx list

Libraries

👩🏽‍🎨

Design Systems

Design Systems

Designer and Developers create a single source of truth and common language for UI components across products and applications

Design Systems

Pro-Tips:

  • This is where your
    UI Components live​
  • Devote a team to it
  • Have a process for change
  • Split visual and behaviour

💡

npx nx g @nrwl/react:library design-system --directory ui

Generate Libraries

npx nx g @nrwl/react:component SuperButton --project ui-design-system

Generate Components

npx nx g @nrwl/react:storybook-configuration my-shared-react-lib
npx nx g @nrwl/react:redux products --project store

Generate Redux Slices

npx nx g @nrwl/workspace:lib ProductService --directory services

Generate Sevices

Domain Components

And wait... there's more!

npx nx dep-graph
nx affected:test
nx affected:build

TypeScript

npx nx e2e my-app-e2e

Nx allows you to

  • Easily create applications and work with them using a consistent set of commands
  • Verify that a code change is safe
  • Extract shared libraries
  • Create code generators to save time and effort
  • Reinforce best practices

Recap

APIs and Platform

Stores

React Components

Services

Domain
Components

How to Build Large Scale React Apps 

nrwl.io
github.com/BigAB/enterprise-react-example

How to Build Large Scale React Apps (JS VidCon)

By Adam L Barrett

How to Build Large Scale React Apps (JS VidCon)

This talk is about the fundamental principles for managing the complexity of large front-end applications at scale. It is not about scaling your deployments (i.e. operations), but rather the trouble of scaling front-end development across multiple teams, with strategies for front-end architecture, code organization, predictability and fighting complexity.

  • 617