luchob/softuni-sep-2023

Въпроси

Closed this issue · 10 comments

Малко въпроси които съм събрал до сега.

    • Когато валидиране входа, освен на сървъра, трябва ли да имаме и анотациите на базата или ако имаме на сървъра в базата не е задължително?
    • Когато направим модел-атрибут, при всяка заявка ли се създава нов модел?
    • Когато джава скрипт, прави проверка на всяка секунда примерно за да визуализира съобщения в чат, това до колко забавя приложението или страницата. Днес установих, че когато използвам големи файлове за картинки страницата зарежда а картинките започват бавно бавно да се пълнят и го няма. Трябва да ги чакам, как да се справим с този проблем?
    • За крайния проект, може ли да използваме различни мапери, за да се покаже че знаем повече от един или е много лоша практика и няма да се приеме добре?
    • Код ревю, може ли да се направи на проекта ми (мобилеле)до тук?
luchob commented

Леле ще почвам да ги отмятам един по един в следващите дни :-)

Номер 1 - мисля, че е удачно и редно да има валидации и на двете места - и в ДТО и в entities. За задължително - не е, то ще си работи и без валидации изобщо ;-) Да подкрепя с някакъв разбираем пример.

Да речем имаме ентити с 5 полета. DTO дето ти идва в контролера, но само с 2 полета.

DTO-то може би е валидно само по себе си, но ако пренесеш в сървиса 2-те полета дето трябва да се ъпдейтнат в ентитито от DTO-то - дали вече ще е валидно ентитито?

Още по конкретно - имаш PersonEntity(name, sex, egn).
И PersonDTO(name, sex) с което можеш да смениш името и/или пола на човека в едит на акаунт.

Хубаво, но имаш и валидация за ЕГН. Доколкото помня, предпоследната цифра от ЕГН-то беше пол.

Само по себе си PersonDTO е валидно (Пенка/жена), (Пешо/мъж) ако искаш да редактираш пола и името. Но тогава ЕГН-то може да стане невалидно.

Та.. мисля си, че винаги е най-добре и най-сигурно да имаш валидация и на ентити и в ДТО.

Към номер 1: в заданието на мобилеле, явно е редактирано, няма почти никаква валидация. И аз съм сложиш на всякъде че трябва да има нещо но само на някои места съм сложил допълнения.

thrako commented

Здравейте,

Много щекотлива тема сте отворили:

DTO-то може би е валидно само по себе си, но ако пренесеш в сървиса 2-те полета дето трябва да се ъпдейтнат в ентитито от DTO-то - дали вече ще е валидно ентитито?

Още по конкретно - имаш PersonEntity(name, sex, egn). И PersonDTO(name, sex) с което можеш да смениш името и/или пола на човека в едит на акаунт.

Хубаво, но имаш и валидация за ЕГН. Доколкото помня, предпоследната цифра от ЕГН-то беше пол.

Само по себе си PersonDTO е валидно (Пенка/жена), (Пешо/мъж) ако искаш да редактираш пола и името. Но тогава ЕГН-то може да стане невалидно.

Та.. мисля си, че винаги е най-добре и най-сигурно да имаш валидация и на ентити и в ДТО.

Ето и моите 5 пари: Смяната на пола, не е легално допустима в България, към момента (а вероятно и още доста време няма да бъде). Дали в такъв случай програмата ни трябва да позволява смяна на пола, изобщо? А ако позволява, това трябва ли неминуемо да води до промяна на или да се изисква съвпадение с предпоследната цифра в ЕГН-то (тази цифра все пак вярно е отразявала състоянието на обекта при раждането му)? Твърде философски въпроси.

