Skip to content
Go back

The Module Filesystem Pattern

Module Filesystem Pattern: A Practical Architecture for Web Applications

Over the years, I’ve found myself reorganizing React codebases again and again — trying to balance reusability, clarity, and scale. After working with both Domain Driven Design and Clean Architecture, I landed on a lightweight pattern that’s pragmatic for modern web apps. I call it the Module Filesystem Pattern.

Your web application is an onion

Like an onion, your application has layers. Each layer has a distinct purpose, and when organized well, peeling back one layer reveals another that’s just as coherent and testable. Let’s walk through the outer and inner layers — components, hooks, utilities, and tests — before seeing how they fit together.

Components

React applications are essentially made up of components, hooks, and scripts/utilities. There are no real rules about how you organize them. A simple “getting started” or “hello world” tutorial might guide you through creating your first component, but as you go down the rabbit hole, you’ll quickly want to create sub-components to better organize and reuse code. Using guidance like Component Driven User Interfaces helps to conceptualize how one might break up an app into logical blocks. Further recommendations from Brad Frost’s Atomic Design Methodology provide a framework for how to organize those blocks and build up your components into logical, reusable chunks.

Hooks and Utilities

React introduced hooks in 16.8, and have made the developer experience much better by replacing the old lifecycle methods with a framework for state, context, and reference management that can be pulled out of components into their own files to be better organized and reused. Other reusable code can be placed in “utility” scripts.

Testing

While Jest and Testing Library make it easy to test your components, I’ve found that creating “dumb” components and “smart” utilities and hooks makes creating testing layers easier. This way you can isolate things like data reads from component behavior and even do some mocking of external dependencies to isolate what you’re testing at each level.

The Pattern

Traditional architecture patterns like DDD or Clean Architecture emphasize strict boundaries and inversion of control — great for backend systems, but often overkill for client apps that need agility. The goal here is to capture their spirit — modularity, testability, and separation of concerns — in a filesystem pattern that stays lightweight and intuitive for front-end teams.

src/
└── domains/
    └── todos/
        ├── atoms/
        │   ├── TodoCheckbox.tsx
        │   ├── TodoInput.tsx
        │   └── TodoLabel.tsx
        ├── hooks/
        │   ├── useTodos.ts
        │   ├── useTodoForm.ts
        │   └── useTodoFilters.ts
        ├── molecules/
        │   ├── TodoItem.tsx
        │   ├── TodoForm.tsx
        │   └── TodoSearch.tsx
        ├── organisms/
        │   ├── TodoList.tsx
        │   ├── TodoFilters.tsx
        │   └── TodoStats.tsx
        ├── templates/
        │   ├── TodoPage.tsx
        │   └── TodoDetailPage.tsx
        └── utils/
            ├── todoValidation.ts
            ├── todoSorting.ts
            └── todoApi.ts

Domain Directory

The domains/<domain> folder represents a business domain or feature area in your application. In our example, we’re using “todos”, but this could be “users”, “products”, “authentication”, or any other business concern. Each domain encapsulates all the code related to that specific functionality, making it easy to find, test, and maintain related code.

Atoms

Atomic design atoms are the basic HTML elements with styling overrides. These are the smallest, most fundamental building blocks of your UI - things like buttons, inputs, labels, and icons. They should be highly reusable and have no business logic.

Hooks

React hooks used specifically for this domain, such as useTodos, useUserProfile, or useProductSearch. These hooks encapsulate the state management and side effects related to the domain’s functionality.

Molecules

Combinations of atoms that work together to form more complex UI components. Examples might include a search input with a button, a form field with validation, or a navigation item with an icon and label.

Organisms

Combinations of atoms and molecules that create more complex parts of the user interface. This is also the first level at which business logic typically appears. Examples include a todo list, a user profile card, or a product grid.

Templates

Page-level layouts that define the structure and positioning of organisms. Templates are typically layout-focused and don’t contain much content themselves, but rather define where content should be placed.

Utils

Utility functions and helpers specific to this domain. These might include data transformation functions, validation logic, API helpers, or any other pure functions that support the domain’s functionality.

In closing…

You could call all this bikeshedding — and maybe it is — but I’ve found that a little discipline early on saves a lot of chaos later. Whether it’s a solo side project or a large shared repo, having a repeatable, predictable pattern keeps everyone pedaling in the same direction.

Further reading


Share this post on: