-
Notifications
You must be signed in to change notification settings - Fork 212
docs: excessive-entities page #865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,98 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
# Excessive Entities | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
The entities layer in Feature-Sliced Design is the first layer that incorporates business logic, distinguishing it from the `shared` layer. Unlike the `model` segment, it is globally accessible (except by `shared`), making it reusable across the application. However, its global nature means changes can have a widespread impact, requiring careful design to avoid costly refactors. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
## How to keep `entities` layer clean | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
To keep a maintainable `entities` layer, consider the following principles based on the application's data processing needs. Keep in mind that this classification is not strictly binary, as different parts of the same application may have “thin” or “thick” parts: | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
- Thin Clients: These applications rely on the backend for most data processing. They often do not require an `entities` layer, as client-side business logic is minimal and involves only data retrieval. | ||||||||||||||||||||||||||||||||||||||||||||||
- Thick Clients: These handle significant client-side business logic, making them suitable candidates for the `entities` layer. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
It is acceptable for an application to lack an `entities` layer if it functions as a thin client. This simplifies the architecture and keeps the `entities` layer available for future scaling if needed. | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+5
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: when I was reading this part, I thought that this text is the advice on how to keep the Entities layer clean, but turns out that it's just an informational aside, and the actual advice is in the subsequent headings suggestion: perhaps let's just make this an aside, that way those readers who already know about the distinction of thick vs thin clients could just skip it over:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Although I kinda want to have the thick/thin client be the "0th principle" — like "do you even need the entities layer" |
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
### Avoid Unnecessary Entities | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Do not create an entity for every piece of business logic. Instead, leverage types from `shared/api` and place logic in the `model` segment of a current slice. For reusable business logic, use the `model` segment within an entity slice while keeping data definitions in `shared/api`: | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```plaintext | ||||||||||||||||||||||||||||||||||||||||||||||
📂 entities | ||||||||||||||||||||||||||||||||||||||||||||||
📂 order | ||||||||||||||||||||||||||||||||||||||||||||||
📄 index.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📂 model | ||||||||||||||||||||||||||||||||||||||||||||||
📄 apply-discount.ts // Business logic using OrderDto from shared/api | ||||||||||||||||||||||||||||||||||||||||||||||
📂 shared | ||||||||||||||||||||||||||||||||||||||||||||||
📂 api | ||||||||||||||||||||||||||||||||||||||||||||||
📄 index.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📂 endpoints | ||||||||||||||||||||||||||||||||||||||||||||||
📄 order.ts | ||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
### Exclude CRUD Operations from Entities | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
CRUD operations, while essential, often involve boilerplate code without significant business logic. Including them in the `entities` layer can clutter it and obscure meaningful code. Instead, place CRUD operations in `shared/api`: | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```plaintext | ||||||||||||||||||||||||||||||||||||||||||||||
📂 shared | ||||||||||||||||||||||||||||||||||||||||||||||
📂 api | ||||||||||||||||||||||||||||||||||||||||||||||
📄 client.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📄 index.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📂 endpoints | ||||||||||||||||||||||||||||||||||||||||||||||
📄 order.ts // Contains all order-related CRUD operations | ||||||||||||||||||||||||||||||||||||||||||||||
📄 products.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📄 cart.ts | ||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
For complex CRUD operations (e.g., atomic updates, rollbacks, or transactions), evaluate whether the `entities` layer is appropriate, but use it with caution. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
### Store Authentication Data in `shared` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Avoid creating a `user` entity for authentication data, such as tokens or user DTOs returned from the backend. These are context-specific and unlikely to be reused outside authentication: | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: I think this also collides a bit with the guide for authentication, where we present the entities layer as one of the options for storing auth data suggestion: let's simply recommend storing auth data in shared for simple cases (i.e. most cases) |
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
- Authentication responses (e.g., tokens or DTOs) often lack fields needed for broader reuse or vary by context (e.g., private vs. public user profiles). | ||||||||||||||||||||||||||||||||||||||||||||||
- Using entities for auth data can lead to cross-layer imports (e.g., `entities` into `shared`) or usage of `@x` notation, complicating the architecture. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Instead, store authentication-related data in `shared/auth` or `shared/api`: | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```plaintext | ||||||||||||||||||||||||||||||||||||||||||||||
📂 shared | ||||||||||||||||||||||||||||||||||||||||||||||
📂 auth | ||||||||||||||||||||||||||||||||||||||||||||||
📄 use-auth.ts // Hook returning authenticated user info or token | ||||||||||||||||||||||||||||||||||||||||||||||
📄 index.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📂 api | ||||||||||||||||||||||||||||||||||||||||||||||
📄 client.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📄 index.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📂 endpoints | ||||||||||||||||||||||||||||||||||||||||||||||
📄 order.ts | ||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
### Minimize Cross-Imports | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
FSD permits cross-imports via `@x` notation, but they can introduce technical issues like circular dependencies. To avoid this, design entities within isolated business contexts to eliminate the need for cross-imports: | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Non-Isolated Business Context (Avoid): | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```plaintext | ||||||||||||||||||||||||||||||||||||||||||||||
📂 entities | ||||||||||||||||||||||||||||||||||||||||||||||
📂 order | ||||||||||||||||||||||||||||||||||||||||||||||
📂 @x | ||||||||||||||||||||||||||||||||||||||||||||||
📂 model | ||||||||||||||||||||||||||||||||||||||||||||||
📂 order-item | ||||||||||||||||||||||||||||||||||||||||||||||
📂 @x | ||||||||||||||||||||||||||||||||||||||||||||||
📂 model | ||||||||||||||||||||||||||||||||||||||||||||||
📂 order-customer-info | ||||||||||||||||||||||||||||||||||||||||||||||
📂 @x | ||||||||||||||||||||||||||||||||||||||||||||||
📂 model | ||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Isolated Business Context (Preferred): | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```plaintext | ||||||||||||||||||||||||||||||||||||||||||||||
📂 entities | ||||||||||||||||||||||||||||||||||||||||||||||
📂 order-info | ||||||||||||||||||||||||||||||||||||||||||||||
📄 index.ts | ||||||||||||||||||||||||||||||||||||||||||||||
📂 model | ||||||||||||||||||||||||||||||||||||||||||||||
📄 order-info.ts | ||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
An isolated context encapsulates all related logic (e.g., order items and customer info) within a single module, reducing complexity and preventing external modifications to tightly coupled logic. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue:
That's not entirely consistent with the rest of the docs, because we mention that it's okay for some business logic to exist in Shared if the size of that business logic doesn't justify a separate layer.
suggestion:
I'm not sure we need the part about how it's distinct from Shared (they are two different layers, after all), so maybe we can shorten to this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: let's also include a little "bridge" phrase between needing to be careful with the design of Entities and the issue of excessive entities, something like: