JavaRush/Java блог/Java-проекты/Рефакторим и ретроспективим - "Java-проект от А до Я"
Roman Beekeeper
35 уровень

Рефакторим и ретроспективим - "Java-проект от А до Я"

Статья из группы Java-проекты
участников
Всем привет, дорогие друзья. Это последняя статья в нашей серии по созданию Java-проекта от А до Я. Сегодня поговорим о том, что хорошо бы отрефакторить, а также обсудим результат нашей работы и поговорим о планах на будущее проекта JavaRushTelegramBot. "Java-проект от А до Я": Рефакторим и ретроспективим - 1

Отрефакторим наш код

Создадим под это дело новую ветку: STEP_10, в которую запишем все последние изменения до MVP. Рефакторинг — это изменение кодовой базы без изменения ее поведения. Что хочется изменить? В самом начале термином “статья” на проекте я выбрал слово article и пока не начал работать с клиентом Javarush API для статей, это было логично. Но в Javarush API используют термин post. И я думаю, что хорошо будет перейти на их стандарт, поэтому первым делом переименуем везде с article на post. Что будет нашей гарантией, что все осталось также? Гарантией будут наши тесты, которые мы написали. Именно теперь они нам понадобятся. Начнем изменения с БД. В таблице group_sub, нужно переименовать поле lastArticleId в lastPostId. Это очень просто, потому что у нас уже настроен Flyway и для того, чтобы обновить поле, нужно всего добавить еще одну миграцию, в которой мы выполним ALTER TABLE операцию. Создадим новую, третью, миграцию: V00003__rename_last_article_id.sql в котором будет:
ALTER TABLE group_sub CHANGE COLUMN last_article_id last_post_id INT;
Эту миграцию нужно поместить в тоже место, где лежат и две предыдущие. Далее нужно переименовать имя переменной в сущности GroupSub с lastArticleId на lastPostId. Если работа идет через IDEA, то провести переименование очень просто, нужно воспользоваться горячей клавишей: shift + F6, когда курсор находится на имени класса/метода/поля, тогда среда разработки позволяет переименовать все места, где используется это поле. Также переименует геттеры и сеттеры для этого поля.Это полезная вещь и должна быть в арсенале каждого разработчика. Я уверен, что такой же функционал есть и у эклипса, но подсказать не могу, я им не пользуюсь."Java-проект от А до Я": Рефакторим и ретроспективим - 2поэтому переименуем
@Column(name = "last_article_id")
private Integer lastArticleId;
в
@Column(name = "last_post_id")
private Integer lastPostId;
Далее при помощи поиска по всему проекту (ctrl+shift+f) найдем все места, где есть упоминания Article:"Java-проект от А до Я": Рефакторим и ретроспективим - 3Видно из рисунка, что у нас еще есть имена классов и методов со словом Article. Их тоже нужно переименовать в Post. Используем уже известную команду shift+F6 для них:
  • класс FindNewArticlesJob -> FindNewPostsJob

  • класс FindNewArticleService -> FindNewPostsService

  • метод FindNewArticleService#findNewArticles -> FindNewPostsService#findNewPosts

  • класс FindNewArticleServiceImpl -> FindNewPostsServiceImpl

  • проперти bot.recountNewArticleFixedRate -> bot.recountNewPostFixedRate

  • метод JavaRushGroupClient#findLastArticleId -> JavaRushGroupClient#findLastPostId

  • метод FindNewPostsJob#findNewArticles -> FindNewPostsJob#findNewPosts

  • И остальные места, где внутренние методы, где комментарии.

Также у нас еще есть BPMN модель, в которой описан процесс поиска новых статей. Его менять мы будем через специальную программу CamundaModeler, через нее и формирую новую картинку в README. В результате получим:"Java-проект от А до Я": Рефакторим и ретроспективим - 4Еще хочу привести к одному виду поиск чего-то. На данный момент у меня используется два глагола: find и retrieve. Хочу переименовать все retrieve в find:
  • метод CommandContainer#retrieveCommand -> CommandContainer#findCommand
  • все джавадоки с retrieve в find.
