Feature sliced design vs clean architecture Feature sliced design vs clean architecture

Feature sliced design vs clean architecture

Small overview of differences between FSD and Clean architecture

Introduction

This post is an extension of my previous article about clean architecture implementation in React. In this post, I’m going to compare Feature-Sliced Design with Clean Architecture to better understand the topic myself.

Feature-Sliced Design Summary

FSD is a modern architectural methodology for frontend which claims to be more practice-oriented in contrast to “theoretical” (as some say) Clean Architecture or Domain-Driven Design. I personally see FSD as an attempt to combine technical-based layers with domain-based slicing and focus on a front-end part, rather than a whole system.

According to FSD, an app is divided by 6 technical (I see them more as technical) Layers, then each Layer is divided by domain-based Slices, and finally, each Slice is divided by technical-based Segments.

Feature sliced schema

Source: feature-sliced.github.io

Similar to traditional layered architectures, FSD enforces its layer dependency rules.

The trick with layers is that modules on one layer can only know about and import from modules from the layers strictly below.

Slices cannot use other slices on the same layer, and that helps with high cohesion and low coupling.

I think that the trickiest part is to decide in which layer, slice, and segment specific code should belong. Even official FSD examples had multiple iterations with slightly different slicing logic. But it also has benefits by providing such flexibility.

Flow of Development

I think FSD and Clean Architecture have opposite development flows. Clean Architecture is usually explained as business-domain oriented, meaning that development starts with defining domain model entities, followed by application use cases, and ending up at the UI part on the framework layer. In contrast, FSD, being front-end oriented, follows a “pages-first” philosophy, meaning that development starts from application pages and ends up in a shared core. I guess FSD can be seen as a part of Clean Architecture. And the whole system will have 2 development flows (top-down, bottom-up) pointing to each other:

development flow

Layer Isolation

As far as my research goes, Clean Architecture explicitly describes the necessity of dependency injection to adhere to the Dependency Inversion Principle of SOLID. In my Clean Architecture implementation I used composition root to prevent direct dependencies between clean architecture layers.

On the other hand, FSD does not address the Dependency Inversion Principle. FSD does not restrict direct imports (if imported modules are on underlying layers), which in theory might increase coupling.

Implementation

When I started this article, I planned to create a “hello world” example, like I did with Clean Architecture previously. However, I realized that it isn’t worth it for the following reasons:

  • There is a large collection of FSD example projects, which are better than my simple “hello world.”
  • According to FSD’s page-first philosophy, the best way to implement my simple example (explained in my Clean Architecture post) is to place everything in the Pages layer, because my example does not have shared logic and has only a single page. Also, there are a lot of questions regarding the slicing logic of an app, which is not strictly defined by FSD. Thus, I’m just going to briefly explain how I would implement my feature:
└── 📁src
└── 📁app
├── app.ts
└── 📁pages
└── 📁homepage
└── 📁ui
├── Homepage.tsx
└── 📁model
├── Notebook.ts
└── 📁api
├── notebookTauriRepository.ts
└── index.ts
└── vite-env.d.ts
  • app/App.tsx: Entry point to the application. Traditional React’s App.tsx.
  • homepage/ui/Homepage.tsx: Self-explanatory. Here will be the page which should display the notebook’s name. It directly imports methods from the model segment to get the notebook’s name.
  • homepage/model/Notebook.ts: Here might be what I placed in an application layer in FSD: methods for getting notebook’s data, for example, Notebook.getNotebookName().
  • homepage/api/notebookTauriRepository.ts: Is a kind of adapter to the Tauri file reader mechanism. It will read the file, pass the result to Notebook.getNotebookName(), which will extract the notebook’s name and pass it to the homepage.

So, a primitive feature requires only a single layer. Separation will be needed if there is shared code. For example: if an app has 3 pages and every page requires access to notebook data, then notebookTauriRepository.ts might be moved to the widgets layer. If there are multiple widgets which require notebookTauriRepository.ts, then it might be moved to the features layer and so on, up to the shared layer.

Afterword

FSD is quite an unconstrained framework, giving very few strict rules and varying pretty much even in simple examples. FSD tries to adhere to the natural front-end development flow. The main worry I have is that it does not address the Dependency Inversion Principle, which in theory creates tight coupling between layers. If shared UI is changed, then all the imports in upper layers should be fixed, making it look like shotgun surgery.

References

Happy coding! Feedback is appreciated.


← Back to home

Subscription

    New posts will be sent to your inbox