Beliebte Suchanfragen
|
//

Feature-Sliced Design and what we need for good frontend architecture

23.1.2025 | 10 minutes of reading time

Feature-Sliced Design and what we need for good frontend architecture

While a lot has been published on the topic of software architecture in the backend, and there are well-established best practices, this topic is less prominent for frontend applications and is often neglected.

Given the increased complexity of frontend applications due to higher demands on UX and UI in recent years, a solid application architecture is essential if the development is to remain enjoyable in the coming years.

The Feature-Sliced Design methodology can be a solution for structuring complex frontend applications with a clean software architecture.

What is good software architecture in the frontend?

The days when a frontend was merely there to format data and display it in the client are long gone. A modern frontend must handle complex state changes, user interactions, and various interfaces. Often, multiple developers work on the same code, and changes need to be implemented promptly.

With a "grown" segmentation of the application into purpose-bound groups like Services, Shared, UI, Api, etc., and unordered dependencies between them, one quickly fails to meet the requirements of a clean architecture. One risks chaos, new team members struggle to familiarize themselves with the source code, and every change becomes a risk.

For a "clean application architecture" in terms of application structure, the following quality criteria should apply:

  • clarity and understandability: Software architecture should be clear and easy to understand. Developers and stakeholders should be able to easily understand and navigate it.
  • scalability: An application should be able to grow and expand over time to meet new requirements and features.
  • flexibility: Adjustments to new requirements should be possible with minimal effort, without the need to redesign the entire system.
  • maintainability: An application should be designed so that it can be easily maintained and updated. Errors can be quickly located and fixed.
  • consistency: The structure of an application should be consistent, i.e., it should use uniform patterns and practices.

Or as Douglas Crockford put it:

“Good architecture is necessary to give programs enough structure to be able to grow large without collapsing into a puddle of confusion.”

To achieve this, a framework of rules and guardrails helps us in extending an application.

One answer to this is the Feature-Sliced Design methodology.

How does Feature-Sliced Design (FSD) work?

Basically, FSD is just a structural guideline for applications with a set of rules on how elements may depend on each other.

A central part of this is the division of the application into 3 levels: layers, slices, and segments.

Feature-Sliced Design Layers, slices and Segments

The subdivision follows a hierarchy from left to right. That is, a layer is divided into slices, and a slice is further divided into segments.

App and Shared are exceptions in that they do not have slices but only segments under them.

An example folder structure in an application could look like this:

Feature-Sliced Design Folders and Slices

While the choice of slices reflects the business logic of the application and does not prescribe any naming conventions, there are certain guidelines for the selection of layers and segments.

Layers

The layers mentioned in the graphic, app, pages, widgets, features, entities, and shared, are prescribed by FSD but do not all have to be used.

In addition to naming, FSD prescribes another important rule: Each layer may only know about layers below it in the hierarchy. That is, pages may only use widgets, entities, and shared. Shared, on the other hand, may not have dependencies on other components, not even within its own layer.

This guideline makes further sense when one considers the purpose of the different layers:

App

Is responsible for the runtime environment or bootstrapping of the application. Configurations, style definitions, and state management at a global level belong here. In addition, it makes sense to locate routing here. Typically, there is no further subdivision into slices here, as no business logic should be located here.

Pages

Below pages, complete views, e.g., linked by top-level routing, should be created in dedicated slices.

Typically, the slices are divided into segments, as many page-specific components, services, and configurations need to be organized here.

In principle, everything that is not reused elsewhere is well placed within a page slice and thus closely tied to its purpose.

In the e-commerce application outlined below, these would be pages such as shopping cart, checkout, product overview, and product detail page.

Widgets

Self-contained, larger sections on a page can be created as widgets.

If a UI block is not page-specific, i.e., generic and reusable, it makes sense to create it as a widget.

Since widgets have their own business logic, specific configurations, and services, and can consist of multiple components, it makes sense to divide them into slices, which are in turn subdivided into segments.

Header and footer would be typical examples of a widget, as well as sections with similar products.

Feature

A feature describes a concrete functionality of the application. Typically, a user interaction that is reusable or could also be used elsewhere. A first look at the feature layer should immediately give an overview of the most important functionality or user value of the application.

Here, too, the subdivision into slices and then further into segments makes sense.

Interaction elements such as a "Buy" or "Favorites" button or the shopping cart preview in the header area make sense as features.

Entities

Entities are all objects that are essential for the business logic.

This is not only about type definitions but also about the associated business logic and backend connectivity. If an entity has a specific representation in the application, it makes sense to locate it here.

In an e-commerce application, these would be, for example, product, shopping cart, or customer.

Shared

The shared layer is not divided into slices, as only very generic components, libraries, and services with high reusability and minimal external dependencies should be located here, and no division into business logic.

Within the segments of shared, there may be dependencies, as in all other layers.

