Skip to content

Commit

Permalink
feat: Feature-Sliced Design 2.1 (#703)
Browse files Browse the repository at this point in the history
  • Loading branch information
illright authored Nov 13, 2024
1 parent 363c739 commit dd7be18
Show file tree
Hide file tree
Showing 46 changed files with 616 additions and 1,126 deletions.
61 changes: 57 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,64 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Since last release][since-last-release]
<!-- ## [Since last release][since-last-release] -->

### Added
## [2.1.0] - 2024-10-31

The new revision of Feature-Sliced Design is here! The main difference with FSD 2.0 is the new approach to decomposition — “pages first”.

### What's “pages-first”?

You do “pages first” by keeping more code in pages. For example, large blocks of UI, forms and data logic that are not reused on other pages should now stay in the slice of the page that they are used in. The division by segments (`ui`, `api`, `model`, etc.) still applies to all this code, and we encourage you to further split and organize code into folders inside of segments — don't just pile all the code into a single file.

In the same way, widgets are no longer just a compositional layer, instead they should also store code that isn't currently needed outside of that widget, including its own stores, business logic, and API interactions.

When you have a need to reuse code in several widgets or pages, consider putting it in Shared. If that code involves business logic (i. e. managing specific modal dialogs), consider breaking it up into infrastructural code, like the modal manager, and the business code, like the content of the modals. The infrastructure can then go to Shared, and the content can stay in the pages that use this infrastructure.

### How is it different?

In FSD 2.0 we explained how to identify entities and features in your application, and then combine them in widgets and pages. Over time we started disliking this approach, mostly for the following reasons:

- Code cohesion is much worse in this approach
- You need to jump around several folders just to make changes to a single user flow
- Unused code is harder to delete because it's somewhere else
- Finding entities and features is still an advanced skill that needs to be developed over time
- It requires understanding of the business context, which not all developers want to bother with
- On the other hand, splitting by pages is natural and requires little training
- Different developers have different understandings of these concepts, which leads to everyone having their own idea of FSD, which causes conflict and misunderstanding

### Is it hard to migrate from FSD 2.0?

