Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Feature Sliced review #7

Closed
azinit opened this issue Nov 11, 2021 · 15 comments
Closed

Feature Sliced review #7

azinit opened this issue Nov 11, 2021 · 15 comments

Comments

@azinit
Copy link

azinit commented Nov 11, 2021

Опишу здесь основные моменты, надеюсь поможет 🍰

UPD: Буду офк накидывать чисто со стороны проектирования по FS

PR не оч подходил, т.к. нужно еще отсылаться и на не задетые диффом файлы

@azinit
Copy link
Author

azinit commented Nov 11, 2021

Начну с того, что "хорошо"))

praise: Глобальные и инициализирующие стили - по канонам 🚀

@import './styles/variables.css';
@import './styles/reset.css';

@azinit
Copy link
Author

azinit commented Nov 11, 2021

praise: Выделять сразу базовый shared UIKit - тоже топ

export { Button } from './button';
export { Title } from './title';
export { Paragraph } from './paragraph';
export { Loader } from './loader';
export { Output } from './output';
export { InputNumber } from './input-number';
export { Form } from './form';

@azinit
Copy link
Author

azinit commented Nov 11, 2021

praise: Тож отлично разделять явно апишки ресурсов, которые используются

export * as mashapeApi from './mashape';

@azinit
Copy link
Author

azinit commented Nov 11, 2021

praise: Тоже неплохо обыграл View и ViewModel )

Чтоб компонент не разбухал

import { useCopyClipboard } from './lib';
import styled from 'styled-components';
import copy from './icon/copy.svg';
import checkmark from './icon/checkmark.svg';
export const CopyToClipboard = ({ data }) => {
const [isCopied, setCopied] = useCopyClipboard(data, {
successDuration: 1000,
});
return (
<StyledCopyToClipboard onClick={setCopied}>
<img src={isCopied ? checkmark : copy} alt="Copy." />
</StyledCopyToClipboard>
);
};

@azinit
Copy link
Author

azinit commented Nov 11, 2021

praise: Отлично, что сразу есть понимание, что надо заводить сущность, даже если там хранится только модель (и даже если используется в одном месте)

import { createStore, createEffect } from 'effector';
import { mashapeApi } from '../../shared/api/';
export const getTextContentFx = createEffect((num = 1) => mashapeApi.getTextContent(num));
export const $textContent = createStore([])
.on(getTextContentFx.doneData, (_, data) => data);

@azinit
Copy link
Author

azinit commented Nov 11, 2021

Теперь к toImprove)

issue(critical): Сейчас "пойдет", но позже роутинг должен явно декларироваться)

suggestion(critical): Даж если роутера нет и страница по факту одна - то желательно этот момент отдельно задекларировать. Чтоб когда появятся доп. страницы - не было больно все это мигрировать (ну и чтобы в app логика не разбухала)

Можно прям на уроне pages/index.tsx реэкспортить обертку, где напрямую home юзается. Но для аппа это будет обычный роутинг, который потом всегда будет легко поменять

import { Home } from '../pages/home';
export const App = () => (
<Home />

@azinit
Copy link
Author

azinit commented Nov 11, 2021

issue(critical): Так у нас App или HomePage уровень абстракции?)

suggestion(critical): Тут лучше бы явно определить разметку и стили app в соответствующем слое, а для страниц оставить их уровень реализации

thoughts:
(не говорю вставлять сразу хедер/футер в апп - но это явно не должно описываться в странице - потом будет больно добавлять новые иначе)

/* App styles */
.app {
padding: 80px 10px 0;
margin: 0 auto;
max-width: 880px;
}
@media (max-width: 550px) {
.app {
padding-top: 30px;
}

@azinit
Copy link
Author

azinit commented Nov 11, 2021

issue(critical): Можно заметить и по стилям и по разметке - что там уж слишком разбухшая логика)

thoughts: При этом тот же CopyToClipboard почему-то вынесен в отдельную фичу, но не остальное)

suggestion(critical): Щас не особо больно, но по-хорошему бы декомпозировать это все дело:

  • features/generate-text - форма для генерации lorem-заглушки со всеми нужными инпутами
    Пока кажется еще сильней ее дробить не имеет смысл, разве что CopyToClipboard можно действительно отдельно подержать (хотя это возможно вообще слой shared).
    Также можно временно перенести стор связанный с текстом ближе к фиче, а там посмотреть - насколько стоит делать его глобальным (локальный стор всегда предпочтительней глобального, т.к. такое проще дебажить/развивать - хотя это мы уже в датафлоу уходим)
  • shared/ui/empty - обычно для этого даж отдельный компонент выделяют (можно глянуть здесь)

thoughts: Вот. И даж при такой декомпозиции - у тебя уже фича берет на себя значительную часть ответственности. А страница по сути лишь композит и дополняет нужной логикой на своем уровне