Далее хочется решить поле chatId. Опираясь на доку из телеграма, можем сказать, что это числовое значение. Поэтому я думаю будет хорошим тоном сделать также и в приложении. В базе данных оно стоит как строковое значение. Сделали мы это потому, что по какой-то причине в нашей библиотеке работы с ботом при отправке сообщений chatId был именно строковым. Поменяем все так, чтобы конвертировать в строку именно в тот момент. Первым делом обновляем интерфейс SendBotMessageService:
/**
* Service for sending messages via telegram-bot.
*/
public interface SendBotMessageService {

   /**
    * Send message via telegram bot.
    *
    * @param chatId  provided chatId in which would be sent.
    * @param message provided message to be sent.
    */
   void sendMessage(Long chatId, String message);

   /**
    * Send messages via telegram bot.
    *
    * @param chatId  provided chatId in which would be sent.
    * @param message collection of provided messages to be sent.
    */
   void sendMessage(Long chatId, List<String> message);
}
Теперь принимает интерфейс не String chatId, а Long chatId. Обновляем и реализацию, ставим просто chatId.toString() в необходимом поле:
sendMessage.setChatId(chatId.toString());
Далее идем в класс TelegramUser и меняем тип переменной chatId с String на Long:
@Id
@Column(name = "chat_id")
private Long chatId;
В TelegramUserRepostiry меняем в наследуемом интерфейсе также со String на Long:
/**
* {@link Repository} for handling with {@link TelegramUser} entity.
*/
@Repository
public interface TelegramUserRepository extends JpaRepository<TelegramUser, Long> {
   List<TelegramUser> findAllByActiveTrue();
   List<TelegramUser> findAllByActiveFalse();
}
Обновляем метод CommandUtils#getChatId, чтобы он не приводил к строке:
/**
* Get chatId from {@link Update} object.
*
* @param update provided {@link Update}
* @return chatID from the provided {@link Update} object.
*/
public static Long getChatId(Update update) {
   return update.getMessage().getChatId();
}
И во всех местах, где используется chatId, убираем приведение к строке. Там, где методы принимают String chatId, меняем тип на Long. Кстати, пока менял приведение к строке нашел, что утилитный метод getChatId(update) не везде используется, так что обновил и те места. Последний этап — нужно обновить тип поля в таблице tg_user. Для этого создадим четвертую миграцию V00004_change_chat_id_type_to_long.sql:
ALTER TABLE tg_user MODIFY chat_id INT;
После всех этих обновлений пришло время запустить тесты :D В результате упал только один тест, могло быть и хуже. Оказалось, что падал тест из-за того, что JUnit позволяет компилировать сравнение разных типов при assert:"Java-проект от А до Я": Рефакторим и ретроспективим - 5Как только я вместо String.valueOf(i + 1) сделал Long.valueOf(i + 1), то тест начал работать, как ожидается. Теперь нужно удостовериться, что бот запускается и работает, как ожидается. Попробую добавить подписку, посмотреть список подписок и удалить одну из них. Думаю, этого хватит для того, чтобы проверить на smoke test. Поклацал все, что мог, все работает. Это хорошо, значит, рефакторинг прошел именно так, как я хотел, — изменился код, но изменения никак не повлияли на поведение бота. Мы провели неплохой рефакторинг по проекту. Привели к общему знаменателю термины и убрали путаницу с типом переменной chatId.

Последние приготовления перед выходом в свет стабильной версии

Теперь, когда уже написан весь код, который планировался, можно сделать последние приготовления. Обновим версию в приложении на стабильную 1.0.0:
<version>1.0.0</version>
и добавим описание в RELEASE_NOTES:
# Release Notes ## 1.0.0 Implemented all the logic, planned up to MVP: * User can subscribe on group of posts * User can view list of group subscriptions on which user subscribes * User can unsubscribe from group of posts * User can set an inactive bot and do not receive notifications * User can restart getting notifications * Admin has ability to see bot statistics
Все эти пункты я взял из README.md, думаю они как раз опишут результат работы, которая была проделана за эти 8 месяцев. Следующий этап — создаем коммит, в нем я дублирую данные из RELEASE_NOTES. Получим ПР с последними изменениями: STEP_10: v1.0.0.

