-
Notifications
You must be signed in to change notification settings - Fork 212
Docs/concepts/public api #76
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
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
f023c92
feat(public-api): add draft
AlexandrHoroshih 4427319
feat(public-api): add sections for api requirements
AlexandrHoroshih 434d59b
feat(public-api): add requirements to head section
AlexandrHoroshih 09909fc
feat(public-api): add formatting to examples
AlexandrHoroshih f80509c
feat(public-api): add formatting to description
AlexandrHoroshih 1e9e8c8
feat(public-api): stylistic amends
AlexandrHoroshih 903ecc9
feat(public-api): minor amends
AlexandrHoroshih a127095
feat(public-api): clarified on pros of public api
AlexandrHoroshih 422be0f
feat(public-api): add example on good api
AlexandrHoroshih 738c220
feat(public-api): minor amend
AlexandrHoroshih cc42bc0
feat(public-api): hide examples under spoiler
AlexandrHoroshih e1dfb4d
feat(public-api): highlight key requirements
AlexandrHoroshih 5f2a415
fix(public-api): update structure as per review
AlexandrHoroshih b806998
feat(public-api): style examples for easier reading
AlexandrHoroshih ee22567
feat(public-api): add "See also" section
AlexandrHoroshih 1116122
feat(public-api): add reexports section
AlexandrHoroshih 5146208
feat(public-api): add note on tree-shaking
AlexandrHoroshih 33a0aa0
feat(readme): add link to public-api.md
AlexandrHoroshih 4ac2a6a
fix(public-api): more explicit wording
AlexandrHoroshih ecb55d2
feat(public-api): add access control example
AlexandrHoroshih 367127f
feat(public-api): add remark on module behavior
AlexandrHoroshih ce77b79
fix(public-api): document flow
AlexandrHoroshih a948aa6
fix(public-api): merge spoilers
AlexandrHoroshih e055f27
feat(public-api): name collisons example
AlexandrHoroshih bdb31ab
fix(public-api): typo
AlexandrHoroshih File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
# Публичное API модуля приложения | ||
|
||
Каждая сущность методологии проектируется как **удобный в использовании и интеграции** модуль. | ||
|
||
## Проектирование взаимодействия модуля с остальным приложением | ||
Удобство использования и интеграции модуля достигается через выполнение ряда целей: | ||
|
||
1. Приложение должно быть защищено от изменений внутренней структуры отдельных модулей | ||
2. Переработка внутренней структуры модуля не должна затрагивать другие модули | ||
3. Существенные изменения поведения модуля должны быть легко определяемы | ||
> Существенные изменения поведения модуля - изменения, ломающие ожидания сущностей-пользователей модуля. | ||
|
||
Достичь этих целей позволяет введение публичного интерфейса (Public API), представляющего собой единую точку доступа к возможностям модуля и определяющего "контракт" взаимодействия модуля с внешним миром. | ||
|
||
<details> | ||
|
||
> Структура сущности должна иметь единую точку входа, предоставляющую публичный интерфейс | ||
|
||
|
||
```sh | ||
└── features/ # | ||
└── feature-name/ # Внутренняя структура фичи | ||
├── ui/ # | ||
├── model/ # | ||
├── {...}/ # | ||
└── index.ts # Энтрипоинт фичи с ее публичным API | ||
``` | ||
|
||
```js | ||
// index.ts | ||
export { Form as AuthForm } from "./ui" | ||
export * as authFormStore from "./model" | ||
``` | ||
</details> | ||
|
||
|
||
## Требования к публичному API | ||
|
||
1. Public API должен осуществлять **контроль доступа** к содержимому модуля | ||
- Другие части приложения могут использовать **только те сущности модуля, которые представлены в публичном интерфейсе** | ||
- Внутренности модуля за пределами публичного интерфейса доступны только самому модулю. | ||
|
||
AlexandrHoroshih marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<details> | ||
|
||
> **Плохо**: Идет обращение напрямую к внутренним частям модуля, минуя публичный интерфейс доступа - опасно, особенно при рефакторинге модуля | ||
```diff | ||
- import { Form } from "features/auth-form/components/view/form" | ||
- <Form ... /> | ||
``` | ||
|
||
> **Хорошо:** API заранее экспортирует только нужное и разрешенное, разработчику модуля теперь нужно думать только о том, чтобы не ломать Public API при рефакторинге | ||
```diff | ||
+ import { AuthForm } from "features/auth-form" | ||
+ <AuthForm ... /> | ||
``` | ||
|
||
</details> | ||
|
||
2. Public API должен быть **анти-хрупким** - устойчивым к изменениям внутри модуля | ||
- Ломающие изменения поведения модуля отражаются в изменении Public API | ||
> Например, изменение внутренней структуры не должно приводить к изменению Public API | ||
|
||
<details> | ||
|
||
> **Плохо:** перемещение или переименование этого компонента внутри фичи приведет к необходимости рефакторить импорты во всех местах использования компонента. | ||
```diff | ||
- import { Form } from "features/auth-form/ui/form" | ||
``` | ||
> **Хорошо:** интерфейс фичи не отображает её внутреннуюю структуру, внешние "пользователи" фичи не пострадают от перемещения или переименования компонента внутри фичи | ||
```diff | ||
+ import { AuthForm } from "features/auth-form" | ||
``` | ||
|
||
</details> | ||
|
||
3. Public API должен способствовать **легкой и гибкой интеграции** | ||
- Должен быть удобен для использования остальными частями приложения, в частности, решать проблему коллизии имен | ||
<details> | ||
|
||
> **Плохо:** будет коллизия имен | ||
```js | ||
// features/auth-form/index.ts | ||
export { Form } from "./ui" | ||
export * as store from "./model" | ||
|
||
// features/post-form/index.ts | ||
export { Form } from "./ui" | ||
export * as store from "./model" | ||
``` | ||
```diff | ||
- import { Form, store } from "features/auth-form" | ||
- import { Form, store } from "features/post-form" | ||
``` | ||
|
||
> **Хорошо:** коллизия решена на уровне интерфейса | ||
|
||
```js | ||
// features/auth-form/index.ts | ||
export { Form as AuthForm } from "./ui" | ||
export * as authFormStore from "./model" | ||
|
||
// features/post-form/index.ts | ||
export { Form as PostForm } from "./ui" | ||
export * as postFormStore from "./model" | ||
``` | ||
```diff | ||
+ import { AuthForm, authFormStore } from "features/auth-form" | ||
+ import { PostForm, postFormStore } from "features/post-form" | ||
``` | ||
--- | ||
> **Плохо:** неудобно писать, неудобно читать, "пользователь" фичи страдает | ||
```diff | ||
- import { storeActionUpdateUserDetails } from "features/auth-form" | ||
- dispatch(storeActionUpdateUserDetails(...)) | ||
``` | ||
|
||
> **Хорошо:** "пользователь" фичи получает доступ к нужным вещам итеративно и гибко | ||
```diff | ||
+ import { authFormStore } from "features/auth-form" | ||
+ dispatch(authFormStore.actions.updateUserDetails(...)) | ||
``` | ||
</details> | ||
|
||
- Коллизия имен должна решаться на уровне публичного интерфейса, а не реализации | ||
<details> | ||
|
||
> **Плохо:** коллизия имен решается на уровне реализации | ||
|
||
```js | ||
// features/auth-form/index.ts | ||
export { AuthForm } from "./ui" | ||
export { authFormActions, authFormReducer } from "model" | ||
|
||
// features/post-form/index.ts | ||
export { PostForm } from "./ui" | ||
export { postFormActions, postFormReducer } from "model" | ||
``` | ||
|
||
> **Хорошо:** коллизия имен решается на уровне интерфейса | ||
|
||
```js | ||
// features/auth-form/model.ts | ||
export { actions, reducer } | ||
// features/auth-form/index.ts | ||
export { Form as AuthForm } from "./ui" | ||
export * as authFormStore from "./model" | ||
|
||
// features/post-form/model.ts | ||
export { actions, reducer } | ||
// features/post-form/index.ts | ||
export { Form as PostForm } from "./ui" | ||
export * as postFormStore from "./model" | ||
``` | ||
</details> | ||
|
||
Выполнение этих требований позволяет свести взаимодействие с модулем к **выполнению публичного интерфейса-контракта** и, тем самым, достичь надежности и удобства в использовании модуля. | ||
|
||
## О реэкспортах | ||
В JavaScript публичный интерфейс модуля создается с помощью реэкспорта сущностей изнутри модуля в `index.js` файле: | ||
|
||
```js | ||
// index.ts | ||
export { Form as AuthForm } from "./ui" | ||
export * as authStore from "./model" | ||
``` | ||
|
||
Файлы-реэкспорты имеют ряд недостатков: | ||
|
||
1. В большинстве популярных бандлеров из-за реэкспортов код-сплиттинг работает хуже, т.к. [tree-shaking](https://webpack.js.org/guides/tree-shaking/) при таком подходе может безопасно отбросить только модуль целиком, но не его часть. | ||
> Например, импорт `authStore` в модели страницы приведет к попаданию компонента `AuthForm` в чанк этой страницы, даже если этот компонент там не используется. | ||
|
||
2. Как следствие, инициализация чанка становится дороже, т.к. браузер должен обработать все модули в нем, в том числе и те, что попали в бандл "за компанию" | ||
|
||
**Возможные пути решения** | ||
- `webpack` позволяет отметить файлы-реэкспорты как [**side effects free**](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) - это разрешает `webpack` использовать более агрессивные оптимизации при работе с таким файлом | ||
|
||
## 📑 См. также | ||
- [*Обсуждение* "Public API абстракции"](https://github.com/feature-sliced/wiki/discussions/41) | ||
- [Принципы **SOLID**](https://ru.wikipedia.org/wiki/SOLID) | ||
- [Паттерны **GRASP**](https://ru.wikipedia.org/wiki/GRASP) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.