Skip to main content

App splitting

Group: Layers

The first level of separation: according to the scope of responsibility of the module

Self-check

"Which application layer does the module belong to?"

└── src/
β”œβ”€β”€ app/ # Initializing application logic
β”œβ”€β”€ processes/ # (Optional) Application processes running over pages
β”œβ”€β”€ pages/ # Application Pages
β”œβ”€β”€ features/ # Crucial functionality of the application
β”œβ”€β”€ entities/ # Business entities
└── shared/ # Reused modules

Layer order

If you look at the order of the layers , you can distinguish two general patterns:

By the level of knowledge/responsibility

app > processes > pages > features > entities > shared

The module "knows" only about itself and the underlying modules, but not the ones lying above

This also affects the allowed imports

By the level of danger of changes

shared > entities > features > pages > processes > app

The lower the module is located , the more dangerous it is to make changes to it

Because most likely it is used in many overlying layers

Group: Slices

The second level of separation is by specific BL functionality

The methodology has almost no effect on this level and much depends on the specific project

Self-check

"What scope of BL does the module affect?"

Before that , it is necessary to determine the scope of responsibility (layer)

β”œβ”€β”€ app/
| # Does not have specific slices,
| # Because it contains meta-logic on the project and its initialization
β”œβ”€β”€ processes/
| # Slices for implementing processes on pages
| β”œβ”€β”€ payment
| β”œβ”€β”€ auth
| β”œβ”€β”€ quick-tour
| └── ...
β”œβ”€β”€ pages/
| # Slices for implementing application pages
| # At the same time, due to the specifics of routing, they can be invested in each other
| β”œβ”€β”€ profile
| β”œβ”€β”€ sign-up
| β”œβ”€β”€ feed
| └── ...
β”œβ”€β”€ features/
| # Slices for implementing specific functionality on pages
| β”œβ”€β”€ auth-by-phone
| β”œβ”€β”€ inline-post
| └── ...
β”œβ”€β”€ entities/
| # Slices of business entities for implementing a more complex BL
| β”œβ”€β”€ viewer
| β”œβ”€β”€ posts
| β”œβ”€β”€ i18n
| └── ...
β”œβ”€β”€ shared/
| # Does not have specific slices
| # is rather a set of commonly used segments, without binding to the BL

Rules

Since a slice is a specific level of abstraction, the methodology is obliged to impose certain rules on it

Low Coupling & High Cohesion

Slices of the same layer cannot use each other directly, and their interaction and composition should be determined on the upper layer, relative to their current one

features/baz/ui.tsx
// Bad: the feature imports another feature (slices of the same layer)
import { Bar } from "features/bar"

function Baz({ foo, ...barProps}) {
...
<Bar {...barProps} />
}
pages/foo/ui.tsx
// Good: features are compiled on the page (overlying layer)
import { Baz } from "features/baz"
import { Bar } from "features/bar"

function Foo() {
...
<Baz {...fooProps}>
<Bar {...barProps} />
</Baz>
}

Grouping

  • In most cases, you should avoid nesting in slices, and use only structural grouping by folders, without additional coupling logic

    features/order/           # Feature group
    β”œβ”€β”€ add-to-cart # Full-fledged feature
    β”œβ”€β”€ total-info # Full-fledged feature
    - β”œβ”€β”€ model.ts # General logic for the group
    - β”œβ”€β”€ hooks.ts # General hooks for the group
    └── index.ts # Public API with feature re-export
  • At the same time, some layers (e.g., pages) initially require nesting due to the requirements of the project / framework

    pages/
    β”œβ”€β”€ order/
    | β”œβ”€β”€ cart/
    | β”œβ”€β”€ checkout/
    | | β”œβ”€β”€ delivery/
    | | └── payment/
    | β”œβ”€β”€ result/
    | └── index.tsx
    β”œβ”€β”€ auth/
    | β”œβ”€β”€ sign-in/
    | └── sign-up/
    β”œβ”€β”€ home/
    β”œβ”€β”€ catalog/
Important

Nested slices should be avoided as much as possible, but even if you have to use them (for example, for pages), you need to link them explicitly, to avoid unforeseen consequences

Group: Segments

The third level of separation: by the purpose of the module in the code and implementation

Self-check

"What part of the technical implementation of the logic affects the module?"

Before that, it is necessary to determine the scope of influence (layer) and domain affiliation (slice)

{layer}/
β”œβ”€β”€ {slice}/
| β”œβ”€β”€ ui/ # UI-logic (components, ui-widgets,...)
| β”œβ”€β”€ model/ # Business logic (store, actions, effects, reducers,...)
| β”œβ”€β”€ lib/ # Infrastructure logic (utils/helpers)
| β”œβ”€β”€ config*/ # Configuration (of the project / slice)
| └── api*/ # Logic of API requests (api instances, requests,...)

At the same time, each segment can be represented as a file, or as a separate directory - depending on the complexity and size

Restrictions

The methodology was developed with the aim of not limiting and not bothering developers with the rules for choosing abstractions (it's desirable to use any segment in any layer)

However, as a result of discussions and analysis of extensive experience - it was determined that it is better and more practical to limit each layer to segments used internally.

General rules

  1. The higher the layer is located , the more it knows about the BL of the application and vice versa
  2. API logic recommended should be put in shared so that the logic is not scattered around the project
  • Usually, it is common and presented as single instances
    • Edge-case "exceptions": GraphQL, react-query hooks

Application for layers

LayerContentAllowed Segments
appDoes not include slices and contains initialization logicThe existing segments are not quite suitable, and therefore /providers (/hoc, ...), /styles, etc. are usually used. It depends very much on the project and is unlikely to be solved by the methodology
processesThe slices inside include only business logic, without displaying (1)ui lib model (api)
pagesThe slices inside include a ui and model composition of various features for a specific pageui lib model (api)
featuresThe slices inside include the composition of entities and the implementation of BL in the model + displayui lib model (api)
entitiesThe slices inside represent a disparate set of submodules for usingui lib model (api)
sharedContains only infrastructure logic without BL (1)ui lib api

See also