<Form onSubmit={handleSubmit} $marginTop="31px" $grid $gap="12px">
<InputNumber
labelText="Paragraphs:"
value={input}
onChange={(e) => setInput(e.target.value)}
min={1}
max={999}
$color="var(--color-blue-darker)"
/>
<Button
type="submit"
$color="#FFFFFF"
$background="var(--color-blue-medium)"
$backgroundHover="var(--color-blue-lightest)"
$backgroundActive="var(--color-blue-darker)"
>
Generate
</Button>
</Form>
<Output $padding="37px" $marginTop="24px" $height="550px" $gap="32px" $center>
<CopyToClipboard data={textContentJoined} />

/* Form */
.form {
display: grid;
grid-auto-flow: column;
justify-content: center;
align-items: center;
grid-gap: 12px;
margin-top: 31px;
}
@media (max-width: 362px) {
.form {
grid-auto-flow: row;
}
}
/* Empty */
.empty {
color: #617D98;
text-align: center;

@azinit
Copy link
Author

azinit commented Nov 11, 2021

nitpic(non-critical): Некритично, но для консистнетности и конвенциональности - папку с иконками назвал бы assets

import copy from './icon/copy.svg';
import checkmark from './icon/checkmark.svg';

thoughts: Ну и да - тож надо следить за сложностью папки, чтоб было считываемо какая логика к какому сегменту относится

Пока норм, но потом по мере сложности фичи - понадобится директории выделять почти наверняка
image

@azinit
Copy link
Author

azinit commented Nov 11, 2021

thoughts: На самом деле большой вопрос как уже говорил - является ли это фичой... Пока можно оставить, но что-то мне подсказывает, что придется в shared/lib положить, т.к. логика достаточно близкая к веб-платформе)

thoughts: Кста да - чтобы это попало в фичу генерации текста - не обязательно напрямую дергать - можно как раз на уроне страницы композить как считаешь нужным
(а фича чтобы просто имела пропс - через который можно передать что угодно)

import { useCopyClipboard } from './lib';
import styled from 'styled-components';
import copy from './icon/copy.svg';
import checkmark from './icon/checkmark.svg';
export const CopyToClipboard = ({ data }) => {
const [isCopied, setCopied] = useCopyClipboard(data, {
successDuration: 1000,
});
return (
<StyledCopyToClipboard onClick={setCopied}>
<img src={isCopied ? checkmark : copy} alt="Copy." />
</StyledCopyToClipboard>
);
};

import { useState, useEffect } from 'react';
import copy from 'copy-to-clipboard';
export const useCopyClipboard = (
text,
options,
) => {
const [isCopied, setIsCopied] = useState(false);
const successDuration = options?.successDuration;

@azinit
Copy link
Author

azinit commented Nov 11, 2021

nitpick(non-critical): Пока некритично, но прям цепляется глаз за относительные импорты слоев)

Потом это может неплохо так выстрелить в колено, поэтому для импорта внешних ресурсов лучше сразу использовать абсолютные импорты

См. подробнее здесь

import { CopyToClipboard } from '../../features/copy-to-clipboard';
import {
InputNumber, Button, Title, Output, Form, Loader, Paragraph,
} from '../../shared/ui';
import { $textContent, $textContentJoined, getTextContentFx } from '../../entities/text';

@azinit
Copy link
Author

azinit commented Nov 11, 2021

suggestion(non-critical): Пока норм, и в целом оправдано с CSSinJS что каждый компонент единым файлом, а не директорией.

Но тем не менее рано или поздно компоненты эти начнут друг друга дергать, т.к. даже UIKit бывает разной сложности

Select,Button => ComboBox

Можно глянуть атомик и по нему потом раскладывать компоненты, чтобы они друг друга не кросс импортили жестко

image

@azinit
Copy link
Author

azinit commented Nov 11, 2021

За остальное глаз не зацепился вродь, но и у проекта сложность не шибко большая на старте)

На самом деле еще стоит понимать - что концепции эти часто мешает применять уже существующее понимание принципов (GRASP, DRY) + нехватка контекста, чтобы правильно применять концепции FS (критерии слоев)

Но уже оч круто, что есть понимание - что можно дробить на слои логику, а не держать "на подольше" рядом с местом использования. Это рано или поздно выстреливает, особенно когда проект больше 1 человека мейнтейнит)

@azinit
Copy link
Author

azinit commented Nov 11, 2021

Если все эти правки внести, то вполне можно добавить репе лейбл feature-sliced и можно будет к нам в примеры FS проектов занести спокойно, если есть желание)

@azinit
Copy link
Author

azinit commented Nov 11, 2021

Если останутся вопросы - пингуй)

Repository owner locked and limited conversation to collaborators Nov 12, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants