|
| 1 | +--- |
| 2 | +title: Slice groups |
| 3 | +sidebar: |
| 4 | + order: 3 |
| 5 | +--- |
| 6 | + |
| 7 | +import { FileTree } from '@astrojs/starlight/components'; |
| 8 | + |
| 9 | +A slice group is a folder that visually groups related slices together. |
| 10 | +It is not a required part of FSD and can be optionally introduced when the number of slices grows large enough to make navigation difficult. |
| 11 | + |
| 12 | +This document explains what a slice group is, what problem it solves, when to introduce it, and what rules to follow when applying it. |
| 13 | + |
| 14 | +> Slice groups are also referred to as structural grouping or slice grouping. In this document, we call them **slice groups**. |
| 15 | +
|
| 16 | +## What is a slice group |
| 17 | + |
| 18 | +A slice group is a folder that groups related slices within the same layer. |
| 19 | +A slice group itself is not a slice — it does not have its own segments or public API. |
| 20 | + |
| 21 | +In other words, a slice group is a way to make the folder structure easier to find and read without changing FSD's dependency rules. |
| 22 | + |
| 23 | +### Core rules |
| 24 | + |
| 25 | +> **Rule 1: A group folder is just a structural folder** |
| 26 | +> |
| 27 | +> A group folder does not have its own segments like `model`, `ui`, or `api`. It also does not have a public API like `index.ts`. |
| 28 | +
|
| 29 | +> **Rule 2: Slices inside a group are still independent** |
| 30 | +> |
| 31 | +> Even when slices are under the same group, the isolation rules between slices apply exactly the same as outside the group. |
| 32 | +
|
| 33 | +> **Rule 3: A group is only for navigation, not as a separate functional unit** |
| 34 | +> |
| 35 | +> Do not use a slice group as a new layer, sub-layer, or shared module. |
| 36 | +
|
| 37 | +If these rules are not followed, the group folder may end up being used as a separate structural unit rather than a simple organizational folder. |
| 38 | +This makes it harder to distinguish which folders are actual slices and can make dependency relationships more complex. |
| 39 | + |
| 40 | +## Why it is needed |
| 41 | + |
| 42 | +When there are few slices, a flat structure is sufficient for navigation. |
| 43 | +However, as slices of the same nature grow in number, you have to scan through the entire layer multiple times to find related code. |
| 44 | + |
| 45 | +For example, suppose the `entities` layer has accumulated many payment-related slices. |
| 46 | + |
| 47 | +**Before grouping:** |
| 48 | + |
| 49 | +<FileTree> |
| 50 | +- entities/ |
| 51 | + - invoice/ |
| 52 | + - model/ |
| 53 | + - ui/ |
| 54 | + - receipt/ |
| 55 | + - model/ |
| 56 | + - ui/ |
| 57 | + - transaction/ |
| 58 | + - model/ |
| 59 | + - ui/ |
| 60 | + - user/ |
| 61 | + - model/ |
| 62 | + - ui/ |
| 63 | + - product/ |
| 64 | + - model/ |
| 65 | + - ui/ |
| 66 | + - ... |
| 67 | +</FileTree> |
| 68 | + |
| 69 | +To find the payment-related slices (`invoice`, `receipt`, `transaction`), you have to scan through the entire layer to locate them. |
| 70 | + |
| 71 | +**After grouping:** |
| 72 | + |
| 73 | +<FileTree> |
| 74 | +- entities/ |
| 75 | + - payment/ slice group |
| 76 | + - invoice/ slice |
| 77 | + - model/ |
| 78 | + - ui/ |
| 79 | + - receipt/ slice |
| 80 | + - model/ |
| 81 | + - ui/ |
| 82 | + - transaction/ slice |
| 83 | + - model/ |
| 84 | + - ui/ |
| 85 | + - user/ slice (not a group) |
| 86 | + - model/ |
| 87 | + - ui/ |
| 88 | + - product/ slice (not a group) |
| 89 | + - model/ |
| 90 | + - ui/ |
| 91 | + - ... |
| 92 | +</FileTree> |
| 93 | + |
| 94 | +Now, payment-domain-related slices can be found directly under `payment/`. |
| 95 | + |
| 96 | +Slice groups work this way — by placing related slices close together, they **help you find related slices faster**. |
| 97 | +This is especially helpful for new team members or developers seeing the area for the first time to understand the structure. |
| 98 | + |
| 99 | +### The same problem can occur in pages |
| 100 | + |
| 101 | +A similar problem can appear in the pages layer. |
| 102 | +When list, detail, create, and edit pages for a specific domain are scattered across the entire layer, discoverability suffers. |
| 103 | + |
| 104 | +<FileTree> |
| 105 | +- pages/ |
| 106 | + - orderCreate/ |
| 107 | + - ui/ |
| 108 | + - orderDetail/ |
| 109 | + - ui/ |
| 110 | + - orderList/ |
| 111 | + - ui/ |
| 112 | + - customerDetail/ |
| 113 | + - ui/ |
| 114 | + - customerList/ |
| 115 | + - ui/ |
| 116 | + - settings/ |
| 117 | + - ui/ |
| 118 | + - ... |
| 119 | +</FileTree> |
| 120 | + |
| 121 | +With `order`-related and `customer`-related pages listed flat, you have to scan the entire layer to find pages for a specific domain. |
| 122 | + |
| 123 | +Introducing slice groups allows you to see all pages of the same domain in one place. |
| 124 | + |
| 125 | +<FileTree> |
| 126 | +- pages/ |
| 127 | + - order/ slice group |
| 128 | + - orderCreate/ slice |
| 129 | + - ui/ |
| 130 | + - orderDetail/ slice |
| 131 | + - ui/ |
| 132 | + - orderList/ slice |
| 133 | + - ui/ |
| 134 | + - customer/ slice group |
| 135 | + - customerDetail/ slice |
| 136 | + - ui/ |
| 137 | + - customerList/ slice |
| 138 | + - ui/ |
| 139 | + - settings/ slice (not a group) |
| 140 | + - ui/ |
| 141 | + - ... |
| 142 | +</FileTree> |
| 143 | + |
| 144 | +In this way, slice groups can be applied not only to entities but also to the pages layer when related pages grow numerous enough to make navigation difficult. |
| 145 | + |
| 146 | +## When to consider introducing them |
| 147 | + |
| 148 | +Slice groups are not a default structure — they are introduced optionally when you need to solve a navigation problem. |
| 149 | + |
| 150 | +### Signals to consider introduction |
| 151 | + |
| 152 | +- **Multiple slices sharing the same business context are scattered within a layer.** |
| 153 | + For example, `invoice`, `receipt`, and `transaction` all deal with the payment context but are listed flat. |
| 154 | + |
| 155 | +- **A common parent context is naturally apparent just from the slice names.** |
| 156 | + For example, `orderCreate`, `orderDetail`, and `orderList` are all understood under the common context of `order`. |
| 157 | + Similarly, `invoice`, `receipt`, and `transaction` can be recognized together under the `payment` context. |
| 158 | + |
| 159 | +- **You find yourself repeatedly scanning the entire layer to find related slices.** |
| 160 | + |
| 161 | +- **New team members find themselves repeatedly scanning the entire layer to understand the structure.** |
| 162 | + |
| 163 | +### Signals it is not needed yet |
| 164 | + |
| 165 | +- Even with many slices, names alone are sufficient for quick navigation. |
| 166 | + |
| 167 | +- The grouping criteria feel unnatural, and different team members keep grouping things differently. |
| 168 | + |
| 169 | +- Even after creating a group, it only contains 1–2 slices, so the organizational benefit is minimal. |
| 170 | + |
| 171 | +- Creating a group actually requires additional explanation like "is this a slice or a group?" |
| 172 | + |
| 173 | +The key point is not about increasing folder depth, but about whether the structure actually becomes easier to find and understand. |
| 174 | + |
| 175 | +## How to apply |
| 176 | + |
| 177 | +### entities — domain-based grouping |
| 178 | + |
| 179 | +Group slices that are close from a domain model perspective. |
| 180 | + |
| 181 | +<FileTree> |
| 182 | +- entities/ |
| 183 | + - payment/ slice group |
| 184 | + - invoice/ slice |
| 185 | + - model/ |
| 186 | + - ui/ |
| 187 | + - receipt/ slice |
| 188 | + - model/ |
| 189 | + - ui/ |
| 190 | + - transaction/ slice |
| 191 | + - model/ |
| 192 | + - ui/ |
| 193 | + - user/ slice (not a group) |
| 194 | + - model/ |
| 195 | + - ui/ |
| 196 | + - ... |
| 197 | +</FileTree> |
| 198 | + |
| 199 | +Since `payment/` is not a slice, it does not have its own `model.ts`, `index.ts`, or `api/`. |
| 200 | + |
| 201 | +Not all slices need to be placed in a group. |
| 202 | +Slices like `user/`, whose meaning is clear on its own, can remain without a group. |
| 203 | + |
| 204 | +### pages — domain-based page grouping |
| 205 | + |
| 206 | +This can be used when multiple pages of the same domain need to be handled together. |
| 207 | + |
| 208 | +<FileTree> |
| 209 | +- pages/ |
| 210 | + - order/ slice group |
| 211 | + - orderCreate/ slice |
| 212 | + - ui/ |
| 213 | + - orderDetail/ slice |
| 214 | + - ui/ |
| 215 | + - orderList/ slice |
| 216 | + - ui/ |
| 217 | + - customer/ slice group |
| 218 | + - customerDetail/ slice |
| 219 | + - ui/ |
| 220 | + - customerList/ slice |
| 221 | + - ui/ |
| 222 | + - settings/ slice (not a group) |
| 223 | + - ui/ |
| 224 | + - ... |
| 225 | +</FileTree> |
| 226 | + |
| 227 | +This structure is helpful when there are many pages on the same topic, such as list, detail, create, and edit. |
| 228 | +Opening just one parent folder lets you see all related pages together. |
| 229 | + |
| 230 | +### Can it be used in features too? |
| 231 | + |
| 232 | +Yes, it can. |
| 233 | +However, since features often span multiple entities and multiple user flows, the grouping criteria may be less clear than in entities or pages. |
| 234 | + |
| 235 | +Therefore, it is better to view slice groups in features as an option to be used exceptionally. |
| 236 | +If there is no natural grouping criterion, it is better not to force their introduction. |
| 237 | + |
| 238 | +## Patterns to avoid |
| 239 | + |
| 240 | +A slice group should be a structural folder only. |
| 241 | +If you start placing shared code or files that re-export multiple slices in the group folder, the group effectively starts behaving as a module. |
| 242 | + |
| 243 | +**Pattern to avoid:** |
| 244 | + |
| 245 | +<FileTree> |
| 246 | +- entities/ |
| 247 | + - payment/ |
| 248 | + - model.ts group-level shared model |
| 249 | + - index.ts group-level re-export |
| 250 | + - invoice/ |
| 251 | + - model/ |
| 252 | + - ui/ |
| 253 | + - receipt/ |
| 254 | + - model/ |
| 255 | + - ui/ |
| 256 | +</FileTree> |
| 257 | + |
| 258 | +In this structure, through `payment/model.ts` or `payment/index.ts`, |
| 259 | +even though the child slices appear independent, they easily become coupled at the group level in practice. |
| 260 | + |
| 261 | +As a result, slice boundaries become blurred, dependency directions become harder to trace, and it becomes confusing what the actual independent units are. |
| 262 | + |
| 263 | +**Correct pattern:** |
| 264 | + |
| 265 | +<FileTree> |
| 266 | +- entities/ |
| 267 | + - payment/ |
| 268 | + - invoice/ |
| 269 | + - model/ |
| 270 | + - ui/ |
| 271 | + - receipt/ |
| 272 | + - model/ |
| 273 | + - ui/ |
| 274 | +</FileTree> |
| 275 | + |
| 276 | +If shared logic is genuinely needed, you should first consider whether it can be separated into a more appropriate layer or a separate slice, rather than forcing it into the group folder. |
| 277 | + |
| 278 | +## FAQ |
| 279 | + |
| 280 | +### Do all slices need to be placed in a group? |
| 281 | + |
| 282 | +No. |
| 283 | +Only group slices that naturally belong together, and leave the rest as they are. |
| 284 | + |
| 285 | +For example, group only cases where viewing them together is natural, like `payment/invoice` and `payment/receipt`, |
| 286 | +while slices that are independently understood like `user` and `settings` can remain without a group. |
| 287 | + |
| 288 | +### Is it okay if the group name is included in the import path? |
| 289 | + |
| 290 | +Yes, it is fine. |
| 291 | +For example, if the group name is included in the path like `@/entities/payment/invoice`, the path itself reveals which topic the slice belongs to. |
| 292 | + |
| 293 | +However, how to expose the path is a matter of the team's import convention. |
| 294 | + |
| 295 | +### Does having a slice group make imports between slices more flexible? |
| 296 | + |
| 297 | +No. |
| 298 | +Import rules between slices are not relaxed just because they are in the same group. |
| 299 | + |
| 300 | +A group is only a structure for navigation, and slice boundaries and dependency rules remain the same as before. |
| 301 | + |
| 302 | +## References |
| 303 | + |
| 304 | +- [KakaoPay — A case of applying slice grouping in the pages layer](https://tech.kakaopay.com/post/fsd/#2-slice-grouping-%ED%97%88%EC%9A%A9-%EB%B0%8F-pages-%EB%A0%88%EC%9D%B4%EC%96%B4-%EA%B5%AC%EC%84%B1) |
| 305 | +- [Russian Telegram FSD community — Discussion on structural grouping, slice groups, and layer extension](https://t.me/c/1216849846/1/2785) |
| 306 | +- [Russian Telegram FSD community — Nested vs. flat structures, forms and allowed scope of slice groups](https://t.me/c/1216849846/1/5051) |
| 307 | +- [Russian Telegram FSD community — Why there are fewer grouping examples in features, and whether slice groups are possible in features](https://t.me/c/1216849846/1/80620) |
0 commit comments