Skip to content

Modular system

Dmitry Usik edited this page Mar 28, 2021 · 4 revisions

Modules

Modules contain application code split by features. For example, all countries' related code is placed in the countries module. Every module must have exactly one declaration file - index.ts. This file lists all module exports that are available for use in other modules. Modules can also declare their types (types.ts) and configuration options (config.ts).

The rest of the module's code is split into sub-modules. Available sub-modules are: __mocks__, components, hooks, screens and services. A module must contain at least one sub-module and can contain all sub-modules.

For example, consider the countries module. This module contains all allowed submodules and it's structured in the following way:

./countries
----| __mocks__
----| components
----| hooks
----| screens
----| services
----| config.ts
----| index.ts
----| types.ts

Module configuration

File config.ts contains constants and configuration settings of the module. It can be used throughout the module itself and is also exported to use in other modules.

config.ts example:

export const SOME_VALUE = 10;

Module export declaration

File index.ts defines all exports available in other modules.

index.ts example:

export * from './components';
export * from './hooks';
export * from './services';
export * from './screens';
export * from './types';
export * from './config';

Module types

File types.ts contains all the types related to the module. It can import types from other modules and it also exports types to be used throughout the module itself and in other modules.

types.ts example:

export type SomeType = 'value1' | 'value2';

Components sub-module

This is the place for react-native components related to this module. Every component is placed in a separate directory. The directory name is capitalized, camel-cased name of the component. All the assets, styles, and tests related to the component are placed in the same directory. The component code itself is in the index.tsx file of the given component and it's exported by default. Apart from the "main" component, the component directory can also contain helper components. These are not meant to be exported from the module, but they can be used to split larger components into smaller parts.

components sub-module example:

./components
----| SomeComponent
----| AnotherComponent

SomeComponent component files structure with InternalComponent helper component:

./components
----| SomeComponent
----|----| __tests__
----|----| InternalComponent.tsx
----|----| image.png
----|----| index.tsx
----|----| styles.ts

Hooks sub-module

Contains all module-related custom hooks. Every hook is placed into its own file and the filename must start with use. For example useSomeInformation.ts. Hooks are exported from their files as default exports. Apart from hooks, this submodule also contains an index.ts file that exports available hooks.

hooks sub-module example:

./hooks
----| __tests__
----| index.ts
----| useSomeInformation.ts
----| useAnotherInformation.ts

index.ts for this example would look like this:

import { default as useSomeInformation } from './useSomeInformation';
import { default as useAnotherInformation } from './useAnotherInformation';

export { useSomeInformation, useAnotherInformation };

Screens sub-module

Contains all navigation screens of the module. Every screen is placed into its own folder. The folder name is capitalized, camel-cased name of the screen. This folder also contains tests and styles for the given screen.

screen sub-module structure example:

./screens
----| SomeScreen
----|----| __tests__
----|----| index.tsx
----|----| styles.ts
----| AnotherScreen
----|----| index.tsx
----| index.ts

This screens would be exported from sub-module's index.ts file in a following way:

import { default as SomeScreen } from './SomeScreen';
import { default as AnotherScreen } from './AnotherScreen';

export { SomeScreen, AnotherScreen }

Services sub-module

Contains various module-related services: APIs, parsers, utilities, initializers, and so on.

Every service is placed into a separate file. The filename of the service consists of the lowercased module name and service type. Eg.: someApi.ts, someParsers.ts etc.

services sub-module example:

./services
----| __mocks__
----| __tests__
----| someApi.ts
----| someParsers.ts
----| index.ts

The exports from sub-modules index.ts:

import * as someApi from './someApi';
import * as someParsers from './someParsers';

export { someApi, someParsers };

Imports / exports

There are few simple rules for importing code depending on where does the import comes from.

The simplest case is an import of 3rd part code from a node_modules package:

import React from 'react';
import { View } from 'react-native';

Another possibility is the import of code from the same module. In this case we use relative path and we import the whole needed sub-module:

import { someApi } from '../../servies';
import { useSomeInformation } from '../../hooks';

The next option is importing code from the same module and same sub-module (eg. from one module's component into another). In this case, we use just plain relative import:

import SomeComponent from '../SomeComponent';
import AnotherComponent from '../AnotherComponent';

And last option is the import of code from one module to another. These imports can only import parts declared in the index.ts file of the module that we want to import and they use an alias:

import { SomeComponent, useSomeInformation, someApi } from '~modules/someModule`;