FE Project Architecture & Design?

Tech

I recently went on parental leave, and while thinking about what to study using the bits of free time in between, I asked an AI to recommend things to study to improve my practical skills at my current level of experience. I received a variety of recommendations, from the latest React paradigms to advanced TypeScript, but among them the item "frontend architecture & design skills" particularly caught my eye.


There are several approaches to frontend architecture, but in this article I want to briefly look at Feature Sliced Design, Clean Architecture, and Atomic Design. My goal is to compare them with the structure of the project I am currently in charge of developing, and to organize what insights can be gained from the perspective of frontend architecture and design. Since these three design methods are already widely used and there are many well-organized articles about them, I will keep things light here and focus on the core points.



Feature Sliced Design

FSD (Feature Sliced Design) is said to be an architectural methodology devised to control the complexity of a project and ensure scalability. The actual design is conceptually similar to domain-driven design or modular architecture, but it is designed to effectively separate complexity with a focus on the characteristics of React components.


The most important core elements are Layer / Slice / Segment, and FSD uses these three to follow a three-level hierarchical structure.


FE-Architecture-1

FSD is an architecture designed with a Layer / Slice / Segment structure that vertically partitions business value so that all dependencies flow in a single direction.


Layer is the top-level folder that contains common UI as well as core business logic, pages, and more. Within Layer there is a top-to-bottom order of app, pages, widgets, features, entities, and shared, and a lower Layer cannot reference a higher Layer.

Slices is a structure separated by business domain within a Layer, and different Slices within the same Layer cannot reference each other.

Segments is a structure separated according to the role of the code within a Slice. Among the layers, app and shared are composed directly of Segments without a Slices structure.


FSD's vertical partitioning structure lowers coupling between code and raises cohesion, helping to minimize the side effects on the entire system even when a specific feature is modified. Thanks to these characteristics, it is more suitable for application development than for a simple library, is advantageous in terms of maintainability, and makes it easy to align a shared understanding of the structure with teammates working on the same project.

However, a certain level of learning about the structure is required, and it should be considered that when each page has strongly separated, independent characteristics, the excessive structure may actually increase complexity.



Clean Architecture

Clean Architecture is a design method proposed by Robert C. Martin to manage system complexity and increase maintainability. The core of the architecture is said to be separation of concerns: designing the system so that it does not become dependent on specific tools by making the main business logic independent from technical environments such as the UI, database, and frameworks.


The main core is divided into four concentric-circle layers: Entities / Use Cases / Interface Adapters / Frameworks & Drivers.


FE-Architecture-2

In the concentric layers of Clean Architecture, the further inward you go, the higher-level the system's policies are—rules that do not change easily—while the further outward you go, the lower-level, more easily changeable rules are located.


Entities consist of the business's core data models and rules, and are the most essential elements of the system, entirely unaffected by external changes.

Use Cases are the elements that define the business-specific rules and user actions that the system must perform. Without knowing any details such as where the data is stored or how it is displayed on screen, they aim solely to execute the business scenario in a self-contained manner.

Interface Adapters serve as the bridge that connects the Use Cases with the external environment. They convert the internal business language into a data format that external systems can understand, or actually implement the interfaces defined internally to make the system's concrete behavior executable.

Frameworks & Drivers are the outermost layer of the system, where technical tools that are unrelated to the core business logic and can be replaced at any time—such as databases and frameworks—are located.


The concentric-layer structure of Clean Architecture is designed so that source-code dependencies always point inward, minimizing side effects caused by external changes. In addition, through the Dependency Inversion Principle (DIP), by referencing abstracted interfaces rather than concrete implementations, it separates the flow of control from the direction of dependency and firmly guarantees the independence of high-level policies.

Clean Architecture also has a certain learning curve, and it must be sufficiently considered that its adoption generates a large amount of boilerplate code, which can increase the structural complexity of the project.



Atomic Design

Atomic Design is said to be a methodology, inspired by the atomic design methodology in chemistry, composed of five interrelated stages for building an interface design system in a more systematic and hierarchical way.


The five related stages are used by progressing through them in the order Atoms / Molecules / Organisms / Templates / Pages.


FE-Architecture-3

Each stage combines the elements of the previous stage to compose the elements of the higher stage, and the lower the stage, the more reusable the elements are, each placed according to its own role.


Atoms are the smallest units of elements that can no longer be broken down, such as a Button or an Input. Individually they can hardly perform complex functions, but they become the core foundation that composes all UI.

Molecules are units that combine Atoms to perform a specific function, such as a search box (Input + Button). They serve as small-scale structures designed for easy reuse.

