fullstack-development / developers-roadmap

How to learn front-end or back-end development
1.18k stars 265 forks source link

Выпилить вопросы про branding/flavoring #143

Open Znack opened 4 years ago

Znack commented 4 years ago

Начало обсуждения тут: https://github.com/fullstack-development/developers-roadmap/pull/140#discussion_r365026763

Znack commented 4 years ago

Остановились на этом:

Окей, а как предлагаешь на уровне типов ограничивать конструирование данных, требующих специальных правил, например, как UUID или Email? При этом чтобы только через смарт конструкторы можно было такого рода данные конструировать, где зашиты все нужные проверки. Я бы хотел сигнатуру создания юзера иметь получая типы Email, Phone и UUID, а не string, string, string на входе :)

@sk1e ждём твоего выхода :)

sk1e commented 4 years ago

Остановились на этом:

Окей, а как предлагаешь на уровне типов ограничивать конструирование данных, требующих специальных правил, например, как UUID или Email? При этом чтобы только через смарт конструкторы можно было такого рода данные конструировать, где зашиты все нужные проверки. Я бы хотел сигнатуру создания юзера иметь получая типы Email, Phone и UUID, а не string, string, string на входе :)

@sk1e ждём твоего выхода :)

есть какой-то код? Без примеров непонятно

Znack commented 4 years ago

Чтобы номинативную типизацию прикрутить к какому-нибудь типу и ограничить возможность конструирования значения — форсить использовать смарт конструкторы. Например, для UUID. Чтобы юзер не мог UUID собрать никак кроме как через спец конструкторы, а у них уже можно на типах отразить, что не всякая строка — валидный UUID.

function uuidFromString(x: string): Option<UUID> {}
function uuidFromNumbers(a: number, b: number, c: number, d: number): UUID {}

Обрати внимание, что из не факт, что UUID полчится, а из 4 чисел — точно можно однозначно сделать, поэтому во второй функции нет Option.

Теперь когда мы на уровне типов гарантировали, что все данные типа UUID являются валидными, мы можем это использовать в остальных функциях нашей предметки:

createUser(phone: Phone, email: Email, uuid: UUID): User {}

А иначе мы бы имели тип

createUser(phone: string, email: string, uuid: string): User {}

Это и повышает удобство использования типов, это помогает мне легче ориентировать внутри функции createUser, я например, точно могу знать не только что телефон валиден, так ещё и уверен, в каком формате этот телефон (со знаком + в начале или без и тд)

sk1e commented 4 years ago

Зачем нам на фронте собирать uuid?

sk1e commented 4 years ago

На фронте телефон и имейл с 99% будет вводиться пользователем, валидироваться внешним валидатором. Как туда интегрировать смарт-конструкторы?

Такие даннные из формы просто кладутся в запрос на сервер. Больше мы с этими сущностями не работаем. Попытка получать гарантии формата на уровне типа выглядит переусложнением для таких случаев. Согласен? Есть какие-нибудь другие кейсы?

Znack commented 4 years ago

Это частая практика. как минимум идемпотентности добиться. Удобно для управления сложными формами создания, как на Птичке, мы там активно UUID на клиентской стороне пользуемся

Znack commented 4 years ago

На фронте телефон и имейл с 99% будет вводиться пользователем, валидироваться внешним валидатором

Что значит внешним валидатором?

Znack commented 4 years ago

Почему 99%? И разве это отменяет факта валидации на клиенте?

Znack commented 4 years ago

Такие даннные из формы просто кладутся в запрос на сервер. Больше мы с этими сущностями не работаем.

Если у тебя тонкий клиент, то ок. Я не про такие случаи. Как быть в случае сложных и толстых клиентов?

sk1e commented 4 years ago

На фронте телефон и имейл с 99% будет вводиться пользователем, валидироваться внешним валидатором

Что значит внешним валидатором?

валидатором с определённой сигнатурой, которая нужна библиотеке которая работает с формами

sk1e commented 4 years ago

Почему 99%?

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

И разве это отменяет факта валидации на клиенте?

Нет, проблема валидации тут в невозможности интегрировать смарт-конструторы в библиотеки по работе с формами. Если они не свои конечно.

sk1e commented 4 years ago

Такие даннные из формы просто кладутся в запрос на сервер. Больше мы с этими сущностями не работаем.

Если у тебя тонкий клиент, то ок. Я не про такие случаи. Как быть в случае сложных и толстых клиентов?

Ок, допускаю кейсы, где это даст ощутимую пользу, но что если в какой-то момент нам потребуется рекорд? И Map нам не подойдёт. Будешь повторно вызывать смарт-конструктор при каждом извлечении элемента из рекорда?

Znack commented 4 years ago

В этот очень редкий случай можно как минимум prop использовать для получения свойства. Здесь проблемы от костыльного использования рекордов однозначно не перевешивают преимуществ от возможности более выразительных типов

sk1e commented 4 years ago

В этот очень редкий случай можно как минимум prop использовать для получения свойства. Здесь проблемы от костыльного использования рекордов однозначно не перевешивают преимуществ от возможности более выразительных типов

очень редкий

Мне кажется ты необъективен) Если у тебя толстый клиент который обильно использует эту модель, то вероятность наличия состояния отражения из этой модели во что-нибудь - наоборот, довольно высокая.

prop

что это такое?

Znack commented 4 years ago

Не понял совсем вот этого, можешь подробнее?

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

И тем более не понял почему вот это :)

Мне кажется ты необъективен)

prop что это такое?

Это из примера от @in19farkt про то, что доступ к ключу рекорда нормально в TS работает, если не напрямую через obj[key] обращаться, а написать свою функцию prop(obj, key), которая то же самое делает