This is a non-breaking change, so you don’t even necessarily need to migrate your current FSD projects to FSD 2.1, but we still think the new way of thinking will lead to a more cohesive and less opinionated structure. We’ve compiled a few steps you can take in [the migration guide](https://feature-sliced.design/docs/guides/migration/from-v2-0).

- New article about how to use FSD with Next.js (#644).
### What else happened since the last release?

Another exciting new thing in the FSD ecosystem is our architectural linter, [Steiger](https://github.com/feature-sliced/steiger). It's still in active development, but it is production-ready.

A couple more minor clarifications to the docs were made as well:

1. Application-aware things like the route constants, the API calls, or company logo, are now explicitly allowed in Shared. Business logic is still not allowed, but these things are not considered to be business logic.
2. Imports between segments in App and Shared were always allowed, but it's been made explicit too.

And here's what happened to the documentation website:

#### Added

- Slightly rewritten and expanded overview page to give some details about FSD right away (#685).
- New partial translations: Korean (#739, #736, #735, #742, #732, #730, #715), Japanese (#728).
- The tutorial was rewritten. Technical details were stripped out, more FSD theory has been added (#665).
- Guides on how to deal with common frontend issues like page layouts (#708), types (#701), authentication (#693).
- Guides on how to use FSD with Nuxt (#710, #689, #683, #679), SvelteKit (#698), Next.js (#699, #664, #644), and TanStack Query (#673).
- A new feedback widget, powered by PushFeedback! Go give it a try and let us know what you think of the new pages (#695).
- Comparison of FSD with Atomic Design (#671).

#### Changed

- The migration guide from a custom architecture (formerly known as "from legacy") has been actualized (#725).

#### Removed

- The decomposition cheatsheet is now unlisted for an undefined period of time. It proved to be more harmful than useful, but maybe it can be saved later (#649).

## [2.0.0] - 2023-10-01

Expand Down Expand Up @@ -41,5 +93,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The overview page has been rewritten to be more concise and informative (#512, #515, #516).
- FSD has updated its branding, and there are now guidelines to the brand usage. The standard spelling of the name is now "Feature-Sliced Design" (#496, #499, #500, #465).

[since-last-release]: https://github.com/feature-sliced/documentation/compare/v2.0.0...HEAD
[since-last-release]: https://github.com/feature-sliced/documentation/compare/v2.1.0...HEAD
[2.1.0]: https://github.com/feature-sliced/documentation/releases/tag/v2.1.0
[2.0.0]: https://github.com/feature-sliced/documentation/releases/tag/v2.0.0
2 changes: 1 addition & 1 deletion config/docusaurus/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const presets = [
showLastUpdateTime: true,
versions: {
current: {
label: `v2.0.0 🍰`,
label: `v2.1`,
},
},
sidebarItemsGenerator,
Expand Down
18 changes: 15 additions & 3 deletions config/docusaurus/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,17 @@ const LEGACY_ROUTES = [
{
title: "Decouple of entities",
from: "/docs/concepts/decouple-entities",
to: "/docs/reference/isolation/decouple-entities",
to: "/docs/reference/layers#import-rule-on-layers",
},
{
title: "Low Coupling & High Cohesion",
from: "/docs/concepts/low-coupling",
to: "/docs/reference/isolation/coupling-cohesion",
to: "/docs/reference/slices-segments#zero-coupling-high-cohesion",
},
{
title: "Cross-communication",
from: "/docs/concepts/cross-communication",
to: "/docs/reference/isolation",
to: "/docs/reference/layers#import-rule-on-layers",
},
{
title: "App splitting",
Expand Down Expand Up @@ -276,6 +276,18 @@ const LEGACY_ROUTES = [
},
],
},
{
group: "Deduplication of Reference",
details:
"Cleaned up the Reference section and deduplicated the material",
children: [
{
title: "Isolation of modules",
from: "/docs/reference/isolation",
to: "/docs/reference/layers#import-rule-on-layers",
},
],
},
];

// @returns { from, to }[]
Expand Down
2 changes: 1 addition & 1 deletion i18n/en/docusaurus-plugin-content-docs/current.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version.label": {
"message": "v2.0.0 🍰",
"message": "v2.1",
"description": "The label for version current"
},
"sidebar.getstartedSidebar.category.Tutorials": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ You can ask your question in our [Telegram chat][telegram], [Discord community][

### Is there a toolkit or a linter?

There is an official ESLint config — [@feature-sliced/eslint-config][eslint-config-official], and an ESLint plugin — [@conarti/eslint-plugin-feature-sliced][eslint-plugin-conarti], created by Aleksandr Belous, a community member. You're welcome to contribute to these projects or start your own!
Yes! We have a linter called [Steiger][ext-steiger] to check your project's architecture and [folder generators][ext-tools] through a CLI or IDEs.

### Where to store the layout/template of pages?

Expand Down Expand Up @@ -58,10 +58,10 @@ Rather yes than no

Answered [here](/docs/guides/examples/auth)

[ext-steiger]: https://github.com/feature-sliced/steiger
[ext-tools]: https://github.com/feature-sliced/awesome?tab=readme-ov-file#tools
[import-rule-layers]: /docs/reference/layers#import-rule-on-layers
[reference-entities]: /docs/reference/layers#entities
[eslint-config-official]: https://github.com/feature-sliced/eslint-config
[eslint-plugin-conarti]: https://github.com/conarti/eslint-plugin-feature-sliced
[motivation]: /docs/about/motivation
[telegram]: https://t.me/feature_sliced
[discord]: https://discord.gg/S8MzWTUsmp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ As such, our Pages folder will look like this:

The key difference of Feature-Sliced Design from an unregulated code structure is that pages cannot reference each other. That is, one page cannot import code from another page. This is due to the **import rule on layers**:

*A module in a slice can only import other slices when they are located on layers strictly below.*
*A module (file) in a slice can only import other slices when they are located on layers strictly below.*

In this case, a page is a slice, so modules (files) inside this page can only reference code from layers below, not from the same layer, Pages.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ To store the token in the User entity, create a reactive store in the `model` se

Since the API client is usually defined in `shared/api` or spreaded across the entities, the main challenge to this approach is making the token available to other requests that need it without breaking [the import rule on layers][import-rule-on-layers]:

> A module in a slice can only import other slices when they are located on layers strictly below.
> A module (file) in a slice can only import other slices when they are located on layers strictly below.
There are several solutions to this challenge:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 1
sidebar_label: From a custom architecture
---

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
sidebar_position: 4
sidebar_position: 2
---

# Migration from v1
# Migration from v1 to v2

## Why v2?

Expand Down Expand Up @@ -158,7 +158,7 @@ Now it is much easier to [observe the principle of low coupling][refs-low-coupli
- [New ideas v2 with explanations (atomicdesign-chat)][ext-tg-v2-draft]
- [Discussion of abstractions and naming for the new version of the methodology (v2)](https://github.com/feature-sliced/documentation/discussions/31)

[refs-low-coupling]: /docs/reference/isolation/coupling-cohesion
[refs-low-coupling]: /docs/reference/slices-segments#zero-coupling-high-cohesion
[refs-adaptability]: /docs/about/understanding/naming

[ext-v1]: https://feature-sliced.github.io/featureslices.dev/v1.0.html
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
sidebar_position: 3
---

# Migration from v2.0 to v2.1

The main change in v2.1 is the new mental model for decomposing an interface — pages first.

In v2.0, FSD would recommend identifying entities and features in your interface, considering even the smallest bits of entity representation and interactivity for decomposition. Then you would build widgets and pages from entities and features. In this model of decomposition, most of the logic was in entities and features, and pages were just compositional layers that didn't have much significance on their own.

In v2.1, we recommend starting with pages, and possibly even stopping there. Most people already know how to separate the app into individual pages, and pages are also a common starting point when trying to locate a component in the codebase. In this new model of decomposition, you keep most of the UI and logic in each individual page, maintaining a reusable foundation in Shared. If a need arises to reuse business logic across several pages, you can move it to a layer below.

Another addition to Feature-Sliced Design is the standardization of cross-imports between entities with the `@x`-notation.

## How to migrate {#how-to-migrate}

There are no breaking changes in v2.1, which means that a project written with FSD v2.0 is also a valid project in FSD v2.1. However, we believe that the new mental model is more beneficial for teams and especially onboarding new developers, so we recommend making minor adjustments to your decomposition.

### Merge slices

A simple way to start is by running our linter, [Steiger][steiger], on the project. Steiger is built with the new mental model, and the most helpful rules will be:

- [`insignificant-slice`][insignificant-slice] — if an entity or feature is only used in one page, this rule will suggest merging that entity or feature into the page entirely.
- [`excessive-slicing`][excessive-slicing] — if a layer has too many slices, it's usually a sign that the decomposition is too fine-grained. This rule will suggest merging or grouping some slices to help project navigation.

```bash
npx steiger src
```

This will help you identify which slices are only used once, so that you could reconsider if they are really necessary. In such considerations, keep in mind that a layer forms some kind of global namespace for all the slices inside of it. Just as you wouldn't pollute the global namespace with variables that are only used once, you should treat a place in the namespace of a layer as valuable, to be used sparingly.

### Standardize cross-imports

If you had cross-imports between in your project before (we don't judge!), you may now take advantage of a new notation for cross-importing in Feature-Sliced Design — the `@x`-notation. It looks like this:

```ts title="entities/B/some/file.ts"
import type { EntityA } from "entities/A/@x/B";
```

For more details, check out the [Public API for cross-imports][public-api-for-cross-imports] section in the reference.

[insignificant-slice]: https://github.com/feature-sliced/steiger/tree/master/packages/steiger-plugin-fsd/src/insignificant-slice
[steiger]: https://github.com/feature-sliced/steiger
[excessive-slicing]: https://github.com/feature-sliced/steiger/tree/master/packages/steiger-plugin-fsd/src/excessive-slicing
[public-api-for-cross-imports]: /docs/reference/public-api#public-api-for-cross-imports
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,9 @@ A detailed description of the key concepts of Feature-Sliced Design.
to="/docs/reference/slices-segments"
Icon={AppstoreOutlined}
/>
<NavCard
title="Isolation"
description="Pratices for scalable and efficient module interactions"
to="/docs/reference/isolation"
Icon={NodeIndexOutlined}
/>
<NavCard
title="Public API"
description="Practices for designing scalable and easy-to-integrate modules"
description="Definition and goals of public APIs, cross-imports with @x, troubleshooting"
to="/docs/reference/public-api"
Icon={ApiOutlined}
/>

This file was deleted.

Loading

0 comments on commit dd7be18

Please sign in to comment.