Organisms are independent interface sections composed by combining Atoms and Molecules. They are the core component units responsible for a specific area of the actual screen.

Templates are the stage where components are placed to establish the layout and skeleton before actual data is injected. They mainly design the positions of Organisms and define the overall structure.

Pages are the completed stage where actual content and data are injected into Templates, and are the screens that the user ultimately encounters.


Atomic Design aims to build higher stages by combining small units of elements based on unidirectional dependency, and ultimately to compose the actual screen. It allows components to be subdivided in a systematic structure, and effectively prevents code duplication by combining highly reusable minimal units. Thanks to these advantages, it can be used as a very useful architecture when building a consistent design system.

However, ambiguity in classifying the middle layers can cause confusion in the process of dividing components, and when the layers are divided strictly, the project structure can become complex or the directory depth can become deeper.

Also, because Atomic Design focuses on the layering of UI components, separate rules are needed from the perspective of domain or business functionality. Therefore, recently it is common to apply Atomic Design to the UI segment (Segments) within an FSD (Feature-Sliced Design) structure, or to separate the main business logic through Clean Architecture and mix in Atomic Design only for the UI area.



What About Our Project?

I would describe the project I am currently in charge of as an architecture designed around Routes. The structure I initially designed aimed to manage business logic in feature units and place UI in components, distributing the business logic and UI.

feature was divided by the classification criteria of the server API, and I established a rule that if common logic or components used across multiple features existed, they would be managed in a common folder called commons that exists at the same level as feature. And under feature, things are managed by dividing them into components / hooks / models / stores / utils.

In components, within a feature, they were divided into Containers / Presentations to distinguish and manage components that contain business logic from those that do not.


At the top level, at the same level as feature, I placed commons / routes / pages / layers / error directories to separately manage common modules, routing definitions, page components, modal components, and error handling respectively.

docker
src/ ├── commons/ # Common modules ├── routes/ # Routing management ├── pages/ # Page components ├── layers/ # Modal components ├── error/ # Error handling ├── ... # Listed by feature unit ├── home/ # home feature ├── setting/ # setting feature ├── components ├── hooks ├── models ├── stores ├── utils

The reason I described the project as an architecture designed around Routes is that, although it started out managed in feature units, because it is a project with distinct per-page characteristics, the server APIs are also divided in a form that clearly indicates which page will call which API, so I think it is more accurate to view it as designed around Routes. In fact, the areas divided by feature exist as page components under pages.



What Would Be Good to Improve?

The first thing that comes to mind is that, because business logic and components are divided around Routes, a lot of logic ends up going into commons (common modules) when it is needed across multiple features. If an API or business logic used in one specific feature comes to be needed elsewhere, it has to be moved to the common modules. I think this results in a picture where the important logic of a single domain or function simply piles up in a warehouse called common modules. And when situations arise where a module within the common modules needs to reference another module at the same level, it seems the structure has become one where circular references are highly likely to occur.

I think it would have been better if we had separated business domains in a form like FSD's Slices, or clearly divided the main business logic as in Clean Architecture.


Second, although I established the rule of dividing components into Containers / Presentations—Containers that contain business logic and Presentations that do not—I found that there are components that do not follow the rule. In the beginning we worked while keeping the rule, but it seems the boundary collapsed one piece at a time, and now the distinction looks largely meaningless.

To solve this problem, I considered whether it would be a good idea to apply Atomic Design in the commons area for highly reusable components, dividing them into Atoms / Molecules, and to divide the components inside a feature into Organisms / Templates / Pages units to compose a single page. That said, after introducing such a structure, how to consistently maintain the rules is a task I think I will need to reconsider later.



FE Project Architecture & Design?

FSD (Feature Sliced Design), Clean Architecture, and Atomic Design can all be seen as sharing the common purpose of clearly managing dependencies through layer separation. They are similar in direction in that they distinguish between core areas that do not change easily or are highly reusable and areas that are relatively more likely to change, and clearly define which direction each layer should depend on.

These design methods do not stop at merely dividing the structure; they treat as important the very process of separating responsibilities by layer and progressively completing the result based on dependency rules. Through this, the purpose is to create a structure that is resilient to change and to secure maintainability and scalability.

In actual development, one often falls into the question, "Which architecture and design is most suitable for this project?" One could simply apply the architectures, design patterns, or other design methods examined above to a project, but I also think that if we adjust and build rules to fit the character of the project while being aware of the common core principles—layer separation, dependency management, and clarification of responsibilities—then in the end our own architecture and design rules best suited to that project will naturally emerge.

FE-Architecture-4

"Design is not just what it looks like and feels like. Design is how it works." - Steve Jobs -