Като оставим шегата настрана, ето какъв подход следвам аз: Може и понякога се налага да имаш валидации и в DTO, и в Entity. Изцяло зависи от конкретния случай, на кое място би било по-подходящо да валидираш данните, важно е обаче, след като данните преминат през целия процес на валидация, в базата данни да остане валиден обект (всяко поле поотделно да е валидно и комбинациите от полета също да са валидни, ако случаят го изисква). Следва да се има предвид още, че валидациите на ниво Entity са по-генерални, т.к. обичайно имаш едно Entity и много DTO-та. Разбира се, излишно е да казвам, че не би искал едни и същи данни да бъдат валидирани по няколко пъти за едно и също (просто не е оптимално). Така че, като вземеш предвид горното и спрямо конкретния ти случай, надявам се по-лесно ще преценяваш на кое ниво коя валидация да поставиш. Има случаи, при които това е очевидно: напр. дали паролата и повторно въведената парола съвпадат, очевидно ще проверяваш на ниво DTO, просто защото в Entity ще имаш само едно поле с парола. Други случаи обаче са по-засукани :)

Поздрави,
Траян

thrako commented
3. * [ ]  Когато джава скрипт, прави проверка на всяка секунда примерно за да визуализира съобщения в чат, това до колко забавя приложението или страницата. Днес установих, че когато използвам големи файлове за картинки страницата зарежда а картинките започват бавно бавно да се пълнят и го няма. Трябва да ги чакам, как да се справим с този проблем?

Предполагам, че за да не правиш проверка на всяка секунда по-оптимално би било да се използват web sockets, доколкото зная истинските месинджъри/чатове работят по такъв (или подобен) начин. Отворил съм issue, т.к. и на мен ми е интересно как става на практика.

Поздрави,
Траян

thrako commented
4. * За крайния проект, може ли да използваме различни мапери, за да се покаже че знаем повече от един или е много лоша практика и няма да се приеме добре?

Не ми се струва добра идея да използваш няколко mapper-а в един и същи проект, освен ако нямаш добра причина да го правиш. Да покажеш, че можеш да ги ползваш, определено не е такава. Избери си един mapper и ползвай само него. Отделно можеш да си създадеш няколко демо проекта, в които да демонстрираш как използваш останалите mapper-и. При разговора с изпитващия може да споменеш (ей така между другото), че можеш да боравиш и с други mapper-и, но си избрал, точно този, защото... [fill in the blank]. Не мога да намеря точен цитат в момента, но в Clean Code на Uncle Bob много ясно е застъпена идеята, че кодът не трябва да изненадва читателя. Имай предвид, че много често читателят на твоя код си си ти самия (след време, когато си позабравил вече къде, какво и защо си използвал).

Аз лично, никак не харесвам mapper-ите, т.к. употребата им обикновено е сложна и скъпа (откъм ресурси). Повечето използват reflection отдолу, а дори и да не използват, преминават през сложни алгоритми, за да отгатнат кои данни, на кое поле трябва да се map-нат. Ако трябва да map-ваш някакъв по-сложен клас, който в себе си държи колекция от друг клас, вторият пък на свой ред държи колекция от трети клас и т.н. (схващаш идеята), може да загубиш много повече време да настроиш mapper-а какво точно да прави, отколкото да си напишеш method, който върши същата работа и не му се налага да гадае кои данни за кое поле се отнасят. Сигурно има и случаи, при които употребата на mapper е удачна (но за момента не съм се сблъсквал с такъв).

В моя изпитен проект само на едно място използвах mapper (и то за най-простия случай, просто да се застраховам, защото беше изискване за изпита, но сега като отвори темата ме подсещаш да го рефакторирам), вместо това масово използвах static factory method-и. Например, имаш PersonEntity, което трябва да експортнеш към PersonDTO. В PersonDTO си създаваш:

public static PersonDTO fromEntity (PersonEntity entity) {
    return new PersonDTO()
        .setFullName(entity.getFirstName() + " " + entity.getLastName())
        .setAddress(entity.getAddress().toString()) //да кажем, че Address ти е самостоятелен обект, но в dto-то ти трябва String
        //и т.н.
}

След това е супер лесно и четимо да се използва, напр. вземаш List от базата, стриймваш го и го мапваш ето така:

return personRepository.getAll().stream()
    .map(PersonDTO::fromEntity)
    .collect(Collectors.toList());

Ако някой се интересува как точно става map-ването е на един клик разстояние от това да разбере, докато ако използваш mapper - може да се окаже доста сложна задача.

Поздрави,
Траян

luchob commented

Леле....