Создаем наш первый релиз: v1.0

Весь код, который мы планировали уместить в MVP, готов, и теперь нужно подвести черту — создать релиз. Что такое релиз? Это опять калька из английского слова release, которое в нашем случае означает выход новой версии продукта. Для этого у GitHub в репозитории есть секция Releases, в которой как раз будут храниться релизы. Только их нужно создавать."Java-проект от А до Я": Рефакторим и ретроспективим - 6Смержим последний пулл-реквест и создадим свой первый релиз на основе последнего коммита в main ветке. Переходим и создаем новый релиз и заполняем данными:"Java-проект от А до Я": Рефакторим и ретроспективим - 7Все, можно нажимать Publish Release. И все, теперь у нас есть наш первый релиз приложения. Пришло время и для ретроспективы.

Что дальше с ботом?

Конечно, хочется, чтобы бот начал развиваться за счет сообщества. Чтобы молодые разработчики предлагали свои идеи по реализации новой функциональности. Но здесь насильно мил не будешь, пока таких нет, и я буду посильно развивать его. Есть несколько направлений, которые интересны для меня:
  • Создать джобу для ежедневного сбора и сохранения статистики бота, чтобы можно было отследить во времени его развитие.

  • Настроить работу с бэкапами, в целом идея описана в предыдущей статье.

  • Подумать над унитаризацией подхода в создании телеграм бота для получения уведомлений о новых статьях. В идеале (хотя я не знаю, получится ли такое) сделать так, чтобы был опубликованный Spring boot стартер, который бы требовал настройки базы данных, бота и реализации интерфейсов работы для получения данных о статьях, а остальное бы делала эта библиотека. Посмотрим что из этого может получиться, тут нужно хорошенько подумать.

  • Расширить статистику для всех по авторам: сколько статей, количество просмотров статей, какая оценка статей. По понятным причинам JavaRush не является блогом для написания статей, и поэтому такого функционала у него попросту нет. А мне, как автору, это интересно. При помощи бота можно создать продуманную статистику и вести ее.

  • Реализовать обработку исключений так, чтобы было пользователю было понятно, что произошло.

  • Выделить отдельно Java-библиотеку с JavaRush API client.

  • Добавить GitHub action, который бы создавал релиз автоматически каждый раз, когда обновляется ветка main.

  • Добавить подписку на конкретного автора, а не на группу статей.

  • Улучшить UI представление бота. Вместо уродливых команд создать красивые кнопки, которые бы помогли более приятно и элегантно управлять ботом.

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

  • Изучить опыт ботов, которые работают в группах и посмотреть, что можно сделать с этим в нашем случае. Я уверен, что есть закрытые группы людей, изучающих Java, которым было бы интересно получать уведомления о новых статьях.

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

Ретроспектива (вместо окончания)

Я часто повторяю это слово уже последние несколько статей. Что это значит? Для меня в рамках этой серии — это посмотреть на то, что я хотел сделать и что за эти 8 месяцев получилось. Было немало комментариев на тему того, что все это классно, хоть бы автор сделал это до конца:"Java-проект от А до Я": Рефакторим и ретроспективим - 8"Java-проект от А до Я": Рефакторим и ретроспективим - 9"Java-проект от А до Я": Рефакторим и ретроспективим - 10Теперь уже можно сказать, что да, я таки завершил. Думал ли я, что это будет тааак долго? Нет, не думал. Ожидал, что сделаю до начала 2021 года. Но я понял, что нужно рассказать и о базах данных, которые вышли на целую серию статей, и о многих технологиях. И это растянулось. Хотя могу сказать, что и результатом и объемом работы доволен. Я вижу людей, которые учатся на этом, выражают свою благодарность, и это мотивирует меня идти дальше. Что это дало мне? Прежде всего понимание, что такие проекты можно брать и они пользуются интересом. Я всегда с большим удовольствием читаю комментарии к статьями и в меру сил отвечаю на них. Конечно, я стал опытнее и в написании статей, и в написании приложений, потому что процесс развертывания приложения потребовал от меня усовершенствование моих навыков. Да, я зналб что я хочу сделать в итоге, я зналб что это реально. И все равно потребовалось время на изучение реализации моих идей. В процессе написания этой серии захотелось как-то систематизировать свои материалы в одном местеб и для этого я создал свой телеграм-канал, в котором уже более 400 подписчиков. Для меня это шикарный результат. Новое направление развития. Теперь есть понимание, куда расти дальше. Также в процессе родился новый продукт, которым уже пользуются люди и я в частности — Javarush Telegram Bot. Да, пока что только 26 активных пользователей. Но я верю, что это будет востребовано и количество пользователей будет расти. Чем хорош этот проект? Его еще развивать и развивать. Студенты из JavaRush, которые хотят отточить свои навыки, смогли бы предлагать новый функционал и реализовывать его. А я буду руководить этим проектом и проводить Code Review всех изменений. Финализируем то, что планировалось и что получилось:
  • Главной целью проекта было написание приложение с базой данных, в котором была бы настроена работа по развертыванию и управлению. Эта цель точно достигнута.

  • Сроки разработки сдвинулись и были проблемы с выходом статей. Были моменты, когда не было статей по 2-3 недели.

  • Полностью настроили работу с БД, добавили Flyway.

  • Описали работу с Maven в нескольких статьях.

  • Поговорили о Docker. Не так много и детально, как хотелось бы, но все же.

  • Баш скрипты мы тоже затронули, у нас на этом настроен запуск всего развертывания.

  • По планированию проекта можно сказать, что все прошло максимально реалистично. Только несколько задач были добавлены в процессе написания.

Что не сделали?
  • Полностью не рассмотрели сравнение двух решений — Flyway и Liquibase.

  • Как-то вскользь поговорили о Lombok’e. Так только на практике и все. Хотелось бы больше.

  • Мало уделили время для UniRest решения.

Может, я что-то упустил? Пишите в комментариях. Спасибо всем, кто активно участвовал в процессе развития этого проекта. Жду от вас отзыва о том, что в итоге получилось. Обратная связь, так сказать) Пока я писал эту серию, у меня накопилось много интересных тем, о которых я бы хотел поговорить, так что скоро будем писать свое резюме на GitHub! До встречи)

Список всех материалов серии в начале этой статьи.

Комментарии (21)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Bolivar Li
Уровень 32
28 декабря 2023, 08:58
Уффф дошел, спасио Роман. Было очень интересно. Сформировалось понимание куда дальше копать , теже тесты )) 🤝🤝🤝
kiiril
Уровень 51
16 декабря 2023, 15:41
Отличный набор статей, я как человек на финишной прямой своего обучения был рад сделать этот проект, разобраться в чем-то новом и подчеркнуть для себя какие-то интересные подходы. Спасибо автору за проделанную работу и думаю еще не один ученик JavaRush будет делать этот проект)
Kirill
Уровень 32
11 ноября 2022, 19:48
Добрался! :D Роман, по пути уже выражал благодарность, но хочу еще раз сказать, спасибо за проделанный труд! Иногда было потно и слезливо, а иногда было задорно и радостно:) В общем и целом могу сказать что предоставленная информация была очень полезной и помогла сложить хоть какое-то представление о написании приложений!
Alexander Lipatov
Уровень 1
31 октября 2022, 17:33
Роман, спасибо огромное за данный цикл статей! Местами было сложно, тк по сути мое первое большое приложение. Реализовал без докера. Но все равно очень рад! Было море полезной информации. Успехов и удачи!
Kosmos
Уровень 35
24 мая 2022, 18:09
Ура!!! Я это сделал - релиз готов. Кто молодец - я молодец!!! 🥳 Спасибо тебе Роман за твои труды.
Рустам Косимов
Уровень 31
1 января 2022, 13:41
Спасибо, просто БРАВО!!! Узнал очень много чего нового, теперь хоть есть какое-то понимание как делать приложения
Hamza
Уровень 0
4 октября 2021, 11:28
Спасибо большое за проделанный труд, давно искал такого рода обучалки. Успехов Вам!
17 июня 2021, 18:48
Респект тебе и уважуха за твои старания! Такой выносливости и целеустремленности можно только позавидовать. Предлагаю следующий проект - операционную систему на java😀
Сергей Цыманов
Уровень 35
9 июня 2021, 09:03
Добрый день, скрипт который апдейтит колонку chat_id в INT не срабатывает потомучто на ней висит foreign key, ошибка при деплое. Нужно сначала сбросить ключи, потом обновить и затем обратно повесить
Roman Beekeeper тг-канал по java разработ в t.me/romankh3
9 июня 2021, 09:48
Привет. Интересно.. А у тебя есть не числовые значения в бд для этого поля?
Сергей Цыманов
Уровень 35
9 июня 2021, 09:57
с нуля накатывал, база пустая
Roman Beekeeper тг-канал по java разработ в t.me/romankh3
9 июня 2021, 10:21
Значит что-то, Сергей, у тебя не так как у меня. Эта миграция уже давно в проде и она прошла без проблем. Может не MySQL стоит, а какая-то другая БД?
Сергей Цыманов
Уровень 35
9 июня 2021, 12:16
ну как не тот MySQL, тот который определен в docker-compose, версия 5.7. FlyWay в моем случае при миграции выкидывает исключение когда пытается применить этот скрипт ALTER TABLE tg_user MODIFY chat_id INT. Проблема из-за foreign_key Нашел почему у вас проблема не выскочила, у вас название скрипта V00004_change_chat_id_type_to_long.sql не верно для FlyWay, он у вас при миграции просто не применился, зайдите в Mysql базу бота и проверте, там нет его. А проблема в том что пропущено одно нижнее подчеркивание в названии.
Roman Beekeeper тг-канал по java разработ в t.me/romankh3
9 июня 2021, 12:19
интерсненько, просмотрю, спасибо))
Roman Beekeeper тг-канал по java разработ в t.me/romankh3
16 июня 2021, 20:35
Да, все верно. Как будет время - обновлю, Спасибо тебе Сергей за то, что нашел и написал
Kosmos
Уровень 35
24 мая 2022, 18:04
Тоже было, что миграцию не подхватывал. Пришлось переименовывать файл миграции. Как FlyWay подхватил, вылезла такая же ошибка (версия mysql 8.0, latest в dockerhub). Здесь выкрутился таким способом:
ALTER TABLE group_x_user DROP FOREIGN KEY group_x_user_ibfk_1;
ALTER TABLE group_x_user MODIFY user_id INT;
ALTER TABLE tg_user MODIFY chat_id INT;
ALTER TABLE group_x_user ADD CONSTRAINT group_x_user_ibfk_1 FOREIGN KEY (user_id) REFERENCES tg_user(chat_id);
Dmitry Nevar
Уровень 26
8 июня 2021, 06:50
Роман! У меня еще есть вопрос, как мне сделать бот рабочим вне idea, чтоб я мог добавлять в него функционал и показывать как рабочий проект в портфолио! а возможно и переделал его под нужды друзей/родных и пользовал как полезный инструмент)
Roman Beekeeper тг-канал по java разработ в t.me/romankh3
8 июня 2021, 08:12
Дима, тебе нужно найти место, где на постоянной основе будет запущено приложение с ботом и все. Никаких статических IP адресов не нужно. Это может быть сервер на Хероку или EC2 машина на AWS. Или какой-то другой провайдер для сервера. На худой конец это может быть никогда не выключающийся комп у тебя дома)) Я планирую установить домашний сервер для личных нужд, в том числе и приложения рабочие.