npm.io
1.0.2 • Published 5h ago

@fractal-layered-architecture/eslint-config

Licence
MIT
Version
1.0.2
Deps
0
Size
25 kB
Vulns
0
Weekly
0
Stars
1

FLA: Fractal Layered Architecture

Overview

This architecture is a methodology for designing application structure. A fractal (self-similar) layer structure is designed so the same layer pattern repeats at every project scale, helping you stay flexible and maintain a predictable, stable project as business requirements change.

Goals

FLA (Fractal Layered Architecture) limits responsibility and scope through a unidirectional layer structure and clearly defined module roles in each layer. That helps you find and respond to the layer where a problem originated quickly, and limits blast radius. Nested layers also let you move or extend modules without changing the existing structure.

Layers

Layers allow only unidirectional references from upper to lower. An upper layer may reference lower layers and their child layers. Each layer limits responsibility and scope by role. These rules make dependencies easier to trace and limit analysis impact when something breaks. An upper layer may include lower-layer implementation; extract it gradually as code complexity or reuse frequency increases.

Nested layers

A layer may nest the same structure inside itself. Modules in a nested layer are tightly coupled and cohesive, and they move or are removed together with their parent layer module. This lets you preserve and extend behavior without restructuring modules.

  • When layers are nested, other layers must not reference a lower layer directly. When needed, create a Barrel (index.ts), export explicitly, and reference only that Barrel from other layers.

    • // Bad
      └── _pages
          └── main
              ├── components
                └── button.tsx
              ├── main.tsx
              └── main.type.ts
          └── account
              ├── account.tsx // (x) import Button from "../main/button.tsx" - direct import not allowed
              └── account.type.ts
      // Good
      └── _pages
          └── main
              ├── index.ts
              ├── components
                └── button.tsx
              ├── main.tsx
              └── main.type.ts
          └── account
              ├── account.tsx // (o) import { Button } from "../main" - import via Barrel
              └── account.type.ts

Layer roles

FLA proposes the following layer roles. (These are examples only; you may extend or change them to match your project.)

Overly granular layers can increase rule complexity and ambiguity. However, you may define new layers with your team as long as the unidirectional layer rule is strictly followed.

Frontend
flowchart LR
  Pages --> Containers --> States --> Components --> APIs --> Utils
  Pages -.-> States & Components & APIs & Utils
  Containers -.-> Components & APIs & Utils
  States -.-> APIs & Utils
  Components -.-> Utils
Pages → Containers → States → Components → APIs → Utils
  • Pages

    • The page layer handles interaction with the browser. This may include browser host object access and page navigation. If a lower layer needs a browser event, delegate it to the page layer.

    • Finish business logic in the page layer first to avoid premature abstraction, then extract modules into lower layers as needed.

  • Containers

    • The container layer composes the screen from components and manages data state. It can load data when needed and decide screen flow based on processing state.
  • States

    • The state layer is built with global state libraries such as React Query or Zustand. It may include business logic for data requests and responses.
  • Components

    • The component layer consists of pure components that do not reference external state. It uses only injected props or internal state.
  • APIs

    • The API layer handles external communication. This includes server communication as well as browser external interfaces such as local storage and cookies.
  • Utils

    • The utils layer is a place for items outside the predefined layers. Domain-agnostic pure functions such as date formatting and regex checks usually live here.
Backend
flowchart LR
  Routes --> Middlewares --> Services --> Models --> Utils
  Routes -.-> Services & Models & Utils
  Middlewares -.-> Models & Utils
  Services -.-> Utils
Routes → Middlewares → Services → Models → Utils
  • Routes

    • The route layer handles HTTP requests and responses. It validates request parameters and calls middleware and service layers to process user requests. Finish business logic in the route layer first to avoid premature abstraction, then extract modules into lower layers as needed.
  • Middlewares

    • The middleware layer handles shared logic that repeats across routes. This may include common authentication such as JWT validation, and processing before or after service calls.
  • Services

    • The service layer holds business logic for processing user requests. Authentication, authorization checks, and domain rules belong here.
  • Models

    • The model layer handles communication with external systems. It includes external interfaces such as databases, caches, and external APIs, and also performs data transformation.
  • Utils

    • The utils layer is a place for items outside the predefined layers. Domain-agnostic pure functions such as date formatting and regex checks usually live here.

File and directory conventions

  • Use kebab-case (lowercase English and hyphens -) for file and directory names. This avoids case-sensitivity issues on some systems and keeps naming consistent.

  • Use an underscore (_) prefix for layer directories to distinguish them from ordinary modules.

  • Split functionality out of layer modules into separate files to clarify roles and reduce complexity. Use suffixes to distinguish these files.

    • e.g., *.stories.ts, *.schema.ts, *.type.ts, ...
Grouping modules

Each module can be grouped as a directory or without a directory. Use a consistent approach within the project or layer.

When grouping by directory, the directory name is the module name and related files live inside it.

└── _pages
    └── main
        ├── main.tsx
        └── main.type.ts
    └── account
        ├── account.tsx
        └── account.type.ts
File modules

You can distinguish modules by file without a directory. However, if a module contains nested layers, it must be grouped as a directory.

└── _pages
    ├── main.tsx
    ├── main.type.ts
    ├── account.tsx
    └── account.type.ts

Coding principles

Detailed rules for writing code within each layer follow the project conventions.

  • Implement as much as possible in the upper module, then extract into lower layers gradually when logic repeats or complexity grows. This helps avoid premature abstraction and code fragmentation.

  • Prefer nested layers first. When reuse is needed across multiple layers, consider moving the module to a lower layer (minimize root layer pollution).

Lint

FLA enforces layer dependency rules with eslint-plugin-boundaries. The suggested FE/BE rules are included in this package.

Install
npm install -D @fractal-layered-architecture/eslint-config eslint eslint-plugin-boundaries
Add to ESLint

eslint.config.js (ESLint 9 flat config) example:

import { defineConfig } from "eslint/config";
import flaFe from "@fractal-layered-architecture/eslint-config/fe";
import flaBe from "@fractal-layered-architecture/eslint-config/be";

export default defineConfig([flaFe, flaBe]);

Keywords