-
Notifications
You must be signed in to change notification settings - Fork 212
docs: add entities.mdx example #859
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: master
Are you sure you want to change the base?
Conversation
built with Refined Cloudflare Pages Action⚡ Cloudflare Pages Deployment
|
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.
Phew, this is a complicated page, but I'm really glad you decided to write it up!
One overarching concern I have with this page is that I'm not sure it belongs in "Guides > Examples" (which frankly is a terrible name for a section, it should be "Guides > Common Tasks" or something, we'll need to workshop this a bit).
I don't think that there is room for this page in the current structure of "Guides", but since it's a very important page, we should probably restructure the Guides section altogether. I don't have any concrete suggestions yet, but I'll think about it.
Anyway, thank you very much, this is great work
i18n/en/docusaurus-plugin-content-docs/current/guides/examples/entities.mdx
Show resolved
Hide resolved
|
||
Most frontend applications function as "thin clients" with minimal business logic, relying on the backend for data processing. In such cases, `entities` layer can often be omitted in favor of `shared/api`. Use `entities` layer only when the following conditions are met: | ||
|
||
- The application handles **client-only data structures** with significant **client-only business 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: I don't entirely agree here that a data structure needs to be client-only to warrant a place in entities.
As an example from my practice, one application I worked on had some non-trivial logic of computing whether the project in its current state can be moved to the next stage. The project was made up of blocks, and each type of block needed to fulfill a certain different condition before the whole project was ready to move to the next step. This logic was implemented on the backend, but for real-time feedback, it was also reimplemented on the frontend.
To me, this reimplementation of business rules is still very much business logic, even if it's not client-only (although to be fair, maybe this particular case relates more to features than entities, but I think my point here still stands).
suggestion: let's broaden the definition:
- The application handles **client-only data structures** with significant **client-only business logic**. | |
- The frontend encodes certain rules about how data structures behave, i. e., _business logic_. | |
- This business logic is used in several parts of the application or is central to the entire purpose of the application. |
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.
I messed up the "suggested change", I meant to replace not just the first bullet point, but both of them
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.
I refactored this section a bit to make sure that “hybrid” is not an exceptional case:
The idea is to make sure that:
- Developers don't create entities by copy-pasting DTOs, as it will require workarounds like cross-imports and lead to the issue of the
entities
layer not providing any meaningful abstraction. - Let developers put business logic inside the
entities
layer, but keep DTOs on a different layer to allow future abstractions from DTOs (if required).
If you end it a situation where you can't create an entity, but you have a significant amount of reused client-only business logic, you can do a hybrid approach: | ||
|
||
- Keep all types/DTOs in `shared/api` without creating a new entity. | ||
- Use `shared/api` for data fetching operations like create, read, update, delete, filter, etc. | ||
- Use `entities/<name>/model` for client-only business 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: this part is a bit confusing to me overall.
If you end it a situation where you can't create an entity
How can this situation arise? Does "can't" here refer to external factors or does it refer to the rules above that dictate that you "mustn't" create an entity?
you have a significant amount of reused client-only business logic
I think a short example would really benefit this section, off the top of my head I'm struggling to imagine what this might look like. Particularly, in light of my previous comment, I'm not sure what is "client-only" business logic, and if such business logic even exists. To me, a client, a frontend, should never be the only party that controls data consistency and adherence to business rules, because a client can never be trusted.
suggestion: I think this hybrid approach is an idea with great potential, so we might be better off dedicating an entire section to it, rather than trying to squeeze it in here. Then we'd also be able to provide examples and expand on the scenario that this hybrid approach might be preferable, as well as mention some pros and cons
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.
Answered in #859 (comment) , but we can replace "busines logic" with something like "data processing".
- Use `entities/<name>/model` for client-only business logic. | ||
|
||
### Examples of Valid Use Cases | ||
1. **Client-Only Entities**: If the frontend manages an entity that does not exist on the backend and involves complex business logic, consider placing it in `entities` layer. This is appropriate only if the entity is used extensively across the application. |
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: I think we're often using the term "entity" without establishing first what an entity is, even though that's what the whole section is about
suggestion: I think DDD has run up against the same issue before, so we might be able to look to them for inspiration for a defintion of "entity". This question on Stack Overflow, for example, provides some insight, but will still probably need more explanation: https://stackoverflow.com/questions/57367017/the-meaning-of-entity-in-domain-driven-design
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.
question: Regarding this part:
This is appropriate only if the entity is used extensively across the application
I'm struggling to come up with an example of an entity that would have complex business logic, but wouldn't be worthy of being put into the entities layer. Do you have some examples?
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.
I will probably need some help with defining "Entity", as for now we only describe it by its properties, and it is challenging to make a good formal description 🤔
And tried to specify "heavy" use with https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)
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.
|
||
### Examples of Valid Use Cases | ||
1. **Client-Only Entities**: If the frontend manages an entity that does not exist on the backend and involves complex business logic, consider placing it in `entities` layer. This is appropriate only if the entity is used extensively across the application. | ||
2. **Aggregating Microservices Data**: When the backend is split across microservices and the frontend needs to combine data with significant client-side business logic, an aggregate entity in `entities` layer may be justified. |
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 (non-blocking): this point might not necessarily be about microservices, but broadly also about any kind of aggregation and synchronization of data from several sources, we can probably expand the example here or add another point
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.
done
|
||
:::note | ||
|
||
Both heavy client-side business logic and client-only data structures must be present to justify creating an entity. If either criterion is missing, avoid creating an entity. |
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: this line forbids doing something without really offering an alternative, which could leave the user stranded, not knowing what to do
suggestion: this whole line would also probably change if we remove the "client-only" condition. Regardless, I think we should direct the user towards some libraries for managing server state like TanStack Query, although I'd prefer if this wasn't our only suggestion, since there's a fair amount of people who dislike it. At least for TanStack Query we have a guide that we can point to
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.
I will try to address several points at once:
I did this proposal in a more prescriptive manner intentionally to create a steiger-like ruleset. So the new developers and developers with heavy DDD-like background don't find a leeway in definitions that will let them create unnecessary entities and prevent them from creating bad abstractions (and blaming FSD for this)
I agree that repeating the same idea 3 times might be an overkill, but I would like to keep the overal "prescriptiveness" (especially now, when logic can be placed to this layer without creating a new entity)
|
||
## Best Practices | ||
|
||
To maintain a clean and maintainable codebase, adhere to the following rules: |
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 (non-blocking):
To maintain a clean and maintainable codebase
this feels like an overpromise to me :) There are many other factors in maintaining a clean and maintainable (tautological btw) codebase
suggestion:
To maintain a clean and maintainable codebase, adhere to the following rules: | |
To make the Entities layer easy to navigate, adhere to the following rules: |
|
||
To maintain a clean and maintainable codebase, adhere to the following rules: | ||
|
||
- **Avoid Dangling Entities**: Do not create entities that are used only once. Treat `entities` layer like any other layer in a pages-first approach, ensuring entity is actually needed. |
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: there might be legitimate reasons to create entities that are only used on a single page, if that page is the main thing about the application. I'm talking about some highly interactive web app like an editor of some kind.
suggestion: let's loosen this criterion, after all, it's "pages-first", not "pages-only".
- **Avoid Dangling Entities**: Do not create entities that are used only once. Treat `entities` layer like any other layer in a pages-first approach, ensuring entity is actually needed. | |
- **Avoid Dangling Entities**: Be suspicious of entities that are used only once. If the entity doesn't facilitate code reuse across several pages, consider moving its code into the page itself if it's possible and practical. This will increase the cohesion of your code, ensuring that related things are close to each other. |
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.
nitpick (non-blocking): I'm also not sure what the point of the title case is in "Avoid Dangling Entities". It's okay-ish on this line, but on the next line, "Do Not Replicate Backend Entities", it makes my eyes jump way too often as I read.
suggestion: I think these rules would look just as good without title case:
- **Avoid dangling entities**: …
To maintain a clean and maintainable codebase, adhere to the following rules: | ||
|
||
- **Avoid Dangling Entities**: Do not create entities that are used only once. Treat `entities` layer like any other layer in a pages-first approach, ensuring entity is actually needed. | ||
- **Do Not Replicate Backend Entities**: Backend entities and API responses belong in `shared/api`, not `entities` layer. `entities` layer is for client-specific logic, not for mirroring server-side data structures. |
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: this feels a bit too prescriptive. I don't see that much of a problem with putting API requests and DTOs in Entities as long as it's a conscious decision because for some reason, putting them in shared/api
didn't sit right with the team. I think we should advocate for putting API code in Shared, but not prescribe it
suggestion: let's tone this down a bit:
- **Do Not Replicate Backend Entities**: Backend entities and API responses belong in `shared/api`, not `entities` layer. `entities` layer is for client-specific logic, not for mirroring server-side data structures. | |
- **Consider keeping backend-related code in `shared/api`**: The Entities layer works best for encoding business logic rules. If these rules are mostly encoded on the backend, consider moving your API request functions, DTOs, and mappers into `shared/api`. This provides fewer architectural restrictions, which can help you iterate faster and avoid arguments when a backend endpoint relates to several kinds of entities at once. |
I also added an explanation for why it's preferable to consider that
## Common Pitfalls | ||
|
||
Seasoned developers may be inclined to create an entity for every piece of data in the application. This is a mistake in FSD. Overusing the entities layer can lead to unnecessary complexity and maintenance overhead. Most frontend applications function effectively without `entities` layer, relying instead on `shared/api` for data fetching and minimal logic. | ||
|
||
:::note | ||
|
||
Prematurely introducing `entities` layer can harm the project's scalability and maintainability. Evaluate whether the layer is truly necessary before adding it. | ||
|
||
::: |
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: I think we've made our point, this feels like rephrasing the same thing again :D
suggestion: let's drop this section
Hey, thanks for the review, I tried to address the majority of questions I agree that Guides is not the correct place for this article; I just want to see how far we can get to clarifying entities before moving/refactoring it into an appropriate section. |
Background
Right now
entities
is very tricky to use, and we constantly need to assist people with it. The idea of this proposal is to gather feedback and formalize how and when to use this layer, to make sure that everyone understands when and how to use itAfter formalization, I would also like to refactor https://feature-sliced.github.io/documentation/docs/guides/examples/auth guide to make sure that we don't suggest creating
entities
layer before it is actually required by the project