Skip to content

Latest commit

Β 

History

History
211 lines (173 loc) Β· 7.14 KB

README_CONVENTIONS.md

File metadata and controls

211 lines (173 loc) Β· 7.14 KB

Conventions

Since "consistency is the key" majority of rules is enforced by automated tooling as ESLint, TypeScript, Prettier, etc.
Still certain design and architectural decisions must be followed which are covered with described conventions bellow.

Code Collocation

  • Every application or package in monorepo has project files/folders organized and grouped by feature.
  • Place code as close to where it's relevant as possible.
  • Deep folder nesting does not represent an issue.
  • Relevant article on code collocation.

Project Structure

Every application has following file/folder structure:

apps/
β”œβ”€ product-manager/
β”‚  β”œβ”€ common/
β”‚  β”‚  β”œβ”€ components/
β”‚  β”‚  β”‚  β”œβ”€ ProductTitle/
β”‚  β”‚  β”‚  β”œβ”€ ...
β”‚  β”‚  β”‚  └─ index.tsx
β”‚  β”‚  β”œβ”€ consts/
β”‚  β”‚  β”‚  β”œβ”€ paths.ts
β”‚  β”‚  β”‚  └─ ...
β”‚  β”‚  └─ types/
β”‚  β”œβ”€ modules/
β”‚  β”‚  β”œβ”€ HomePage/
β”‚  β”‚  β”œβ”€ ProductAddPage/
β”‚  β”‚  β”œβ”€ ProductPage/
β”‚  β”‚  β”œβ”€ ProductsPage/
β”‚  β”‚  β”‚  β”œβ”€ api/
β”‚  β”‚  β”‚  β”‚  └─ useGetProducts/
β”‚  β”‚  β”‚  β”œβ”€ components/
β”‚  β”‚  β”‚  β”‚  β”œβ”€ ProductItem/
β”‚  β”‚  β”‚  β”‚  β”œβ”€ ProductsStatistics/
β”‚  β”‚  β”‚  β”‚  └─ ...
β”‚  β”‚  β”‚  β”œβ”€ utils/
β”‚  β”‚  β”‚  β”‚  └─ filterProductsByType/
β”‚  β”‚  β”‚  └─ index.tsx
β”‚  β”‚  β”œβ”€ ...
β”‚  β”‚  └─ index.tsx
β”‚  └─ pages/
β”‚     β”œβ”€ products/
β”‚     β”‚  β”œβ”€ [id].tsx
β”‚     β”‚  β”œβ”€ add.tsx
β”‚     β”‚  β”œβ”€ index.tsx
β”‚     β”œβ”€ _app.tsx
β”‚     β”œβ”€ index.tsx
β”‚     └─ ...
β”œβ”€ warehouse/
β”œβ”€ admin-dashboard/
└─ ...
  • common folder is responsible for implementations that are truly used across application, where it should be used sparingly since codebase tries to follow grouped by feature project structure as much as possible
  • modules folder is responsible for implementation of each individual page (routed from pages folder)
  • pages folder serves as a router, where its only responsibility is to define routes

Data immutability

Majority of the data should be immutable (Readonly, ReadonlyArray). Always return new array, object etc. with the changes, not the original.

Functions

Since React components are also functions, convention should be followed as much as possible.

  • Function should have single responsibility.
  • Function should be stateless where for the same input arguments they return same value every single time.
  • Function should accept at least one argument and return data.
  • Function should not have side effects, but be pure. It's implementation should not modify or access variable value outside its local environment (global state, fetching etc.).

Sometimes potential exceptions are react components and hooks.

Exception examples
const Logo = () => {
  return (
    <svg width="100" height="100">
      <circle cx="50" cy="50" r="40"></circle>
      <text x="50%" y="50%">
        Icon
      </text>
    </svg>
  );
};

const ProductsPage = () => {
  const { data: products } = useFetchProducts();

  return (
    <div>
      {products.map((product) => (
        <ProductItem name={product.name} />
      ))}
    </div>
  );
};

const useGetUsers: UseGeUsers = ({ country, isActive }) =>
  useQuery(['fetchUsers', { country, isActive }], () => fetchUsers({ country, isActive }));

Naming

Strive to keep naming conventions consistent and readable, because another person will maintain the code you have written.
There is no convention on cache invalidation, but for the second hardest thing, bellow conventions should be followed:

  • React components - Pascal case (ProductItem, ProductsPage)
  • Prop Types - component name with "Props" postfix [ComponentName]Props - Pascal case (ProductItemProps, ProductsPageProps)
  • Functions - Camel case (filterProductsByType, useGetProducts)
  • Variables
    • Locals (products, productsFiltered)
    • Booleans are prefixed with is, has (isProduct)
    • Constants (PRODUCT_ID)
    • Enums are singular with values as constants
      enum OrderStatus {
        PENDING,
        FULFILLED,
        ERROR,
      }

React Components

Component Types

  • Container:
    • All container components have postfix "Container" or "Page" [ComponentName]Container|Page
    • Each feature has a container component (AddUserContainer.tsx, EditProductContainer.tsx, ProductsPage.tsx etc.)
    • Includes business logic.
    • API integration.
    • Expected file/folder structure:
    ProductsPage/
    β”œβ”€ api/
    β”‚  └─ useGetProducts/
    β”œβ”€ components/
    β”‚  └─ ProductItem/
    β”œβ”€ utils/
    β”‚  └─ filterProductsByType/
    └─ index.tsx
    
  • UI - Feature specific
    • Representational components that are designed to fulfill feature requirements.
    • Should follow functions conventions as much as possible.
    • No API integration.
    • Expected file/folder structure:
    ProductItem/
    β”œβ”€ index.tsx
    β”œβ”€ styled.tsx
    β”œβ”€ ProductItem.stories.tsx
    └─ ProductItem.test.tsx
    
  • UI - Design system
    • Reusable/generic types of components used throughout whole monorepo.
    • Expected file/folder structure:
    Button/
    β”œβ”€ index.tsx
    β”œβ”€ styled.tsx
    β”œβ”€ Button.stories.tsx
    └─ Button.test.tsx
    

Passing Data

  • Prop drilling should not become an issue, if it does break out your render method and keep in mind that React components are functions, which should have single responsibility.
  • Component composition is not allowed.
  • Global state is not allowed.
  • Fetching of data is only allowed in container components.

Tests

Test can be run through npm scripts, but it's highly encouraged to use Jest Runner VS code extension so any monorepo app/package single test can be run instantly.

code --install-extension firsttris.vscode-jest-runner
  • All test descriptions should follow naming convention as it('should ... when ...').
  • Snapshot tests are not allowed (except if truly needed in design system library).

Conventions enforced by automated tooling

List and reasoning of some conventions enforced by automated tooling:

  • Whole monorepo codebase is written in TypeScript strict mode with enabled ESlint Strict Configuration

  • All types are defined with type alias. In case of rare exceptions (extending third-party types) interface can be used with disabling linter:

    // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
  • Arrays are defined with generic syntax.

    const x: Array<string> = ['a', 'b'];
    const y: ReadonlyArray<string> = ['a', 'b'];
  • Default export is not allowed. In case of exception this rule is disabled in .eslintrc.js (Next.js pages etc.)

  • All test descriptions follows naming convention as it('should ... when ...').