In practice, we find here, for example, formatting helpers and very generic UI components such as dialogs, buttons, or tooltips. As well as reusable abstractions for connecting to interfaces.

Slices

Slices represent the grouping by business context in each layer. There are no guidelines for naming, but the business context should be taken into account, and care should be taken to ensure that the code is easy to navigate. Slices should be loosely coupled and have no dependencies within the same or higher layers. The degree of cohesion, on the other hand, should be high.

Segments

In contrast to slices, segments are not grouped by their business purpose but by their technical one.

There are no strict guidelines on which segments should be used in an application or on their naming. However, as a guideline, a uniform segmentation across all slices of the application makes sense.

FSD suggests the following segments:

FolderDescription
uiUser interfaces, UI components with styles and formatting
apiBackend connections or interfaces to external services
modelData types and schemas, state management
libHelper libraries used by multiple modules
configConfigurations and feature flags

Dependencies with clear direction

As a practical example, I have created an Angular application that represents a rudimentary e-commerce solution:

Feature-Sliced Design Sample project structure

Here we see that all arrows between the layers strictly go from top to bottom. Also, there are no relationships between the slices of a layer.

By clearly regulating the dependencies of all components on each other and allowing them to occur only in one direction, we eliminate the risks of circular dependencies and too tight coupling.

In terms of a "clean application architecture," we are getting closer to our goals:

  • clarity and understandability: The division into layers, slices, and segments is regulated both technically and in terms of business, so the what and how of a component should be clear, and navigation within the structure should be facilitated.
  • scalability: FSD has a clear concept of how applications can be divided and organized into reusable components. Also, the structure is excellent for particularly large and complex applications.
  • flexibility: By decoupling dependencies, components can be changed or removed without unpredictable or hard-to-understand side effects.
  • maintainability: Here, too, the decoupling of dependencies is a decisive factor. In addition, the clearly defined direction of dependencies helps to limit errors.
  • consistency: Since FSD prescribes some rules and naming conventions and applies the same concepts at different levels, we have a uniform application structure.

These effects, of course, presuppose a uniform understanding in the team of the architectural guidelines and FSD, as well as the commitment to adhere to them.

Rules need control

Fortunately, in development, we do not have to rely on all participants knowing the rules and adhering to them. ESLint, which is already used in most frontend projects to control compliance with code styles and standards, can be extended with feature-sliced/eslint-config so that violations of the relationship rules of FSD are flagged.

Going one step further is the library steiger. Here, in addition to controlling dependencies, naming conventions and general FSD-specific best practices are also checked, such as whether a widget is reused, whether slices are allowed in the corresponding layer, or too many of them are used, etc.

To facilitate the creation of folders and to place barrel files in the right place, a practical CLI library can be used. There are also already plugins for IntelliJ and VS Code.

Is Feature-Sliced Design the savior?

Few frontend frameworks and libraries prescribe a strict set of rules on how applications should be organized. And that's a good thing. But it also leads to applications tending to grow wild over time, becoming difficult to maintain, and appearing impenetrable to new team members. Error rates increase, and development time for new features grows with the complexity of the software.

Even if rules and conventions have already been defined for the project, they are often project-specific and must first be internalized during onboarding.

FSD aims to provide a project- and technology-independent methodology that is based on common best practices and has proven itself in practice.

If this is applied across projects, discussions about architectural decisions are saved, and there is a uniform project landscape in which employees can quickly find their way around.

Of course, it's not that simple, and this becomes clear quickly when looking at reference projects according to FSD or gathering practical experience with it.

These projects are usually not as uniform as one would wish. It quickly becomes apparent that the question of whether something is a feature, a widget, or better an entity sometimes allows for some interpretation.

Especially the restriction to extract code into other layers only if it is reused in multiple places and otherwise to keep it in the corresponding slice of the page can lead to components being moved around or no longer being uniformly navigable.

For some guidelines, it makes sense to consciously discuss deviations in the team and document them in writing in the project.

What do we learn from Feature-Sliced Design, and should we use it in our next project?

The approaches of FSD are enormously valuable for every frontend project!

Especially if you haven't yet thought about a basic architecture and don't know how to structure your application.

A clear division into features, i.e., what the code does, and into technology, i.e., how the code does something, makes sense in any case. Keeping the coherence high is also a very sensible approach.

Defining module dependencies through rules helps to develop maintainable and robust applications and is supported out-of-the-box by some frameworks like NX or can be quickly used in any project via a library like eslint-plugin-boundaries.

Whether it has to be a comprehensive methodology like FSD also depends on how large and long-lived the project will be. If you are building a fine-grained micro frontend architecture, a website without more complex business logic, or a short-lived proof-of-concept, the granularity of FSD may be overdimensioned.

In any case, you should think about the architecture in every project, and it certainly helps to orient yourself to proven best practices, such as those defined in FSD.

Further information on FSD can be found on the community page.

|

share post

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.