docs: add guidance on when to create an entity#898
docs: add guidance on when to create an entity#898dyakubovskiy wants to merge 8 commits intomainfrom
Conversation
built with Refined Cloudflare Pages Action⚡ Cloudflare Pages Deployment
|
i18n/en/docusaurus-plugin-content-docs/current/reference/layers.mdx
Outdated
Show resolved
Hide resolved
illright
left a comment
There was a problem hiding this comment.
The content is great, thank you! I agree with @Gaic4o that the diagram should perhaps be inserted as an image because it is unreadable when rendered by Docusaurus:
And I also think that the Reference section is not the best place to put advice. This documentation is structured by the Diataxis framework (https://diataxis.fr/), and we've been keeping advice mostly in Guides. Speaking of which, there is a guide about Excessive entities (Code smells & issues), and I have a feeling that this new section shares a lot of content with that guide, could we perhaps deduplicate and leave a link instead?
dyakubovskiy
left a comment
There was a problem hiding this comment.
I replaced the diagram with a Mermaid
dyakubovskiy
left a comment
There was a problem hiding this comment.
I replaced the diagram with a Mermaid one
i18n/en/docusaurus-plugin-content-docs/current/reference/layers.mdx
Outdated
Show resolved
Hide resolved
|
[RU] Рекомендации по выявлению сущностей. Сущность - объект предметной области, который отражает суть бизнеса. Отличительная черта сущности - его уникальность. При проектировании слоя entities в рамках FSD нужно сначала определиться с бизнесом, что есть сущность по его уникальности. Например, уникальный атрибут у заказа - номер, у товара - артикул. Иногда, есть данные, которых не хватает для однозначного определения сущности. Тогда нужно изучить структуру, которую отдает бэк и найти уникальный атрибут, например, id. Если и этого не хватает для однозначного определения сущности, то можно подумать об объекте в реально жизни. Может ли он обладать своей уникальностью? Слой entities отражает ваши основные объекты и нуждается в проектировании на ранних этапах. Пример: Типичная задача. Заголовок: Описание:
Первый пункт. Может ли ФИО говорить об уникальности? Нет. А что с пользователем? Если по бизнесу нет явных атрибутов указывающих на ее уникальность, а их нет, то мы смотрим на технические атрибуты. У пользователя как и у роли есть атрибут id. Вывод: пользователь - сущность. Другой пример Заголовок: Описание:
Второй пункт нас не интересует. Первый пункт нам говорит о том, что у отчета есть уникальный ключ, который характеризует его как сущность. Не бойтесь общаться с бизнесом. Слой entities должен изменятся именно с изменениями в бизнесе. Я рекомендую нарезать сущности сразу, чтобы избежать анемичной модели (https://en.wikipedia.org/wiki/Anemic_domain_model). Важно! Это противоречит подходу с page first, потому что требует чуть больше времени и усилий. [EN] Recommendations for identifying entities. An entity is a domain object that reflects the essence of a business. The distinguishing feature of an entity is its uniqueness. When designing the entities layer within the FSD framework, it is necessary to first determine the business and identify the entity based on its uniqueness. For example, the unique attribute of an order is its number, while the unique attribute of a product is its SKU. In some cases, there may be data that is insufficient to uniquely identify an entity. In such cases, it is necessary to examine the structure provided by the back-end and identify a unique attribute, such as an ID. If even this is insufficient to uniquely identify an entity, it may be necessary to consider the entity in real life and determine whether it possesses its own uniqueness. The entities layer represents your primary objects and requires early design. Example: This is a typical task. Title: Description:
First point: Can the Full Name be considered unique? No. What about the user? If there are no explicit business-oriented attributes that indicate its uniqueness, and there are none, then we look at the technical attributes. The user, like the role, has an id attribute. Therefore, the user is an entity. Another example Title: Description:
We are not interested in the second point. The first point tells us that the report has a unique key that characterizes it as an entity. Don't be afraid to communicate with the business. The entities layer should change with changes in the business. I recommend slicing entities immediately to avoid an anemic model (https://en.wikipedia.org/wiki/Anemic_domain_model). Important! This goes against the page first approach because it requires a little more time and effort. |
…ced/documentation into feature/update_entities_info
|
I still think this page should not be in the Reference, but rather, in Guides. It gives advice rather than describing definitions and base principles, as the reference should, in my opinion. |
Solant
left a comment
There was a problem hiding this comment.
I got a read, I see good points how we can show proper usage of entities, but I am a bit concerned about a current state of this article:
- it is quite large and repeats itself a lot (in regards to business-management communication)
- examples are vue-specific, and it would be much better to show how to handle business logic in pure js (without react state and vue reactivity), as it might be hard for React developers to follow Vue example and vice-versa
- while text states that there should be a good reason to create an entity, in the examples there are couple of entities created straight from the
DTOtype, without any abstraction. so it looks like examples are trying to create an entity when it is not required (just for the sake of it), which can lead to leaky abstractions - some paragraphs are repeating already existing pages from the documentation
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.mdx
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.mdx
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
i18n/en/docusaurus-plugin-content-docs/current/reference/when-create-entity.md
Outdated
Show resolved
Hide resolved
|
One concern about the In real-world products, “User” is often a very broad umbrella concept, so readers might interpret this page as “put all user-related business logic into With that in mind, I wanted to ask (carefully) whether there might be a better example than Also, in larger domains, it’s common to either: (a) modularize within the same slice by responsibility (e.g., splitting read/write operations and separating model concerns), for example: entities/user/
api/
queries/ # read operations
mutations/ # write operations
model/
permissions/
...(b) or split by domain context into more specific entities or slice groups. For example: entities/user/
guide/
profile/It might be helpful to mention these evolution paths so readers don’t assume a single giant Looking at the current example code, it seems like most of it follows a structure like this: shared/
api/
client.ts # infrastructure only
types.ts
entities/
user/
api/
user-api.ts # API + mapping (~100 lines)
model/
types.ts
use-user-permissions.ts # business logic (~150 lines)
index.tsSo I brought this up after thinking about it in that context. |
|
Taken into account and implemented in the new version. The file has been fully rewritten and moved from the reference section to guides — examples are now in React+TS and plain TypeScript without framework reactivity, repetitions are removed, the Approach A section is condensed with a link to the existing api-requests page, and architectural issues are fixed (entity type moved from |
Solant
left a comment
There was a problem hiding this comment.
I think the flow is a bit fragmented: some paragraphs have no text except the small code samples, and lists consisting of minimal synopses might not be enough for the reader to understand the idea behind the reasoning. So I suggest adding some reasoning/explanation
| export interface UserProfile { | ||
| id: string | ||
| name: string | ||
| email: string | ||
| joinedDaysAgo: number | ||
| } | ||
|
|
||
| function mapProfile(dto: UserProfileDTO): UserProfile { | ||
| return { | ||
| id: String(dto.user_id), | ||
| name: dto.full_name, | ||
| email: dto.email, | ||
| joinedDaysAgo: dto.joined_days_ago, | ||
| } | ||
| } |
There was a problem hiding this comment.
Why do we need the UserProfile type as a 1:1 mapping of the existing DTO? I think it can introduce unnecessary friction if some changes are required: instead of changing one UserProfileDTO, we have to change UserProfile and mapper every time the response changes.
| - Unknown what other fields will be needed elsewhere | ||
| - YAGNI — don't create structure "for the future" | ||
|
|
||
| ### Triggers for Moving to Approach A or B |
There was a problem hiding this comment.
nit: maybe it is better to clarify what approaches A and B are, because the reader might forget what options A and B were, for example
"Triggers to move code to shared/api or entities"
| - Teams starting to work with FSD | ||
| - Small projects (fewer than ~10 screens) | ||
| - Projects with frequently changing business logic | ||
| - When it's unclear which entities have stabilized |
There was a problem hiding this comment.
It is also a practical approach when generators like orval or openapi are used
|
|
||
| // Approach A: ~15 files need updates | ||
| // Approach B: 1 file (the mapper in entities/user/api/) | ||
| ``` |
There was a problem hiding this comment.
I don't think this is a good way of using mappers. In this particular example (when the field is renamed), the change is indeed localized, but usually backend changes are different (like field addition/removal), and mappers cannot prevent cascade changes when some data is missing or a new field is added
| - Миграция в `entities/` при усложнении | ||
|
|
||
| **Подход Б: Доменное API** (`entities/*/api/`) | ||
| - API привязано к бизнес-сущности |
There was a problem hiding this comment.
Я бы предложил использовать API как доказательство со звездочкой, но не основной идеей. Здесь валидным определением будет все-таки понимание бизнеса и общение с представителями бизнеса. Если бизнес объект является уникальным по своему уникальному свойству (id, article, uid и т.д), то это можно однозначно интерпретировать как сущность, даже не зная будущих фичей.
|
|
||
| // Техническая уникальность | ||
| User { id: "uuid-123" } | ||
| Payment { id: 456 } |
There was a problem hiding this comment.
Payment нужно отнести к бизнесу. Его уникальный атрибут будет в этом случае его тип, например, cash или card
| Payment { id: 456 } | ||
| ``` | ||
|
|
||
| Наличие уникального идентификатора **не означает** автоматического создания Entity. Это лишь признак того, что объект может ею стать. |
There was a problem hiding this comment.
Тогда нужен пример, когда бизнес объект не может быть сущностью, либо убрать это замечание.
|
|
||
| ```typescript | ||
| // Бизнес-термины (потенциальные сущности) | ||
| User, Customer, Order, Product, Invoice, Payment, Subscription |
There was a problem hiding this comment.
Я бы добавил как бизнес называет это своими словами и как это может выглядеть в коде. Сейчас это уже конкретные интерпретации в коде
| ``` | ||
| Order -> belongs to -> User | ||
| Order -> contains -> Products | ||
| User -> has -> Subscription |
| joinedDaysAgo: number | ||
| } | ||
|
|
||
| function mapProfile(dto: UserProfileDTO): UserProfile { |
There was a problem hiding this comment.
ProfileModel. Ведь мы получим модель для приложения
|
|
||
| Создавайте `entities/*/model/` — независимо от того, где лежит API (`shared/api` или `entities/*/api/`) — когда появляется любое из следующего: | ||
|
|
||
| ### 1. Агрегация данных |
There was a problem hiding this comment.
Как-будто нужно дополнить, что это создание модели из нескольких источников.
| } | ||
| ``` | ||
|
|
||
| ### 2. Бизнес-правила и инварианты |
There was a problem hiding this comment.
Предлагаю убрать "инварианты", потому что применения этого термина здесь нет. Инвариант - валидный объект согласно бизнес требованиям. Если проще, то должны быть проверки важных атрибутов модели, без которых модель считается не валидной. Пример: Пользователь не может быть без имени или Заказ без номера заказа. Эти проверки происходят за одну операцию/транзакцию над моделью (агрегатом)
Background
Русский:
Добавляет практические рекомендации по выделению сущностей (entities) на этапе масштабирования проекта. Рекомендации помогают избежать распространённых ошибок при проектировании архитектуры: преждевременного выделения сущностей или, наоборот, запоздалого рефакторинга.
English:
Adds practical recommendations for extracting entities during project scaling. The guidelines help avoid common architectural pitfalls: premature entity extraction or, conversely, delayed refactoring when the codebase has already become entangled.
Changelog
Русский:
English: