Хорошая тема! Лайк как обычно. Кстати, за ссылку на миру отдельное спасибо. Не сразу её увидел, оказалось интересно самому смотреть и слушать видео на фоне.
Очень интересная тема! Некоторое время назад пытался найти подобную информацию, но удалось найти лишь одну толковую статью, и то довольно поверхностную. Помимо тех способов, о которых упомянул Евгений в видео, в этой статье предлагался еще один - создавать базовые типы, основываясь на таблицах в базе данных, с теми же самыми полями. Можно сказать, что это будут shared типы, используя терминологию видео. Один из комментаторов привел такое определение: "Чтоб если выкинуть функцию/сущность/фичу - она не оставляла мусора". Полностью согласен, тем более, что я самостоятельно пришел к такой же идее. ) Но, к сожалению, она не покрывает всех возможных случаев. На данный момент, у меня в голове сложилась картина, что есть всего 2 вида типов: 1) продуцируемые какой-то сущностью (компонент, фича, модуль и т.п.). Если из кода будет удалена эта сущность, то соответствующие типы тоже должны будут удалены, так как больше не будут использоваться. Такие типы кладем как можно ближе к сущности; 2) базовые или вспомогательные типы, используемые по всему приложению (User, Product, WithClassName и т.п.). Такие типы кладем в папочку на глобальном уровне с разумным дроблением на файлы. И вроде как основная сложность на этапе создания нового типа - это определить к какому из двух вариантов он относится и положить в нужное место.
Лично я против всего глобального и за всё локальное. Как предвыборный слоган звучит) Если уж что то и глобальное, оно должно быть максимально абстрактным (по типу WithClassName)
Термины использование и реализация так же легко отличить друг от друга, как связность и связанность. :) На данный момент, стараюсь держать типы рядом с реализацией. Так сказать, увеличиваю связность. Чтоб если выкинуть функцию/сущность/фичу - она не оставляла мусора. Это касается не только типов, но и всего остального. Насчет того, чтобы использовать тип из api в другом модуле/фиче/домене, то обязательно ли делать фичовый тип: копипастить или делать адаптер какой-нибудь? Это может быть полезно, если переиспользовать модуль в другом проекте, ну и для семантики в этом проекте, ведь Employee звучит более определенно, чем User. Это если Employee и User имеют одинаковый тип. Если семантика и переиспользование не важны, и если Employee точно-точно не будет расширятся (риск - благородное дело :), то можно и User попробовать использовать. Ведь если поменяется User, то все равно менять использование в компонентах, независимо, используется User или Employee. Если Employee расширяет User (полностью или частично), то тут без вариантов, и остается вопрос - просто тупая копипаста, или таки объединять с User (pick, omit или целиком). Я за второй вариант. В целом, надежнее, конечно, будет использовать Employee. Т.к. скорее всего, судя по названию, различия в типе с User будут (сразу или со временем). В общем-то да, это адаптер для User.
Про копирование типов с одной стороны ты прав, это сильно упростит жизнь. Но со временем эти типы станут большими, а использование таких типов будет во многих компонентах, тут тоже надо будет что-то выдумывать. И самое главное да, бизнес компоненты не должны импортировать типы (и вообще всё что угодно) в друг-друга. Иначе циклическая зависимость импортов это дело времени, а обнаружить такое может быть больно и долго.
Вообще если правильно разделить на домены и использовать ISP эти типы будут не такими большими. Забавно, что сильно что то думать надо в попытках переиспользлвать типы При копипасте у тебя размер типа соответствует размеру функциональности которую он описывает. И это больше проблема разделения кода
@@jgkdmdevienjjgg8866 Да, это ок. Но в таком случае надо быть на 100% уверенным что только у типов может быть зависимости, а не у бизнес кода. Писать кастомное правило линта под это? Ну фиг знает, зачем? Если можно просто разделить структуру более грамотно, а линтом отменить зоны, из каких папок в какие нельзя импортировать. Будет легко и расширяемо. Такие слои например используют чуваки из makerkit или более сложно в FSD
На тему DDD в пет проектах мучался некоторое время доводя до абсурда разделение типов, между бд, сервисным слоем, ui и пришел к выводу что так кодить очень сложно. Слишком много маппингов получается. И все равно типы из бд протекут в ui. И для себя я решил что тут нужен какой-то разумный трейдоф. Но минимальное разделение нужно, постоянно вижу что никто не парится на фронте насчет разделения доменов и просто переиспользует втупую типы из апи вообще везде - в редуксе, мутируют их направо налево, добавляют доп. свойства, полная порнография. И так повсеместно.
Вообще моя концепция далека до DDD и это касается только явно переиспользуемых типов. У нас все-таки структурная типизация и можно использовать api в ui и это не так страшно до тех пор пока не нужно где то поставить границу по DIP и тут это правило начинает работать Это тот трейдоф который я для себя нашел: не упарываться в DIP
Мб просто изучить, что такое dto и типы/интерфейсы заиграют новыми красками? Если что - это совсем не близко к ddd. Попытка приведение того, что есть - в о.контекст - тоже не удалась - стоит хотя бы желто/зеленую обзорно почитать (200стр осилить можно), не говоря уже о следующих нормальных книгах. Если что, то есть там у эванса в конце книги и вернона в начале - как раскидывать единообразно модули (определение что это там есть для разных языков), чтобы единый язык дополнял первоначальный словарь. Ts тут не исключение, или хотя бы применяя концепцию портов/адаптеров (хексогональную) спокойно укладывается все это в ддд. Но в рамках ооп, а не фукциональщины, о почему так - читаем книжку про ддд ))) Пс вернись к нексту ))
Женя, подскажи, а норм делать так в компоненте? У меня есть тип (точнее интрфейс), допустим User, который вынесен отдельно в папке interfaces, но мне в компоненте так же нужно помимо полей из интерфейса передавать в пропсах необязательный className (так как пишу по БЭМ и часто есть необходимость делать микс, то есть прокидывать класс элемента). И я делаю в компоненте следующее: interface Props extends User { className?: string; } и уже в компоненте достаю пропсы типизируя через созданный локально interface Props, а не User
Не советую так делать. Вообще не советую наследовать пропсы от чело либо Если так делать то лучше сделать interface Props { user: User; className?: string; } И ещё такой момент, что если используется не весь User а только чаcть полей, лучше закопипастить только те которые используешь interface Props { className?: string user: { id: string ,name: string} } Иначе идёт нарушение ISP
А почему не назвать файл как-то типа common.ts (ну или по названию области ответственности определенной), вместо types.ts? А то получается что название файла в соответствии с техническим аспектом а не общим смыслом. Просто по такой аналогии можно все функции класть в functions.ts, константы в constants.ts, классы в classes.ts и так далее. В этом смысле типы вообще ничем не отличаются от других сущностей в коде. Все эти принципы относятся так же и к другим сущностям.
А в чём проблема файлов functions.ts, constants.ts и т.д.? В разумных пределах, конечно. Лично я частенько использую constants и utils файлы/папки. А вот увидеть в common.ts только типы лично я точно не ожидаю.
@@BOCbMOU Так только типы туда и не нужно ложить. Вообще разделять на типы и не типы не очень понятно зачем. Тип это такая же сущность в коде как класс или функция. А проблема примерно такая же как если переменную называть variable
Да важное замечание.Если типов много то types.ts можно заменить на папку, с названиями соответствующим содержимому На низком уровне называть файлы по техническому предназначению норм. Это условно папки проекта называть types components дич полная, но внутри условного домена это нормальное разделение Альтернатива есть помещать это в что то типо model но тут уже от особенностей проекта зависит
Хорошая тема! Лайк как обычно. Кстати, за ссылку на миру отдельное спасибо. Не сразу её увидел, оказалось интересно самому смотреть и слушать видео на фоне.
Отличное видео, благодарю!
Очень интересная тема! Некоторое время назад пытался найти подобную информацию, но удалось найти лишь одну толковую статью, и то довольно поверхностную. Помимо тех способов, о которых упомянул Евгений в видео, в этой статье предлагался еще один - создавать базовые типы, основываясь на таблицах в базе данных, с теми же самыми полями. Можно сказать, что это будут shared типы, используя терминологию видео.
Один из комментаторов привел такое определение: "Чтоб если выкинуть функцию/сущность/фичу - она не оставляла мусора". Полностью согласен, тем более, что я самостоятельно пришел к такой же идее. ) Но, к сожалению, она не покрывает всех возможных случаев.
На данный момент, у меня в голове сложилась картина, что есть всего 2 вида типов:
1) продуцируемые какой-то сущностью (компонент, фича, модуль и т.п.). Если из кода будет удалена эта сущность, то соответствующие типы тоже должны будут удалены, так как больше не будут использоваться. Такие типы кладем как можно ближе к сущности;
2) базовые или вспомогательные типы, используемые по всему приложению (User, Product, WithClassName и т.п.). Такие типы кладем в папочку на глобальном уровне с разумным дроблением на файлы.
И вроде как основная сложность на этапе создания нового типа - это определить к какому из двух вариантов он относится и положить в нужное место.
Лично я против всего глобального и за всё локальное. Как предвыборный слоган звучит)
Если уж что то и глобальное, оно должно быть максимально абстрактным (по типу WithClassName)
Можно ещё подобных видео? Очень понравились))) Понимаю, что у вас на это времени не очень, но всё же, хотелось бы узнать ещё полезного, побольше!!!
Термины использование и реализация так же легко отличить друг от друга, как связность и связанность. :) На данный момент, стараюсь держать типы рядом с реализацией. Так сказать, увеличиваю связность. Чтоб если выкинуть функцию/сущность/фичу - она не оставляла мусора. Это касается не только типов, но и всего остального.
Насчет того, чтобы использовать тип из api в другом модуле/фиче/домене, то обязательно ли делать фичовый тип: копипастить или делать адаптер какой-нибудь? Это может быть полезно, если переиспользовать модуль в другом проекте, ну и для семантики в этом проекте, ведь Employee звучит более определенно, чем User. Это если Employee и User имеют одинаковый тип. Если семантика и переиспользование не важны, и если Employee точно-точно не будет расширятся (риск - благородное дело :), то можно и User попробовать использовать. Ведь если поменяется User, то все равно менять использование в компонентах, независимо, используется User или Employee.
Если Employee расширяет User (полностью или частично), то тут без вариантов, и остается вопрос - просто тупая копипаста, или таки объединять с User (pick, omit или целиком). Я за второй вариант.
В целом, надежнее, конечно, будет использовать Employee. Т.к. скорее всего, судя по названию, различия в типе с User будут (сразу или со временем). В общем-то да, это адаптер для User.
Хорошее видео, но заметила что вас тихо слышно, мне приходится всё выкручивать на максимум, чтобы слышать более-менее
Про копирование типов с одной стороны ты прав, это сильно упростит жизнь. Но со временем эти типы станут большими, а использование таких типов будет во многих компонентах, тут тоже надо будет что-то выдумывать.
И самое главное да, бизнес компоненты не должны импортировать типы (и вообще всё что угодно) в друг-друга. Иначе циклическая зависимость импортов это дело времени, а обнаружить такое может быть больно и долго.
Кстати циклические импорты типов скорее всего будут разруливаться сами если делать через import type, т.к. в этом случае не запускаются сайд эффекты
Вообще если правильно разделить на домены и использовать ISP эти типы будут не такими большими.
Забавно, что сильно что то думать надо в попытках переиспользлвать типы
При копипасте у тебя размер типа соответствует размеру функциональности которую он описывает. И это больше проблема разделения кода
@@jgkdmdevienjjgg8866 Да, это ок. Но в таком случае надо быть на 100% уверенным что только у типов может быть зависимости, а не у бизнес кода. Писать кастомное правило линта под это? Ну фиг знает, зачем? Если можно просто разделить структуру более грамотно, а линтом отменить зоны, из каких папок в какие нельзя импортировать. Будет легко и расширяемо. Такие слои например используют чуваки из makerkit или более сложно в FSD
На тему DDD в пет проектах мучался некоторое время доводя до абсурда разделение типов, между бд, сервисным слоем, ui и пришел к выводу что так кодить очень сложно. Слишком много маппингов получается. И все равно типы из бд протекут в ui. И для себя я решил что тут нужен какой-то разумный трейдоф. Но минимальное разделение нужно, постоянно вижу что никто не парится на фронте насчет разделения доменов и просто переиспользует втупую типы из апи вообще везде - в редуксе, мутируют их направо налево, добавляют доп. свойства, полная порнография. И так повсеместно.
Вообще моя концепция далека до DDD и это касается только явно переиспользуемых типов. У нас все-таки структурная типизация и можно использовать api в ui и это не так страшно до тех пор пока не нужно где то поставить границу по DIP и тут это правило начинает работать
Это тот трейдоф который я для себя нашел: не упарываться в DIP
Мб просто изучить, что такое dto и типы/интерфейсы заиграют новыми красками? Если что - это совсем не близко к ddd. Попытка приведение того, что есть - в о.контекст - тоже не удалась - стоит хотя бы желто/зеленую обзорно почитать (200стр осилить можно), не говоря уже о следующих нормальных книгах. Если что, то есть там у эванса в конце книги и вернона в начале - как раскидывать единообразно модули (определение что это там есть для разных языков), чтобы единый язык дополнял первоначальный словарь. Ts тут не исключение, или хотя бы применяя концепцию портов/адаптеров (хексогональную) спокойно укладывается все это в ддд. Но в рамках ооп, а не фукциональщины, о почему так - читаем книжку про ддд )))
Пс вернись к нексту ))
Женя, подскажи, а норм делать так в компоненте? У меня есть тип (точнее интрфейс), допустим User, который вынесен отдельно в папке interfaces, но мне в компоненте так же нужно помимо полей из интерфейса передавать в пропсах необязательный className (так как пишу по БЭМ и часто есть необходимость делать микс, то есть прокидывать класс элемента). И я делаю в компоненте следующее: interface Props extends User { className?: string; } и уже в компоненте достаю пропсы типизируя через созданный локально interface Props, а не User
Не советую так делать. Вообще не советую наследовать пропсы от чело либо
Если так делать то лучше сделать
interface Props {
user: User;
className?: string;
}
И ещё такой момент, что если используется не весь User а только чаcть полей, лучше закопипастить только те которые используешь
interface Props {
className?: string
user: { id: string ,name: string}
}
Иначе идёт нарушение ISP
А почему не назвать файл как-то типа common.ts (ну или по названию области ответственности определенной), вместо types.ts? А то получается что название файла в соответствии с техническим аспектом а не общим смыслом. Просто по такой аналогии можно все функции класть в functions.ts, константы в constants.ts, классы в classes.ts и так далее. В этом смысле типы вообще ничем не отличаются от других сущностей в коде. Все эти принципы относятся так же и к другим сущностям.
А в чём проблема файлов functions.ts, constants.ts и т.д.? В разумных пределах, конечно. Лично я частенько использую constants и utils файлы/папки.
А вот увидеть в common.ts только типы лично я точно не ожидаю.
@@BOCbMOU Так только типы туда и не нужно ложить. Вообще разделять на типы и не типы не очень понятно зачем. Тип это такая же сущность в коде как класс или функция. А проблема примерно такая же как если переменную называть variable
Да важное замечание.Если типов много то types.ts можно заменить на папку, с названиями соответствующим содержимому
На низком уровне называть файлы по техническому предназначению норм. Это условно папки проекта называть types components дич полная, но внутри условного домена это нормальное разделение
Альтернатива есть помещать это в что то типо model но тут уже от особенностей проекта зависит
IUser... Что за кринжовый нейминг, просто User