Относно валидациите отново бих си казал, че предпочитам да са на двете места. Още един аргумент в полза на това е че промените не винаги идват през ДТО-та. Например може някакъв шедюлър да стартира бекграунд таск в сървис, който пък прави
различни сметки директно с ентита, или пък да дойде съобщение през JMS или Kafka или просто апликейшъна да е смотан и т.н. Да - всичко ще работи и без валидации, но предпочитам да съм сигурен че в ДБ влизат правилни данни а най-сигурния начин за това е стабилна валидация.


Относно JAVA скрипта и картинките. Теоритично не би трябвало да забавя съществено, освен ако този полинг не прави нещо твърде лошо :-)

Затова, предлагам три варианта:

Вариант 1 - най-тривиален, състои се от две стъпки. Ако може да си позволиш, вдигни polling интервала.
След това виж дали няма някакъв перформънс проблем с API-то което викаш, защото това е честа причина за проблеми. Например някакъв гаден N+1 проблем. На следващата лекция ще спомена за N+1 проблеми вдъхновен от PR-a на @thrako :-) където има такъв. Искам да го решим с ентити граф, нещо от сорта на

  @EntityGraph(value = "brandWithModels", attributePaths = "models")
  @Query("SELECT b from BrandEntity b")
  List<BrandEntity> findAllBrandsAndModels();

Една смешка - наскоро колега беше решил N+1 проблем и това пък направи сървиса по-бавен и по-късно го събори с OutOfMemory и всичко отиде във WC :-) Защото в парент обекта имаше някакъв текст дето беше към 4КВ и имаше 3К child обекта и с JOIN дръпна 3000 х 4К и ги превърна в обектчета :-) .

Вариант 2 - когато вариант 1 е изчерпан опитай с една друга техника, която се нарича long polling.

Теория (в момента съм до половината и ми се струва удачно): https://www.pubnub.com/blog/http-long-polling/
Малко демо: https://www.baeldung.com/spring-mvc-long-polling

Вариант 3 - web sockets 😈

thrako commented

Относно валидациите отново бих си казал, че предпочитам да са на двете места

Кой съм аз, че да споря с GOAT на Spring в България? Respect! Започвам да валидирам яко entita и аз. :)

На следващата лекция ще спомена за N+1 проблеми вдъхновен от PR-a на @thrako :-)

Намирисваше ми още докато го писах, че правя прекалено много request-и към базата, но не знаех, че това даже си има име. Оставих го така, заради желанието за сортировка на данните, даже по-късно overload-нах метода, за да може да се вика с custom Comparator-и, но явно всичко ще отиде в кофата :)

Добър вечер,
Относно environment variables, по-сигурно ли е, ако в реални проекти използваме такава променлива за името на базата от данни?
Например: MYSQL_DATABASE_NAME=mobilelele
С уважение, Александър

luchob commented

@AlexanderKK По-скоро - не. Минава в категорията security via obscurity :-) Хубаво би било HOST/PORT, силно желателно USERNAME, задължително PASSWORD.

В PROD среда връзката е добре да се осъществи вместо това с ssl сертификат.

luchob commented

За крайния проект, може ли да използваме различни мапери, за да се покаже че знаем повече от един или е много лоша практика и няма да се приеме добре?

Ами... съгласен съм с tharko до голяма степен. Но и все пак това е рекламен проект, в който целта ти е хем да тъпчеш възможно най-много неща за да покажеш че ги знаеш, хем пък и да не става гротеска с 1000 вида мапъри :-) Всъщност и аз не съм им огромен фен - има и бързи, които не ползват рефлекшън (MapStruct), но си изисква да знаеш какво парви и как точно да го ползваш. Имаме проекти с мапъри и без мапъри и честно казано това ни е най-малкия проблем. А може би последното нещо на което ще те изпитват на интервю е колко вида мапъри можеш да ползваш :-) Кажи им model mapper, map struct, dozer, orika и един много як - мой си 👯 📦 :-)

Код ревю, може ли да се направи на проекта ми (мобилеле)до тук?

По-скоро в тази част не, защото времето ни притиска вече, а искам да поговорим за последното задължително нещо - валидации.