This article aims to provide you with useful insights into the Feature-Sliced Design approach. You will learn about this architecture and its potential benefits, including real code examples from a Todo Application built on Angular. By immersing yourself in this methodology, you will gain a new perspective on architectural design.
Moreover, we will explore how Feature-Sliced Design can effectively and cost-efficiently design business logic. This analysis will showcase the practical applications of this approach and demonstrate its potential value for software development.
Please note that this article will not provide an in-depth theoretical understanding of Feature-Sliced Design. Nevertheless, this architectural methodology has a relatively high level of complexity, and I strongly suggest referring to the official documentation for more comprehensive information.
What is Feature Sliced Design?
Paradigm
2.1 Small puzzles
2.2 Assemble correctly from childhood
2.3 Delete and conquer
Develop FSD on Angular
3.1 Main goal
3.2 Create a project and structure
Recap and Example
Before we get started with the code, let’s determine what FSD is.
“Feature Sliced Design” — architectural design methodology for Front-End applications, a design approach where a product’s features are divided into separate, standalone components, or “slices”. Each slice is then designed and developed independently, allowing for greater flexibility, modularity, and scalability in the design process. This approach is often used in software development and can be applied to the design of web or mobile applications, as well as other types of software products.
Sounds easy, but of course, by dividing everything into small pieces, we will not automatically get this implementation of the pattern.
I would like to highlight a couple of points that are particularly interesting and important.
We figured out that the idea behind FSD is to break down a complex product into smaller, more manageable pieces into layers. Each “Layer” might contain other manageable pieces — “Slices”. “Slices” in turn may have “Segments”.
This can lead to faster development times, better collaboration between teams, and a more flexible and scalable final product. Thus we have a set of puzzles that still need to be able to “assemble”. More details are below.
Every child knows that puzzles can be put together in any way. The picture does not converge, but the puzzles sit tight. Great? Great! The mother is happy, the father is proud, baby understood that everything was correct.
And it’s very similar to many projects: messy imports, and code duplication, the project structure is defined by the location of the folders, and so on. These “Childhood diseases” become garbage dumps. It costs hundreds of man-hours and thousands (billions) of dollars to clean up the dirt. Time is money.
FSD strives to be perfect. It was born with a Mission — to create a Сontrollable Unidirectional Encapsulated Flow (explicit one-way links).
I imagine a stream that turns into an ocean. A small stream knows nothing about the ocean, but the ocean knows about all the streams, rivers, lakes, and seas that feed it.
Let’s say more — one river does not know about the existence of the other. This is the prerogative of the seas and oceans.
Technically, It means that the Bottom Layer (for example, shared) knows nothing about all the layers above or next to it. Upper layers (ex. entities) can use all the layers below, like a river that flows into the sea, and not vice versa.
Let’s remember these Scope Rules. This is a small price for the flexibility and adaptivity that it gives.
My favorite paradigm point. I even made up this screaming headline.
It is your responsibility to design and develop the application so that any block can be easily removed. If you don't need some page/ you just delete it.
You remove it as easily as cutting a piece of cake. A cut-off cake is on the guest’s plate, but your application, like the cake, did not fall into pieces. Just beautiful as before. Just missing one piece, but still such a masterpiece.
This is achieved by Low Coupling & High Cohesion. By writing the code, you will understand how it works. It’s time to hit the keyboard.
I will demonstrate how to build a simple and scalable ToDoList application using the popular Angular framework by including all the crucial layers.
I will also point to the example of well-known Twitter. The To Do app is publicly available on GitHub. So you will have 2 examples.
First, we should create a standard Angular project + generate a structure as below:
└── src/
├── app/ # Initializing application logic
├── processes/ # (Optional) Application processes running over pages
├── pages/ # Application pages
├── widgets/ # (Optional) Independent and self-contained blocks for pages
├── features/ # (Optional) Processing of user scenarios
├── entities/ # (Optional) Business entities that domain logic operates
└── shared/ # Reused modules, non-business specific
My experience is that for a better explanation, we will consider the flow from bottom to top, from shared to app.
Reusable functionality, detached from the specifics of the project/business.(e.g. UIKit, libs, API)
In Feature Sliced Design, “shared” refers to components or features that are used by multiple parts of the application. Shared components are designed and developed to be reusable, meaning that they can be easily used in multiple places throughout the application without duplication of code or effort.
Examples of shared components in Feature Sliced Design might include UI elements such as buttons, headers, footers, or form fields.
Also, I added here all the Material Modules that I need for the project.
@NgModule({
exports: [
MatCheckboxModule,
MatButtonToggleModule,
MatProgressSpinnerModule,
MatCardModule,
MatButtonModule,
],
})
export class UiKitModule {}
And If I need to import some of the mat modules I use them like UiKitModule as below:
@NgModule({
declarations: [NotFoundPage],
imports: [SharedModule, UiKitModule],
exports: [NotFoundPage],
})
export class NotFoundPageModule {}
Сontains the skeleton of the card with slots for content and interactive elements.
“Entity” refers to a data object or record that represents a real-world object or concept. Difficult to understand? Yes, by the way, we have to.
If we say at the ToDoList application — our entity is a Task (business unit) with its attributes such as the task’s name, description, due date, and status.
But we are Front-End developers. How to determine an “Entity”? Of course, as we can — as components with the representation of entities. In our Tutorial, it's a row in *ngFor of [tasks] and Task Details Card (task-card).
For Tweeter Example - it’s a Tweet with Text, Picture, and Tween owner information.
Always contains the interactivity of the card and the logic of processing those interactions.
“Feature” refers to a specific functional aspect or component of a UI. It can be considered a slice of the user interface, with its design specifications and functionalities.
Oversimplify— UI with any kind of interaction — it’s a guaranteed Feature.
In the ToDo app we have task-filter and toggle-task “features”, cause its UI with interacting.
Contains the “assembled” postcard, with content and interactive buttons that are wired up to the relevant calls on the back end.
“Widgets” — extensive and more complex UI layers for combining small, reusable components, UI-kit elements, features, and entities (see below) or that can be combined to create a more extensive and more complex UI.
In our tutorial, we will skip it, because we won’t have a lot of reusable components here. But you have a great visual example — Tweet Card with a full combination of features, entities, and shared.
Widget — is a full combination of all “features” and “entities” with shared components.
Contains the route components for each page in the app, mostly composition, hardly any logic.
In the context of FSD, “pages” — are individual screens or views within a product or user interface. A page is made up of multiple design slices, which are combined to create the complete user experience for that particular view.
Each “page” in FSD is designed and developed as a self-contained unit, with its design specifications and functionalities. This modular approach allows designers and developers to work on individual pages without affecting the rest of the application, making the design process more efficient and flexible.
Additionally, by breaking down pages into smaller, reusable design slices, teams can create a consistent and user-friendly interface that can be easily adapted to different screen sizes, devices, and platforms.
For example, our ToDoList application might have several pages, such as a home page that displays a list of tasks (tasks list), a task detail page that provides more information about a specific task (task details), and a page-not-found.
Contains a setup of routing, store, and global styles.
In our example, we see here AppStoreModule and AppRoutingModule.
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
AppStoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
Let's see what kind of structure we have at least with my comments.
└── src/
├── app/
| ├ app.module
| ├ app-routing.module.ts
| └── store
| └──store.module.ts
|
├── pages/
| ├── not-found/
| ├── task-details/
| └── tasks-list/
├── widgets/ # not represented in this example. See extended edition.
├── features/
| └── tasks-filter
| | | ├── component/ # html, scss, ts
| | | └── model/ # config.service.ts - some logic for filtering
| | └──tasks-filter.module.ts
| └── toggle-task
| | ├── component/ # html, scss, ts
| | └── model/ # config.service.ts - some logic for filtering
| └──toggle-task.module.ts
├── entities/
| └── task
| | ├── task-card/ # html, scss, ts
| | └── task-row/ # html, scss, ts
| └──task.module.ts
└── shared/
├── api #getTasksList api is here
├── lib/ # routerSelectors
└── ui/ #ui-kit modules
Take a look at the full diagram of my example on the link.
You are welcome to see the code on GitHub.
Also published here.