sk1e commented 4 years ago

Не понял совсем вот этого, можешь подробнее?

ID, номер телефона, почта, - всё это свойства моделей которые могут выступать первичными критериями поиска. Если в приложении есть некоторая динамическая коллекция, которая может всячески редактироваться, сортироваться, то оформлять состояние элементов этой коллекции, как правило, лучше всего через рекорд, где ключами будут такие первичные критерии поиска. Клиент который значительно работает с моделями с такими свойствами, почти наверняка будет иметь такие коллекции в виде таблиц или разного рода списков.

Итого, если допустить вероятность наличия коллекции и вероятность оформления её состояния через рекорд за 0.9, то вероятность необходимости рекорда будет равна 0.81 в случае свойств-ключей. Допустим, это половина всех кейсов номинативной типизации, то результативная вероятность необходимости рекордов будет равна 0.4, а это не то значение которое можно выразить через «очень редко».

Znack commented 4 years ago

Очень субъективные оценки, Серег :) Мне вот не кажется, что если у меня сложный фронт, то я именно по такого рода ключам буду рекорды составлять. Но в целом готов согласиться, что "очень редко" тоже слишком категорично было сказано :) Давай короче сойдёмся на том, что такие случаи бывают, но и не в 100%, поэтому для толстых клиентов в целом есть вероятность, что брендинг будет более полезен, чем костылен. А как минимум это уже хороший повод знать про эту тему. Согласен, что знать надо с учетом возможных минусов и костылей

sk1e commented 4 years ago

Ещё не раскрыты сомнительные моменты с формами . На фронте, форма - источник ввода, требующий валидации в подавляющем большинстве случаев.

  1. Зачем нам хранить информацию о валидации за пределами этой формы? Зачем защищаться на уровне типов от передачи пирожков вместо ботинок в функцию processBoots(bootsID: string)? При хорошем именовании функций/переменных класс таких ошибок должен отсутствовать.
  2. Как интегрировать смарт-конструкторы в эти библиотеки? Нам при сабмите нужно получить уже затипизированные свойства. Не вижу такой возможности, если у нас не будет своей такой либы (хотя есть клёвая идея запилить более крутой аналог redux-form на микрофичах и туда можно это вставить)
Znack commented 4 years ago

Что ты имеешь ввиду под формами? Вьюху с компонентами-инпутами и тд?

sk1e commented 4 years ago

Ага

Znack commented 4 years ago

Ну для вьюх, реакт-компонент и тд эта тема в целом менее релевантна. Я поэтому и говорю про толстый клиент, потому что там есть отдельный слой чистой доменной логики и как раз для него это всё релевантно, именно там я пытаюсь по максимуму в моделях все выражать на уровне типов и именно там мне нужна выразительность этих типов.

in19farkt commented 4 years ago

Нам при сабмите нужно получить уже затипизированные свойства.

тут не согласен, при сабмите мы должны получать сырой пользовательский ввод, и если нужно использовать смарт-конструкторы. Т.к. мы говорим, что смарт-конструктор не всякий сырой ввод сможет преобразовать в нужное нам значение (например, UUID из строки не всегда может получиться), то мы не можем в стейте формы хранить прошедшие валидацию данные вместо сырого ввода пользователя.

in19farkt commented 4 years ago

Но если у смарт-конструктора нет никаких правил и любая введенная юзером строка подходит, то можно легко написать Field (как минимум, в redux-form и в final-form), который на юзерский ввод будет применять к новому значению смарт-конструктор и полученное значение отправлять в стейт формы.

sk1e commented 4 years ago

Нам при сабмите нужно получить уже затипизированные свойства.

тут не согласен, при сабмите мы должны получать сырой пользовательский ввод, и если нужно использовать смарт-конструкторы. Т.к. мы говорим, что смарт-конструктор не всякий сырой ввод сможет преобразовать в нужное нам значение (например, UUID из строки не всегда может получиться), то мы не можем в стейте формы хранить прошедшие валидацию данные вместо сырого ввода пользователя.

Я имел в виду что после сабмита при успешной валидации библиотека должна предоставить модель с уже затипизированными свойствами. В стейте формы разумеется не получится держать номинативно затипизированные свойства

sk1e commented 4 years ago

Но если у смарт-конструктора нет никаких правил и любая введенная юзером строка подходит, то можно легко написать Field (как минимум, в redux-form и в final-form), который на юзерский ввод будет применять к новому значению смарт-конструктор и полученное значение отправлять в стейт формы.

Ну вот, ограничения и повторная валидация в конструкторе и в форме

in19farkt commented 4 years ago

блин, я вообще не понимаю как ты связал пользовательский ввод и конечное значение :) зачем нам второе, если ввод еще не закончен и в инпутах что-то не валидное?

in19farkt commented 4 years ago

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

sk1e commented 4 years ago

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

ты правильно говоришь, я разве где-то утверждал обратное?

in19farkt commented 4 years ago

тут 2 пункт не об этом? https://github.com/fullstack-development/developers-roadmap/issues/143#issuecomment-574477524

in19farkt commented 4 years ago

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

Я имел в виду что после сабмита при успешной валидации библиотека должна предоставить модель с уже затипизированными свойствами. В стейте формы разумеется не получится держать номинативно затипизированные свойства

Znack commented 4 years ago

Я имел в виду что после сабмита при успешной валидации библиотека должна предоставить модель с уже затипизированными свойствами.

Ну вот сильное заявление, почему "должна"? Мне кажется, норм, что вьюха собирает данные и отдаёт в своём сыром виде, ты потом это мапаешь в уже в доменные модели и передаешь дальше по слоям приложения куда-нибудь ближе к ядру.