├── .github └── workflows │ └── hugo.yml ├── .gitignore ├── .gitmodules ├── .hugo_build.lock ├── README.md ├── archetypes └── default.md ├── content ├── _index.md ├── finished-projects │ ├── _index.md │ ├── cloud-file-storage.md │ ├── currency-exchange.md │ ├── hangman.md │ ├── other.md │ ├── simulation.md │ ├── task-tracker.md │ ├── tennis-scoreboard.md │ └── weather-viewer.md ├── plan.md ├── projects │ ├── _index.md │ ├── cloud-file-storage.md │ ├── currency-exchange.md │ ├── hangman.md │ ├── simulation.md │ ├── task-tracker.md │ ├── tennis-scoreboard.md │ └── weather-viewer.md └── technologies │ ├── _index.md │ ├── backend.md │ ├── build-systems.md │ ├── databases.md │ ├── dev-ops.md │ ├── frontend.md │ ├── java.md │ ├── microservices.md │ └── tests.md ├── hugo.toml ├── resources └── _gen │ └── assets │ ├── book.scss_e129fe35b8d0a70789c8a08429469073.content │ └── book.scss_e129fe35b8d0a70789c8a08429469073.json └── templates ├── cloud-file-storage.md ├── currency-exchange.md ├── hangman.md ├── simulation.md ├── task-tracker.md ├── tennis-scoreboard.md └── weather-viewer.md /.github/workflows/hugo.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Hugo site to GitHub Pages 2 | name: Deploy Hugo site to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | # Default to bash 25 | defaults: 26 | run: 27 | shell: bash 28 | 29 | jobs: 30 | # Build job 31 | build: 32 | runs-on: ubuntu-latest 33 | env: 34 | HUGO_VERSION: 0.128.0 35 | steps: 36 | - name: Install Hugo CLI 37 | run: | 38 | wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ 39 | && sudo dpkg -i ${{ runner.temp }}/hugo.deb 40 | - name: Install Dart Sass 41 | run: sudo snap install dart-sass 42 | - name: Checkout 43 | uses: actions/checkout@v4 44 | with: 45 | submodules: recursive 46 | - name: Setup Pages 47 | id: pages 48 | uses: actions/configure-pages@v5 49 | - name: Install Node.js dependencies 50 | run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" 51 | - name: Build with Hugo 52 | env: 53 | HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache 54 | HUGO_ENVIRONMENT: production 55 | run: | 56 | hugo \ 57 | --minify \ 58 | --baseURL "${{ steps.pages.outputs.base_url }}/" 59 | - name: Upload artifact 60 | uses: actions/upload-pages-artifact@v3 61 | with: 62 | path: ./public 63 | 64 | # Deployment job 65 | deploy: 66 | environment: 67 | name: github-pages 68 | url: ${{ steps.deployment.outputs.page_url }} 69 | runs-on: ubuntu-latest 70 | needs: build 71 | steps: 72 | - name: Deploy to GitHub Pages 73 | id: deployment 74 | uses: actions/deploy-pages@v4 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | public/ 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "themes/hugo-book"] 2 | path = themes/hugo-book 3 | url = https://github.com/alex-shpak/hugo-book 4 | -------------------------------------------------------------------------------- /.hugo_build.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-learning-course/cf11cbc88334c9527d28c3f105ede63594ab5a3c/.hugo_build.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Сайт роадмапа 2 | 3 | [https://zhukovsd.github.io/java-backend-learning-course/](https://zhukovsd.github.io/java-backend-learning-course/) 4 | -------------------------------------------------------------------------------- /archetypes/default.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = '{{ replace .File.ContentBaseName "-" " " | title }}' 3 | weight = 1 4 | +++ 5 | -------------------------------------------------------------------------------- /content/_index.md: -------------------------------------------------------------------------------- 1 | # Java Роадмап Сергея Жукова 2 | 3 | Данный роадмап является ресурсом, цель которого - дать начинающему разработчику чёткий план по развитию своих профессиональных навыков до уровня, позволяющего трудоустроиться на вакансию Java backend Junior developer. 4 | 5 | Роадмап является воплощением моего опыта менторства начинающих разработчиков (на июль 2024, количество студентов, трудоустроившихся Java джуниорами с моей помощью - 30+). 6 | 7 | Важно отметить, что данный роадмап не стремится быть единственным источником обучающей информации для студента. Предполагается, что здесь можно найти общую структуру образовательного процесса и практику, а теорию студент осваивает удобным для него способом - видеокурсы, книги. Подробнее об этом ниже, в разделе "как пользоваться роадмапом". 8 | 9 | Адаптации роадмапа для других языков программирования: 10 | - Python - [https://zhukovsd.github.io/python-backend-learning-course/](https://zhukovsd.github.io/python-backend-learning-course/) 11 | 12 | ## Обо мне 13 | 14 | С 2009 года профессионально занимаюсь разработкой, на Java пишу с 2014. Рабочая должность и обязанности находятся где-то на пересечении Architect/Fullstack Dev/Team Lead. Люблю и умею учить и менторить начинающих и развивающихся разработчиков. 15 | 16 | - [Блог](https://zhukovsd.it/) 17 | - [LinkedIn](https://www.linkedin.com/in/zhukovsd/) 18 | - [Резюме](https://zhukovsd.github.io/zhukovsd-cv/cv.pdf) 19 | - [Telegram канал](https://t.me/zhukovsd_it_mentor) 20 | 21 | ## Содержимое роадмапа 22 | 23 | Общий [план](./plan.md) от начала учёбы до трудоустройства. 24 | 25 | Основа роадмапа - последовательность из 7 проектов с возрастающей сложностью, для постепенного охвата необходимых, на мой взгляд, навыков и технологий, требуемых для трудоустройства. 26 | 27 | #### Схема навыков 28 | 29 | ![Java Backend developer roadmap drawio](https://github.com/zhukovsd/java-backend-learning-course/assets/14361885/71102727-c9c2-46c2-ae84-68d7deb9b25f) 30 | 31 | Подробно про то как пользоваться роадмапом я рассказывал на [стриме](https://www.youtube.com/live/Om759lMqG3g?si=hIauOFDwC45RSB2r). 32 | 33 | #### Матрица навыков и проектов 34 | 35 | ![Screenshot 2024-10-06 at 23 33 30](https://github.com/user-attachments/assets/22adc0ec-fd03-4c60-bd62-6540ebb9c697) 36 | 37 | Идея матрицы - визуализировать, как проекты охватывают всё больше и больше сфер знаний. 38 | 39 | ### Проекты с тех.заданиями 40 | 41 | Для каждого из проектов написано техническое задание, содержащее описание проекта, с какими знаниями к нему нужно подойти, на что обратить внимание. При написании тех.заданий я фокусировался на том, чтобы темп возрастания сложности был не слишком высоким, но в то же время, проекты дают конкретные технические навыки, требуемые в работе. 42 | 43 | 1. [Виселица](./projects/hangman.md) 44 | 2. [Симуляция](./projects/simulation.md) 45 | 3. [Обмен валют](./projects/currency-exchange.md) 46 | 4. [Табло теннисного матча](./projects/tennis-scoreboard.md) 47 | 5. [Погода](./projects/weather-viewer.md) 48 | 6. [Облачное хранилище файлов](./projects/cloud-file-storage.md) 49 | 7. [Планировщик задач](./projects/task-tracker.md) 50 | 51 | [Список реализаций](./finished-projects/_index.md) проектов роадмапа студентами со ссылками на репозитории, авторов, и мои ревью. 52 | 53 | ### Требуемые знания и технологии 54 | 55 | Для колонок из схем выше я написал документы с описанием того, что нужно знать, ссылками с начальной информацией: 56 | - [Java](./technologies/java.md) 57 | - [Backend](./technologies/backend.md) 58 | - [Базы данных](./technologies/databases.md) 59 | - [Системы сборки](./technologies/build-systems.md) 60 | - [DevOps](./technologies/dev-ops.md) - Git, деплой, CI/CD 61 | - [Frontend](./technologies/frontend.md) 62 | - [Тестирование](./technologies/tests.md) 63 | - [Контейнеры и микросервисы](./technologies/microservices.md) 64 | 65 | ## Общение 66 | 67 | С появлением карьерных стремлений в направлении IT, важно для своего круга общения найти людей, которые уже прошли предстоящий вам путь. 68 | 69 | Частично эту нишу можно заполнить мотивационными YouTube каналами, где опытные и не очень разработчики делятся своими историями. Однако, личное общение решает эту задачу лучше. 70 | 71 | Для вопросов по учёбе, проектам, и общения с единомышленниками пишите в чат сообщества студентов, занимающегося по моим материалам - [https://t.me/zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 72 | 73 | Также есть формат парное менторство. Более подробно можно почитать [тут](https://t.me/zhukovsd_it_mentor/109), доступные менторы в [таблице](https://docs.google.com/spreadsheets/d/1_EaS3CRoBeo-PG04O2YGOYSk3afdGxgeqd3x0WRLe68/edit?gid=0#gid=0). 74 | 75 | ### Лекции 76 | 77 | Для помощи студентам, работающим над проектами, записываю и публикую лекции, объясняющие технологии и идеи, актуальные в контексте проектов роадмапа. 78 | 79 | [Плейлист с лекциями](https://youtube.com/playlist?list=PLOVOZrcS3XMYLy5gWPE1AbZ8UDl7XHpIA&si=BPF-8MNfbCx6Ud36). 80 | 81 | ### Ревью проектов 82 | 83 | К законченному проекту всегда можно написать ряд замечаний, видимых только с высоты опыта. В рамках менторства я делаю ревью реализованных проектов (в своё свободное время). Процесс схож с моим рабочим взаимодействием с джуниорами, в рамках которого я провожу ревью их работы. 84 | 85 | На июль 2024, студентами написано более 380 реализаций, к которым сделано более 90 открытых видео и текстовых ревью. 86 | 87 | - [Коллекция](./finished-projects/_index.md) реализаций проектов роадмапа студентами, и мои ревью к ним 88 | - Плейлист с моими публичными ревью - [YouTube](https://www.youtube.com/playlist?list=PLOVOZrcS3XMbS4iInU-7p6TbIQW-kATfz). 89 | 90 | ### Помощь в выборе учебных материалов 91 | 92 | Для всех [требуемых технологий](#требуемые-знания-и-технологии) в посвященных им страницах роадмапа представлен список избранных учебных материалов. 93 | 94 | ## С чем нужно подойти к роадмапу 95 | 96 | Прежде всего, вам потребуются базовые знания по программированию, будет достаточно университетского уровня, а язык не обязательно должен быть Java. Не беда, если такого уровня нет, но придется достичь его по ходу дела. 97 | 98 | Второе - время и дисциплина. На учёбу желательно выделять не менее 10-15 часов в неделю или больше, регулярно, сделав это частью своей жизни и привычек, распорядка дня. 99 | 100 | Но самое главное - это постановка целей и ваша решимость их достичь. Карьера разработчика несёт в себе множество плюсов, и если ради реализации своих амбиций вы готовы трудиться, то, надеюсь, этот роадмап станет для вас подспорьем в достижении целей. 101 | 102 | ## Как пользоваться роадмапом 103 | 104 | - Определиться со своими целями и возможностями уделять учёбе время 105 | - Начать работу над проектами и изучение необходимой теории 106 | - Поддерживать контакт со мной и другими студентами для тематического общения и менторства 107 | 108 | ## Расширенные материалы 109 | 110 | Итогом полутора лет развития роадмапа стали 270 реализаций проектов, 80 ревью. Видя, что проект приносит пользу, я хочу продолжать его развивать, полностью сохранив бесплатность и доступность тех материалов, что уже созданы. 111 | 112 | Логичным развитием проекта стали расширенные материалы. Что внутри для каждого проекта: 113 | 114 | - Эталонная реализация по шагам. 115 | - Лекция по каждому шагу, включая деплой (20-30 минут на лекцию). 116 | - Разбор типовых ошибок (пример такого списка) - демонстрация в одной из студенческих реализаций, объяснение проблемы и того, как стоило сделать, на примере эталонной реализации (40-60 минут). 117 | 118 | [Подробнее](https://telegra.ph/Moj-pervyj-produkt---rasshirennaya-versiya-roadmapa-04-21) про материалы, какую проблему решают и из чего состоят. 119 | 120 | По состоянию на январь 2025: 121 | 122 | - Виселица - [https://boosty.to/zhukovsd/posts/07961b26-59a9-449f-80c5-53c4c070e2b8](https://boosty.to/zhukovsd/posts/07961b26-59a9-449f-80c5-53c4c070e2b8) 123 | - Симуляция - в работе 124 | - Обмен валют - [https://boosty.to/zhukovsd/posts/08a542e8-5503-4331-a82b-7b6bcf04314b](https://boosty.to/zhukovsd/posts/08a542e8-5503-4331-a82b-7b6bcf04314b) 125 | 126 | ## Помощь и поддержка 127 | 128 | Материально: 129 | - [https://boosty.to/zhukovsd](https://boosty.to/zhukovsd) 130 | - Рассмотрите мои [личные консультуации](https://telegra.ph/Konsultacii--IT-Mentor--Sergej-ZHukov-11-11) и расширенные материалы 131 | 132 | Нематериально: 133 | - Сарафанное радио - если я или мои материалы помогли вам, расскажите о нём тем, кто только начинает свой путь по изучению разработки 134 | - Вычитка материалов, исправление стилистических и орфографических ошибок, опечаток. Исправления можете отправлять в пул реквестах на GitHub или писать мне в Telegram 135 | - Адаптация материалов для других бэкенд платформ - PHP, NodeJS, ASP.NET и других. Для этого можете создать отдельный репозиторий с материалами, и сослаться на оригинальный (данный) репозиторий 136 | - Таймстемпы к [YouTube](https://www.youtube.com/@zhukovsd_it_mentor) видео 137 | 138 | Жду вашу обратную связь, идеи по улучшению и исправлению ошибок - [чат сообщества](https://t.me/zhukovsd_it_chat). 139 | -------------------------------------------------------------------------------- /content/finished-projects/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Завершенные проекты' 3 | weight = 30 4 | bookFlatSection = true 5 | +++ 6 | 7 | # Реализации проектов студентами 8 | 9 | Список реализаций проектов курса (и не только). Для каждого проекта указана ссылка на код, язык программирования/фреймворк, и ссылка на ревью. 10 | 11 | 1. [Виселица](./hangman.md) 12 | 2. [Симуляция](./simulation.md) 13 | 3. [Обмен валют](./currency-exchange.md) 14 | 4. [Табло теннисного матча](./tennis-scoreboard.md) 15 | 5. [Погода](./weather-viewer.md) 16 | 6. [Облачное хранилище файлов](./cloud-file-storage.md) 17 | 7. [Планировщик задач](./task-tracker.md) 18 | -------------------------------------------------------------------------------- /content/finished-projects/cloud-file-storage.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Облачное хранилище файлов' 3 | weight = 6 4 | bookTOC = false 5 | +++ 6 | 7 | # Облачное хранилище файлов 8 | 9 | [ТЗ проекта](../projects/cloud-file-storage.md) 10 | 11 | 63 реализаций на Java, PHP, Python, Kotlin. 25 ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | | [CloudFileStorage](https://github.com/immagixe/CloudFileStorage) | [immagixe](https://github.com/immagixe) | Java | | | 18 | | [cloudFileStorage](https://github.com/BorBoris23/cloudFileStorage) | [BorBoris23](https://github.com/BorBoris23) | PHP | 🎬 [Видео](https://www.youtube.com/watch?v=OVXmQifkexA) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 19 | | [GoogleDriveClone](https://github.com/DLISM/GoogleDriveClone) | [DLISM](https://github.com/DLISM) | Java | 🎬 [Видео](https://t.me/zhukovsd_it_chat/6767) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 20 | | [cloud-file-storage](https://github.com/krios2146/cloud-file-storage) | [krios2146](https://github.com/krios2146) | Java | | | 21 | | [cloud-file-storage](https://github.com/TurboGoose/cloud-file-storage) | [TurboGoose](https://github.com/TurboGoose) | Java | 🎬 [Видео](https://t.me/zhukovsd_it_chat/36625), 📝 [Заметки](https://gist.github.com/zhukovsd/2289c7861de6610b7931ff9335a906f9) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 22 | | [filesCloud](https://github.com/RomanV79/filesCloud) | [RomanV79](https://github.com/RomanV79) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/f1e630b6dcc430762fa28bd74b0a078d) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 23 | | [cloud-file-storage](https://github.com/farneser/cloud-file-storage) | [farneser](https://github.com/farneser) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/662af182fa511b5db31702bd2ac2e934) | Владимир [@krios2146](https://t.me/krios2146) | 24 | | [Cloud-file-storage](https://github.com/ArtemPronkin/Cloud-file-storage) | [ArtemPronkin](https://github.com/ArtemPronkin) | Java | | | 25 | | [fstored](https://github.com/makeitvsolo/fstored) | [Ivan Makeitvsolo](https://github.com/makeitvsolo) | Java | | | 26 | | [cloud-storage](https://github.com/Cofisweak/cloud-storage) | [Cofisweak](https://github.com/Cofisweak) | Java | | | 27 | | [cloud-file-storage](https://github.com/vadimistar/cloud-file-storage) | [vadimistar](https://github.com/vadimistar) | Java | | | 28 | | [cloud_storage](https://github.com/kolobkevic/cloud_storage) | [kolobkevic](https://github.com/kolobkevic) | Java | 📝 [Заметки](https://gist.github.com/Asenim/fba405ca591617fc5599508c61e895a9) | Илья [@coderilya](https://t.me/coderilya) | 29 | | [CloudDataStorage](https://github.com/YuriyNekludov/CloudDataStorage) | [YuriyNekludov](https://github.com/YuriyNekludov) | Java | | | 30 | | [Galaxy_drive](https://github.com/AleksandrKamen/Galaxy_drive) | [AleksandrKamen](https://github.com/AleksandrKamen) | Java | | | 31 | | [Cloud-File-Storage](https://github.com/timmawv/Cloud-File-Storage) | [timmawv](https://github.com/timmawv) | Java | 📝 [Заметки](https://gist.github.com/Asenim/7b89de0d3d4d9fea65974d367beda354) | Илья [@coderilya](https://t.me/coderilya) | 32 | | [cloud-file-storage](https://github.com/Mich4107/cloud-file-storage) | [Mich4107](https://github.com/Mich4107) | Java | 📝 [Заметки](https://gist.github.com/Asenim/7209eb8846aef46d4efe8d0d009d1030) | Иван [@makeitvsolo](https://t.me/makeitvsolo) | 33 | | [cloud-storage](https://github.com/tonkoshkur/cloud-storage) | [tonkoshkur](https://github.com/tonkoshkur) | Java | | | 34 | | [cloudFileStorage](https://github.com/BondarevM/cloudFileStorage) | [BondarevM](https://github.com/BondarevM) | Java | | | 35 | | [cloud_file_storage](https://github.com/VladislavLevchikIsAProger/cloud_file_storage) | [VladislavLevchikIsAProger](https://github.com/VladislavLevchikIsAProger) | Java | | | 36 | | [FileStorage](https://github.com/privetEdik/FileStorage) | [privetEdik](https://github.com/privetEdik) | Java | 📝 [Заметки](https://gist.github.com/krios2146/abfa398f9a3d55fcc7bbe899af164ae3) | Владимир [@krios2146](https://t.me/krios2146) | 37 | | [cloud-file-storage](https://github.com/escape-8/cloud-file-storage) | [escape-8](https://github.com/escape-8) | PHP | | | 38 | | [cloudFileStorage](https://github.com/KostyaDemens/cloudFileStorage) | [KostyaDemens](https://github.com/KostyaDemens) | Java | | | 39 | | [cloud-drive](https://github.com/wallhackj/cloud-drive) | [wallhackj](https://github.com/wallhackj) | Java | | | 40 | | [CloudStorageApp](https://github.com/Solo83/CloudStorageApp) | [Solo83](https://github.com/Solo83) | Java | | | 41 | | [custom-cloud-storage](https://github.com/Nikitavj/custom-cloud-storage) | [Nikitavj](https://github.com/Nikitavj) | Java | | | 42 | | [cloud-file-storage](https://github.com/liemartt/cloud-file-storage) | [liemartt](https://github.com/liemartt) | Java | 📝 [Заметки](https://gist.github.com/krios2146/1b862c67a6d284bf81848cd48c2b8ff8) | Владимир [@krios2146](https://t.me/krios2146) | 43 | | [FileDrive](https://github.com/DarkRubin/FileDrive) | [DarkRubin](https://github.com/DarkRubin) | Java | | | 44 | | [cloud-file-storage](https://github.com/DragomirovCode/cloud-file-storage) | [DragomirovCode](https://github.com/DragomirovCode) | Java | | | 45 | | [cloud_storage](https://github.com/Oldsize/cloud_storage) | [Oldsize](https://github.com/Oldsize) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/a47cb7e657b71e3b5076a4b23cdcb99b) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 46 | | [CloudFileStorage](https://github.com/garaninnv/CloudFileStorage) | [garaninnv](https://github.com/garaninnv) | Java | | | 47 | | [cloud-file-storage](https://github.com/KostaPo/cloud-file-storage) | [KostaPo](https://github.com/KostaPo) | Java | 📝 [Заметки](https://gist.github.com/Asenim/2d1db5f72724483af767c0974d5a9f03) | Евгений [@solid_jdk](https://t.me/solid_jdk) | 48 | | [cloudFileStorage](https://github.com/SahaPWNZ/cloudFileStorage) | [SahaPWNZ](https://github.com/SahaPWNZ) | Java | | | 49 | | [Cloud-files-storage](https://github.com/grafkust/Cloud-files-storage) | [grafkust](https://github.com/grafkust) | Java | | | 50 | | [cloud-file-storage](https://github.com/salavei-a/cloud-file-storage) | [salavei-a](https://github.com/salavei-a) | Java | | | 51 | | [CloudFileStorage](https://github.com/Mibak87/CloudFileStorage) | [Mibak87](https://github.com/Mibak87) | Java | 📝 [Заметки](https://gist.github.com/Asenim/81b00010952c11352b749169715ae099) | Евгений [@solid_jdk](https://t.me/solid_jdk) | 52 | | [cloud-keeper](https://github.com/shinchik17/cloud-keeper) | [shinchik17](https://github.com/shinchik17) | Java | 📝 [Заметки](https://gist.github.com/OlegTihii/14e897164246e736ffb493c9cf09ecc5) | Евгений [@solid_jdk](https://t.me/solid_jdk) | 53 | | [file-storage](https://github.com/LGAzx/file-storage) | [LGAzx](https://github.com/LGAzx) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/a7e79ffdb0c4e326e90caacf7e7804ac) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 54 | | [cloud-file-storage](https://github.com/leofinder/cloud-file-storage) | [leofinder](https://github.com/leofinder) | Java | 📝 [Заметки](https://gist.github.com/OlegTihii/e3b3010de9bfc1b73b1c07240de36489) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 55 | | [cloud](https://github.com/Yvnushevskiy/cloud) | [Yvnushevskiy](https://github.com/Yvnushevskiy) | Java | | | 56 | | [Cloud_file_storage](https://github.com/Wh4tisl0ve/Cloud_file_storage) | [Wh4tisl0ve](https://github.com/Wh4tisl0ve) | Python | | | 57 | | [cloud_file_storage](https://github.com/PavelPerunov/cloud_file_storage) | [PavelPerunov](https://github.com/PavelPerunov) | Java | | | 58 | | [cloud-storage-rest-api](https://github.com/MrShoffen/cloud-storage-rest-api) | [MrShoffen](https://github.com/MrShoffen) | Java | | | 59 | | [files_cloud.git](https://github.com/slavik-gassiev/files_cloud.git) | [slavik-gassiev](https://github.com/slavik-gassiev) | Java | | | 60 | | [cloud-file-storage](https://github.com/at0m-cat/cloud-file-storage) | [at0m-cat](https://github.com/at0m-cat) | Java | | | 61 | | [CloudFileStorage](https://github.com/mak7im-05/CloudFileStorage) | [mak7im-05](https://github.com/mak7im-05) | Java | | | 62 | | [cloud-file-storage](https://github.com/IlyaDudnikov/cloud-file-storage) | [IlyaDudnikov](https://github.com/IlyaDudnikov) | Java | 📝 [Заметки](https://gist.github.com/OlegTihii/65cab1bedcace799073316067a30c25f) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 63 | | [CloudFileStorage](https://github.com/VladShi/CloudFileStorage) | [VladShi](https://github.com/VladShi) | Java | | | 64 | | [Cloud-Storage](https://github.com/Kirillzhukov737/Cloud-Storage) | [Kirillzhukov737](https://github.com/Kirillzhukov737) | Java | | | 65 | | [file-storage-api](https://github.com/Iposhka54/file-storage-api) | [Iposhka54](https://github.com/Iposhka54) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/b5aec4ef62a272ba1941ca87329d1050) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 66 | | [cloud-file-storage](https://github.com/fakechitor/cloud-file-storage) | [fakechitor](https://github.com/fakechitor) | Kotlin | | | 67 | | [Cloud-File-Storage](https://github.com/Niks5041/Cloud-File-Storage) | [Niks5041](https://github.com/Niks5041) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/37368fa3c7cfcd77cef5304981ad151c) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 68 | | [CS](https://github.com/911boe/CS) | [911boe](https://github.com/911boe) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/97438d8b9eb3d9527049566b16298b2e) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 69 | | [CloudStorage](https://github.com/v1adis1av28/CloudStorage) | [v1adis1av28](https://github.com/v1adis1av28) | Python | | | 70 | | [cloud-storage](https://github.com/iqubb/cloud-storage) | [iqubb](https://github.com/iqubb) | Java | | | 71 | | [cloud-file-storage-rest-api](https://github.com/lolipokzz/cloud-file-storage-rest-api) | [lolipokzz](https://github.com/lolipokzz) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/15de5001f01ca72e661aa5c33aac0bff) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 72 | | [CloudStorage](https://github.com/ZhekaSl/CloudStorage) | [ZhekaSl](https://github.com/ZhekaSl) | Java | | | 73 | | [cloud-storage](https://github.com/Ilyalapin/cloud-storage) | [Ilyalapin](https://github.com/Ilyalapin) | Java | 📝 [Заметки](https://github.com/ArtemPronkin/review/blob/main/lapin/cloud_storage/review.md) | Артем [@pronkin_artem](https://t.me/pronkin_artem) | 74 | | [com.saymk.cloudapp.git](https://github.com/saymkarjat/com.saymk.cloudapp) | [saymkarjat](https://github.com/saymkarjat) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/9070a1a15d5e5d22833b17e863fdaa78) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 75 | | [cloud-file-storage](https://github.com/as1iva/cloud-file-storage) | [as1iva](https://github.com/as1iva) | Java | 📝 [Заметки](https://github.com/evg-rdm-reviews/project-reviews/blob/master/cloud-storage/REVIEW_9%2C1e-31.md) | Евгений [@solid_jdk](https://t.me/solid_jdk) | 76 | | [cloud-file-storage](https://github.com/nebarrow/cloud-file-storage) | [nebarrow](https://github.com/nebarrow) | Java | | | 77 | | [Cloud-file-storage](https://github.com/Sss330/Cloud-file-storage) | [Sss330](https://github.com/Sss330) | Java | | | 78 | | [CloudFileStorage-api](https://github.com/progrohan/CloudFileStorage-api) | [progrohan](https://github.com/progrohan) | Java | | | 79 | | [CloudStorage](https://github.com/Kek20703/CloudStorage) | [Kek20703](https://github.com/Kek20703) | Java | | | 80 | -------------------------------------------------------------------------------- /content/finished-projects/other.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Остальное' 3 | weight = 8 4 | bookTOC = false 5 | +++ 6 | 7 | # Остальное 8 | 9 | Проекты вне курса, которые студенты писали по своей инициативе или по моему совету. 10 | 11 | | Проект | Репозиторий | Автор | Язык | Ревью | Автор ревью | 12 | |-----------------|---------------------------------------------------------------------------------------|-----------------------------------------------------|------------|--------------------------------------------------------------------------------------------|-------------| 13 | | Покер на костях | [dice-poker](https://github.com/dublXq/dice-poker) | [dublXq](https://github.com/dublXq) | Java | 🎬 [Видео](https://t.me/zhukovsd_it_chat/1258) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 14 | | Угадайка числа | [Pet_Project_Guess_Number](https://github.com/FinancierJava/Pet_Project_Guess_Number) | [FinancierJava](https://github.com/FinancierJava) | Java | | 15 | | Крестики-нолики | [pet_ticTacToe](https://github.com/halftimedeus/pet_ticTacToe) | [halftimedeus](https://github.com/halftimedeus) | Java | | 16 | | Покер на костях | [dice_poker](https://github.com/PavelFurochkin/dice_poker) | [PavelFurochkin](https://github.com/PavelFurochkin) | Python | | 17 | | Морской бой | [SeaBattle](https://github.com/Dgeyms/SeaBattle) | [Dgeyms](https://github.com/Dgeyms) | Java | 🎬 [Видео](https://www.youtube.com/watch?v=dyqfbwsbdIM) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 18 | | Крестики-нолики | [Tic-Tac-Toy](https://github.com/DAMir2013/Tic-Tac-Toy) | [DAMir2013](https://github.com/DAMir2013) | Javascript | 🎬 [Видео](https://t.me/zhukovsd_it_chat/4420) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 19 | | Покер на костях | [Poker-dice](https://github.com/LastOfWhom/Poker-dice) | [LastOfWhom](https://github.com/LastOfWhom) | PHP | | 20 | | Flight API | [FlightAPI](https://github.com/u-cha/FlightAPI) | [u-cha](https://github.com/u-cha) | Python | 🎬 [Видео](https://t.me/zhukovsd_it_chat/7974) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 21 | | Flight API | [Airlines](https://github.com/kopyshov/Airlines) | [kopyshov](https://github.com/kopyshov) | Java | 🎬 [Видео](https://t.me/zhukovsd_it_chat/11009) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 22 | | TicTacToe | [TicTacToe](https://github.com/sess-128/TicTacToe) | [sess-128](https://github.com/sess-128) | Java | 📝 [Заметки](https://gist.github.com/Asenim/508bdec6b1b9536d8738b37b6e09ba7e) | Алексей [@Raketa4000az](https://t.me/Raketa4000az) | 23 | | TicTacToeOOPEdition | [TicTacToeOOPEdition](https://github.com/Exelent26/TicTacToeOOPEdition/) | [Exelent26](https://github.com/Exelent26) | Java | 📝 [#1](https://gist.github.com/Asenim/2aee35cf5673caa51fe562c64306d503), 📝 [#2](https://gist.github.com/Asenim/1dcabd27c45bb566075da505ba9facc4) | Дмитрий [@soutpri](https://t.me/soutpri), Алексей [@Raketa4000az](https://t.me/Raketa4000az) | 24 | | TicTacToe | [TicTacToe](https://github.com/muted987/TicTacToe) | [muted987](https://github.com/muted987) | Java | 📝 [Заметки](https://gist.github.com/Asenim/06d703f0cf768cb3c21d075396f82f7f) | Алексей [@Raketa4000az](https://t.me/Raketa4000az) | 25 | 26 | -------------------------------------------------------------------------------- /content/finished-projects/task-tracker.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Планировщик задач' 3 | weight = 7 4 | bookTOC = false 5 | +++ 6 | 7 | # Планировщик задач 8 | 9 | [ТЗ проекта](../projects/task-tracker.md) 10 | 11 | 15 реализаций на Java, Go, Python. 5 ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | | [task-tracker-stack](https://github.com/immagixe/task-tracker-stack) | [immagixe](https://github.com/immagixe) | Java | 🎬 [Видео](https://www.youtube.com/watch?v=QwFlp35yaqw) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 18 | | [task-tracker-stack](https://github.com/AtoDaX/task-tracker-stack) | [AtoDaX](https://github.com/AtoDaX) | Java | | | 19 | | [taskplanner-composestack-mvn](https://github.com/RomanV79/taskplanner-composestack-mvn/tree/master) | [RomanV79](https://github.com/RomanV79) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/5dfeea16d2d9fcbca55e1d34f88f6bc6) | Владимир [@krios2146](https://t.me/krios2146) | 20 | | [TODO-App](https://github.com/userksv/TODO-App) | [userksv](https://github.com/userksv) | Python | | | 21 | | [task-traker-compose](https://github.com/yosakoo/task-traker-compose) | [yosakoo](https://github.com/yosakoo) | Go | | | 22 | | [nanotracker](https://github.com/nanokulonq/nanotracker) | [nanokulonq](https://github.com/nanokulonq) | Java | 📝 [Заметки](https://gist.github.com/Asenim/adc115e2d7c66d9c2b2ac5bf44237b07) | Евгений [@solid_jdk](https://t.me/solid_jdk) | 23 | | [Task-tracker](https://github.com/AleksandrKamen/Task-tracker) | [AleksandrKamen](https://github.com/AleksandrKamen) | Java | | | 24 | | [task-scheduler](https://github.com/DragomirovCode/task-scheduler) | [DragomirovCode](https://github.com/DragomirovCode) | Java | | | 25 | | [task-tracker](https://github.com/farneser/task-tracker) | [farneser](https://github.com/farneser) | Java | | | 26 | | [task-tracker](https://github.com/zshri/task-tracker) | [zshri](https://github.com/zshri) | Java | | | 27 | | [task-tracker](https://github.com/vadimistar/task-tracker) | [vadimistar](https://github.com/vadimistar) | Java | | | 28 | | [TaskTracker](https://github.com/DarkRubin/TaskTracker) | [DarkRubin](https://github.com/DarkRubin) | Java | 📝 [Заметки](https://gist.github.com/Asenim/989924fff0e6dd20448bfc64de341272) | Евгений [@solid_jdk](https://t.me/solid_jdk) | 29 | | [Copytrello](https://github.com/Repinskie/Copytrello) | [Repinskie](https://github.com/Repinskie) | Java | | | 30 | | [planboard](https://github.com/lynxiox/planboard) | [lynxiox](https://github.com/lynxiox) | Java | | | 31 | | [task-tracker](https://github.com/VladShi/task-tracker) | [VladShi](https://github.com/VladShi) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/39bad44da5cfc417b34afbf13a02bbb6) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 32 | -------------------------------------------------------------------------------- /content/finished-projects/tennis-scoreboard.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Табло теннисного матча' 3 | weight = 4 4 | bookTOC = false 5 | +++ 6 | 7 | # Табло теннисного матча 8 | 9 | [ТЗ проекта](../projects/tennis-scoreboard.md) 10 | 11 | 114 реализаций на Java, Python, Kotlin, PHP, Ruby. 42 ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | | [tennisTableboard](https://github.com/Jollykai/tennisTableboard) | [Jollykai](https://github.com/Jollykai) | Java | | | 18 | | [TennisScore](https://github.com/immagixe/TennisScore) | [immagixe](https://github.com/immagixe) | Java | | | 19 | | [tennisscore](https://github.com/AtoDaX/tennisscore) | [AtoDaX](https://github.com/AtoDaX) | Java | 🎬 [Видео](https://t.me/zhukovsd_it_chat/5538) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 20 | | [TennisScoreboard](https://github.com/aseptimu/TennisScoreboard) | [aseptimu](https://github.com/aseptimu) | Java | | | 21 | | [tennis_scoreboard](https://github.com/garaninnv/tennis_scoreboard) | [garaninnv](https://github.com/garaninnv) | Java | | | 22 | | [TennisScoreBoard](https://github.com/RomanV79/TennisScoreBoard) | [RomanV79](https://github.com/RomanV79) | Java | 🎬 [Видео](https://www.youtube.com/watch?v=mI7SICN0ekc) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 23 | | [TennisScoreBoard](https://github.com/d-klokov/TennisScoreBoard) | [d-klokov](https://github.com/d-klokov) | Java | 📝 [Заметки](https://t.me/zhukovsd_it_chat/13171) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 24 | | [TennisScoreboard](https://github.com/kopyshov/TennisScoreboard) | [kopyshov](https://github.com/kopyshov) | Java | 🎬 [Видео](https://t.me/zhukovsd_it_chat/19560) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 25 | | [tennis-scoreboard](https://github.com/farneser/tennis-scoreboard) | [farneser](https://github.com/farneser) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/f18617439273169bfbd9054707bb5c53) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 26 | | [tennis-scoreboard](https://github.com/ImyaFamilia/tennis-scoreboard/tree/main) | [ImyaFamilia](https://github.com/ImyaFamilia) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/36966961ed3ca1223471ae561aa19fbd) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 27 | | [TennisScoreboard](https://github.com/Crystaliseddx/TennisScoreboard) | [Crystaliseddx](https://github.com/Crystaliseddx) | Java | | | 28 | | [tennis-scoreboard](https://github.com/KostaPo/tennis-scoreboard) | [KostaPo](https://github.com/KostaPo) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/1992c9dd50d679cb0b870d2cfb8be493) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 29 | | [scoreboard](https://github.com/rybin-dev/scoreboard) | [rybin-dev](https://github.com/rybin-dev) | Java | | | 30 | | [SmalkoScoreBoard](https://github.com/Smalko1/SmalkoScoreBoard/tree/migration_to_H2_database) | [Smalko1](https://github.com/Smalko1) | Java | | | 31 | | [Tennis-scoreboard](https://github.com/ArtemPronkin/Tennis-scoreboard) | [ArtemPronkin](https://github.com/ArtemPronkin) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/e3ee8db70b496612dd82b4610dd5f94f) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 32 | | [Tennis_Match_Scoreboard](https://github.com/Asenim/Tennis_Match_Scoreboard) | [Asenim](https://github.com/Asenim) | Python | | | 33 | | [Tennis-Scoreboard](https://github.com/Cofisweak/Tennis-Scoreboard) | [Cofisweak](https://github.com/Cofisweak) | Java | | | 34 | | [TennisScoreboard](https://github.com/Victor-Smirnoff/TennisScoreboard) | [Victor-Smirnoff](https://github.com/Victor-Smirnoff) | Python | | | 35 | | [Tennis-match-scoreboard](https://github.com/AleksandrKamen/Tennis-match-scoreboard) | [AleksandrKamen](https://github.com/AleksandrKamen) | Java | | | 36 | | [tennis-scoreboard](https://github.com/Aselivm/tennis-scoreboard) | [Aselivm](https://github.com/Aselivm) | Java | 📝 [Заметки](https://gist.github.com/Asenim/b9cc5d0281141c5049f6625774c1ebb8) | Илья [@coderilya](https://t.me/coderilya) | 37 | | [Scoreboard](https://github.com/privetEdik/Scoreboard/tree/master) | [privetEdik](https://github.com/privetEdik) | Java | | | 38 | | [TennisScoreBoard](https://github.com/Vo1odey/TennisScoreBoard) | [Vo1odey](https://github.com/Vo1odey) | Java | | | 39 | | [tennis_scoreboard](https://github.com/nikron173/tennis_scoreboard) | [nikron173](https://github.com/nikron173) | Java | | | 40 | | [pet-project-06-tennis-scoreboard](https://github.com/Jurdio/pet-project-06-tennis-scoreboard) | [Jurdio](https://github.com/Jurdio) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/bb0d6e3347f1187c73addbe9a46e43da) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 41 | | [Tennis-Scoreboard](https://github.com/dDevusS/Tennis-Scoreboard) | [dDevusS](https://github.com/dDevusS) | Java | | | 42 | | [TennisScoreboard](https://github.com/timmawv/TennisScoreboard) | [timmawv](https://github.com/timmawv) | Java | | | 43 | | [tennis_scoreboard](https://github.com/Dmplo/tennis_scoreboard) | [Dmplo](https://github.com/Dmplo) | Java | | | 44 | | [tennis-scoreboard](https://github.com/YuriyNekludov/tennis-scoreboard) | [YuriyNekludov](https://github.com/YuriyNekludov) | Java | | | 45 | | [TennisScoreboard](https://github.com/medvedev888/TennisScoreboard) | [medvedev888](https://github.com/medvedev888) | Java | | | 46 | | [tennis_scoreboard](https://github.com/Mich4107/tennis_scoreboard) | [Mich4107](https://github.com/Mich4107) | Java | | | 47 | | [tennis-scoreboard](https://github.com/DragomirovCode/tennis-scoreboard) | [DragomirovCode](https://github.com/DragomirovCode) | Java | | | 48 | | [tennis-match-scoreboard](https://github.com/escape-8/tennis-match-scoreboard) | [escape-8](https://github.com/escape-8) | PHP | | | 49 | | [tennis-scoreboard](https://github.com/tonkoshkur/tennis-scoreboard) | [tonkoshkur](https://github.com/tonkoshkur) | Java | | | 50 | | [TennisScoreboard](https://github.com/Mibak87/TennisScoreboard) | [Mibak87](https://github.com/Mibak87) | Java | | | 51 | | [TennisScoreboard](https://github.com/BondarevM/TennisScoreboard) | [BondarevM](https://github.com/BondarevM) | Java | | | 52 | | [TennisMatchScoreboard](https://github.com/u-cha/tennisMatchScoreboard/) | [u-cha](https://github.com/u-cha) | Python | 📝 [Заметки](https://gist.github.com/zhukovsd/8c6a07afd054725ae73a57b48f182e87) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 53 | | [TennisScoreboard](https://github.com/Solo83/TennisScoreboard) | [Solo83](https://github.com/Solo83) | Java | | | 54 | | [tennis_scoreboard](https://github.com/VladislavLevchikIsAProger/tennis_scoreboard) | [VladislavLevchikIsAProger](https://github.com/VladislavLevchikIsAProger) | Java | 📝 [Заметки](https://gist.github.com/Asenim/35d2346498104c4fa46fc6a099638072) | Иван [@makeitvsolo](https://t.me/makeitvsolo) | 55 | | [TennisScoreBoard](https://github.com/DarkRubin/TennisScoreBoard) | [DarkRubin](https://github.com/DarkRubin) | Java | | | 56 | | [tennis_match_scoreboard](https://github.com/chskela/tennis_match_scoreboard) | [chskela](https://github.com/chskela) | Kotlin | | | 57 | | [Tennis-ScoreBoard](https://github.com/ArturChegur/Tennis-ScoreBoard) | [ArturChegur](https://github.com/ArturChegur) | Java | 📝 [Заметки](https://gist.github.com/krios2146/76dcf0e97e1ad099208c081495a6dfc6) | Владимир [@krios2146](https://t.me/krios2146) | 58 | | [tennis-scoreboard](https://github.com/liemartt/tennis-scoreboard) | [liemartt](https://github.com/liemartt) | Java | 📝 [Заметки](https://gist.github.com/krios2146/4bd2b2f111a0e676a91119989aa6f480) | Владимир [@krios2146](https://t.me/krios2146) | 59 | | [Tennis-Scoreboard](https://github.com/grafkust/Tennis-Scoreboard/tree/main) | [grafkust](https://github.com/grafkust) | Java | | | 60 | | [Tennis-Scoreboard-Hibernate](https://github.com/Oldsize/Tennis-Scoreboard-Hibernate) | [Oldsize](https://github.com/Oldsize) | Java | 📝 [Заметки](https://gist.github.com/Asenim/15de753733acf8faf645826ffbe200d8) | Костя [@calmekd](https://t.me/calmekd) | 61 | | [tennis-scoreboard](https://github.com/shinchik17/tennis-scoreboard) | [shinchik17](https://github.com/shinchik17) | Java | | | 62 | | [TennisScoreboard](https://github.com/sergei-neretin/TennisScoreboard) | [sergei-neretin](https://github.com/sergei-neretin) | Java | | | 63 | | [Tennis-Score-Board](https://github.com/Y1-Bit/Tennis-Score-Board) | [Y1-Bit](https://github.com/Y1-Bit) | Python | | | 64 | | [tenis_score](https://github.com/slavik-gassiev/tenis_score/tree/demo_one) | [slavik-gassiev](https://github.com/slavik-gassiev) | Java | | | 65 | | [TennisScoreApp](https://github.com/SahaPWNZ/TennisScoreApp) | [SahaPWNZ](https://github.com/SahaPWNZ) | Java | 📝 [Заметки](https://gist.github.com/Asenim/21960d862b55d452253e3aa3c25bf7f9) | Владимир [@MaddeningShadow](https://t.me/MaddeningShadow) | 66 | | [tennis-scoreboard](https://github.com/damvih03/tennis-scoreboard) | [damvih03](https://github.com/damvih03) | Java | | | 67 | | [Tennis-scoreboard](https://github.com/Nasstyaaa/Tennis-scoreboard) | [Nasstyaaa](https://github.com/Nasstyaaa) | Java | 📝 [Заметки](https://gist.github.com/liemartt/88a1ce4734cf18bcb666e649e6bcd8b8) | Артем [@liemartt](https://t.me/liemartt) | 68 | | [Tennis-Scoreboard](https://github.com/ssss1131/Tennis-Scoreboard) | [ssss1131](https://github.com/ssss1131) | Java | | | 69 | | [tennis-scoreboard](https://github.com/leofinder/tennis-scoreboard) | [leofinder](https://github.com/leofinder) | Java | 📝 [Заметки](https://gist.github.com/Asenim/3239d9cc6e7335239005580801bce08b) | Илья [@coderilya](https://t.me/coderilya) | 70 | | [tennis-scoreboard](https://github.com/aleos-dev/tennis-scoreboard) | [aleos-dev](https://github.com/aleos-dev) | Java | | | 71 | | [tennis_scoreboard](https://github.com/LGAua/tennis_scoreboard) | [LGAua](https://github.com/LGAua) | Java | 📝 [Заметки](https://gist.github.com/Asenim/73f5f64f223aeff96d3ea171dee55769) | Александр [@sahapwnz](https://t.me/sahapwnz) | 72 | | [tennis-scoreboard](https://github.com/Ilyalapin/tennis-scoreboard) | [Ilyalapin](https://github.com/Ilyalapin) | Java | 📝 [Заметки](https://gist.github.com/Asenim/60ac5d8eafe5230724050f63dc71ffac) | Михаил [@dragom1rov](https://t.me/dragom1rov) | 73 | | [tennis-scoreboard](https://github.com/salavei-a/tennis-scoreboard) | [salavei-a](https://github.com/salavei-a) | Java | | | 74 | | [TennisMatchScoreboard](https://github.com/IlyaDudnikov/TennisMatchScoreboard) | [IlyaDudnikov](https://github.com/IlyaDudnikov) | Java | | | 75 | | [Tennis_scoreboard](https://github.com/Wh4tisl0ve/Tennis_scoreboard) | [Wh4tisl0ve](https://github.com/Wh4tisl0ve) | Python | 📝 [Заметки](https://gist.github.com/Asenim/84aa741bb7cd11706929b2c62138e3e9) | Максим [@apostol_fet](https://t.me/apostol_fet) | 76 | | [tennis-scoreboard](https://github.com/red-eyed-99/tennis-scoreboard) | [red-eyed-99](https://github.com/red-eyed-99) | Java | | | 77 | | [TennisMatchScoreboard](https://github.com/EgorFurman/TennisMatchScoreboard) | [EgorFurman](https://github.com/EgorFurman) | Python | 📝 [Заметки](https://gist.github.com/Asenim/71cbb5359181cd1f5a1ff144b79346fa) | Сергей [@grandpraline](https://t.me/grandpraline) | 78 | | [TennisMatchScoreboard](https://github.com/Pashosi/TennisMatchScoreboard) | [Pashosi](https://github.com/Pashosi) | Python | 📝 [Заметки](https://gist.github.com/Asenim/86dec26d4fca31f83ffa8f8c45808a00) | Виктор [@csatom](https://t.me/csatom) | 79 | | [TennisScoreBoard.git](https://github.com/Awakary/TennisScoreBoard.git) | [Awakary](https://github.com/Awakary) | Python | 📝 [Заметки](https://gist.github.com/Asenim/afca15c89b3d4df3a27893d8848957fc) | Сергей [@grandpraline](https://t.me/grandpraline) | 80 | | [tennis-Scoreboard](https://github.com/mak7im-05/tennis-Scoreboard) | [mak7im-05](https://github.com/mak7im-05) | Java | 📝 [Заметки](https://gist.github.com/Asenim/3c4ff9d90c4c519c7f13e3ee86fdc755) | Степан [@xseeljvm](https://t.me/xseeljvm) | 81 | | [TennisScoreboard](https://github.com/AleksandrS112/TennisScoreboard) | [AleksandrS112](https://github.com/AleksandrS112) | Java | | | 82 | | [TenisScoreboard](https://github.com/Mar5sha1l/TenisScoreboard) | [Mar5sha1l](https://github.com/Mar5sha1l) | Java | | | 83 | | [tennis-scoreboard-project](https://github.com/Gevorji/tennis-scoreboard-project) | [Gevorji](https://github.com/Gevorji) | Python | 📝 [Заметки](https://gist.github.com/Asenim/62aae2d9f57cffef92392822855ab77e) | Виктор [@csatom](https://t.me/csatom) | 84 | | [tennis-scoreboard-kotlin](https://github.com/fakechitor/tennis-scoreboard-kotlin) | [fakechitor](https://github.com/fakechitor) | Kotlin | 📝 [Заметки](https://gist.github.com/Asenim/c55a2972616fd08c5d2ff8599ac6c8c6) | Илья [@coderilya](https://t.me/coderilya) | 85 | | [TennisMatchProject.git](https://github.com/PavelFurochkin/TennisMatchProject.git) | [PavelFurochkin](https://github.com/PavelFurochkin) | Python | 📝 [Заметки](https://gist.github.com/Asenim/3658b0589e568d320b4dec495e814ef2) | Сергей [@grandpraline](https://t.me/grandpraline) | 86 | | [Tennis_match_scoreboard](https://github.com/Dmitry-Strog/Tennis_match_scoreboard) | [Dmitry-Strog](https://github.com/Dmitry-Strog) | Python | 📝 [Заметки](https://gist.github.com/Asenim/74b49a9b154097f361aa5e77060992c9) | Виктор [@csatom](https://t.me/csatom) | 87 | | [tennis-scoreboard](https://github.com/MrShoffen/tennis-scoreboard) | [MrShoffen](https://github.com/MrShoffen) | Java | | | 88 | | [TennisScoreboard]( https://github.com/VladShi/TennisScoreboard) | [VladShi](https://github.com/VladShi) | Java | | | 89 | | [TenisScoreBoard](https://github.com/falom07/TenisScoreBoard) | [falom07](https://github.com/falom07) | Java | | | 90 | | [tennis-scoreboard](https://github.com/as1iva/tennis-scoreboard) | [as1iva](https://github.com/as1iva) | Java | | | 91 | | [tennis-score-board](https://github.com/Faust32/tennis-score-board) | [Faust32](https://github.com/Faust32) | Java | | | 92 | | [TennisScoreboard](https://github.com/anton-kulakov/TennisScoreboard) | [anton-kulakov](https://github.com/anton-kulakov) | Java | | | 93 | | [tennis_board](https://github.com/Daniyal-Akhadov/tennis_board) | [Daniyal-Akhadov](https://github.com/Daniyal-Akhadov) | Java | 📝 [Заметки](https://gist.github.com/OlegTihii/0a2b8b685f56f5127f566124f8c2e876) | Степан [@xseeljvm](https://t.me/xseeljvm) | 94 | | [com.saymk.tennistable](https://github.com/saymkarjat/com.saymk.tennistable) | [saymkarjat](https://github.com/saymkarjat) | Java | 📝 [Заметки](https://gist.github.com/liemartt/0c0b669964a5fcc8936ef9800edf1048) | Артем [@liemartt](https://t.me/liemartt) | 95 | | [Tennis](https://github.com/Sss330/Tennis) | [Sss330](https://github.com/Sss330) | Java | 📝 [Заметки](https://gist.github.com/ArturChegur/3610bcfe4e82fabb643c87f0a8545be1) | Артур [@ArturChegur](https://t.me/ArturChegur) | 96 | | [TennisScoreboard](https://github.com/progrohan/TennisScoreboard) | [progrohan](https://github.com/progrohan) | Java | 📝 [Заметки](https://gist.github.com/OlegTihii/7ae19e9b16916ac46640094bc6101b3b) | Влад Левчик [@vladusProgramus](https://t.me/vladusProgramus) | 97 | | [TennisScoreboard.git](https://github.com/legotin212/TennisScoreboard.git) | [legotin212](https://github.com/legotin212) | Java | | | 98 | | [tennis-scoreboard](https://github.com/nebarrow/tennis-scoreboard) | [nebarrow](https://github.com/nebarrow) | Java | | | 99 | | [scoreboard](https://github.com/at0m-cat/scoreboard) | [at0m-cat](https://github.com/at0m-cat) | Java | | | 100 | | [tennis-scoreboard](https://github.com/Iposhka54/tennis-scoreboard) | [Iposhka54](https://github.com/Iposhka54) | Java | | | 101 | | [TennisScoreBoard](https://github.com/IlPl123454/TennisScoreBoard) | [IlPl123454](https://github.com/IlPl123454) | Java | | | 102 | | [tennis_scoreboard](https://github.com/ratmeow/tennis_scoreboard) | [ratmeow](https://github.com/ratmeow) | Python | 📝 [Заметки](https://gist.github.com/OlegTihii/62cb7a144572231a589e18f90e48bfd9) | Виктор [@csatom](https://t.me/csatom) | 103 | | [TennisScoreBoard.git](https://github.com/Darncol/TennisScoreBoard.git) | [Darncol](https://github.com/Darncol) | Java | | | 104 | | [tennis_scoreboard](https://github.com/krios2146/tennis_scoreboard) | [krios2146](https://github.com/krios2146) | Ruby | | | 105 | | [Tennis_Match_Scoreboard](https://github.com/Anikavuk/Tennis_Match_Scoreboard) | [Anikavuk](https://github.com/Anikavuk) | Python | 📝 [Заметки](https://gist.github.com/OlegTihii/8f4424f9e05f97e91930467bc80df01b) | Максим [@apostol_fet](https://t.me/apostol_fet) | 106 | | [Tennis-scoreboard](https://github.com/grunder-96/Tennis-scoreboard) | [grunder-96](https://github.com/grunder-96) | Java | | | 107 | | [TennisScoreboard](https://github.com/Spacier829/TennisScoreboard) | [Spacier829](https://github.com/Spacier829) | Java | | | 108 | | [tennis-scoreboard](https://github.com/fearrux/tennis-scoreboard) | [fearrux](https://github.com/fearrux) | Java | | | 109 | | [Tennis_Score_Board](https://github.com/OlegTihii/Tennis_Score_Board) | [OlegTihii](https://github.com/OlegTihii) | Java | | | 110 | | [tennis-scoreboard](https://github.com/Dirol-Mysteri/tennis-scoreboard) | [Dirol-Mysteri](https://github.com/Dirol-Mysteri) | Java | | | 111 | | [tennisScoreBoard](https://github.com/artyyoom/tennisScoreBoard) | [artyyoom](https://github.com/artyyoom) | Java | | | 112 | | [tennis-match-scoreboard](https://github.com/cakeslayer00/tennis-match-scoreboard) | [cakeslayer00](https://github.com/cakeslayer00) | Java | | | 113 | | [tennisScoreBoard](https://github.com/sess-128/tennisScoreBoard) | [sess-128](https://github.com/sess-128) | Java | | | 114 | | [tennis_score_board](https://github.com/RadomirGross/tennis_score_board) | [RadomirGross](https://github.com/RadomirGross) | Java | | | 115 | | [Tennis-Scoreboard](https://github.com/Dimas-Ukimas/Tennis-Scoreboard) | [Dimas-Ukimas](https://github.com/Dimas-Ukimas) | Java | | | 116 | | [TennisScoreboard](https://github.com/PivovarJV/TennisScoreboard) | [PivovarJV](https://github.com/PivovarJV) | Java | | | 117 | | [tennis-match](https://github.com/velz01/tennis-match) | [velz01](https://github.com/velz01) | Java | 📝 [Заметки](https://github.com/Daniyal-Akhadov/student_review/blob/main/danila-tennis-match.md) | Даниял [@daniyal_daniyal](https://t.me/daniyal_daniyal) | 118 | | [tennis_scoreboard](https://github.com/eternallyu/tennis_scoreboard) | [eternallyu](https://github.com/eternallyu) | Java | 📝 [Заметки](https://github.com/Daniyal-Akhadov/student_review/blob/main/egor-tennis-board.md) | Даниял [@daniyal_daniyal](https://t.me/daniyal_daniyal) | 119 | | [tennis_match_csoreboard_v_2](https://github.com/Gichie/tennis_match_csoreboard_v_2) | [Gichie](https://github.com/Gichie) | Python | | | 120 | | [Tennis_Match_Scoreboard](https://github.com/MaksKav/Tennis_Match_Scoreboard) | [MaksKav](https://github.com/MaksKav) | Java | | | 121 | | [tennis_match_score_table.git](https://github.com/Chukcha1337/tennis_match_score_table.git) | [Chukcha1337](https://github.com/Chukcha1337) | Java | | | 122 | | [TennisScoreboard](https://github.com/dontmax/TennisScoreboard) | [dontmax](https://github.com/dontmax) | Java | | | 123 | | [TennisScoreboard](https://github.com/makson4986/TennisScoreboard) | [makson4986](https://github.com/makson4986) | Java | | | 124 | | [tennis-scoreboard](https://github.com/Rinvel/tennis-scoreboard) | [Rinvel](https://github.com/Rinvel) | Java | 📝 [#1](https://gist.github.com/grishuchkov/b4dd28edd049e500b8223979794cbbbd), 📝 [#2](https://gist.github.com/Badbadr/785a8cd90d43b4f9b785fc21a6ea9ed7) | Данила [@ggnavi](https://t.me/ggnavi), Камил [@badrbad](https://t.me/badrbad) | 125 | | [tennis-dashboard](https://github.com/RustamLee/tennis-dashboard) | [RustamLee](https://github.com/RustamLee) | Java | | | 126 | | [tenis-scoreboard](https://github.com/frost2329/tenis-scoreboard) | [frost2329](https://github.com/frost2329) | Java | 📝 [Заметки](https://gist.github.com/ArturChegur/c6121b81cc06e01f365d7233380169c1) | Артур [@ArturChegur](https://t.me/ArturChegur) | 127 | | [TennisBoard](https://github.com/JaGenn/TennisBoard) | [JaGenn](https://github.com/JaGenn) | Java | 📝 [Заметки](https://gist.github.com/krios2146/038c10107110537039f04f748631ac55) | Владимир [@krios2146](https://t.me/krios2146) | 128 | | [TennisScoreBoard](https://github.com/Dmitry-DVal/TennisScoreBoard) | [Dmitry-DVal](https://github.com/Dmitry-DVal) | Python | | | 129 | | [TennisScoreboard](https://github.com/Niker2023/TennisScoreboard) | [Niker2023](https://github.com/Niker2023) | Java | 📝 [Заметки](https://gist.github.com/Badbadr/1c8f9f2f21538a39ccc5f7765af2f917) | Камил [@badrbad](https://t.me/badrbad) | 130 | | [Tennis-Scoreboard](https://github.com/kivislime/Tennis-Scoreboard) | [kivislime](https://github.com/kivislime) | Java | | | 131 | -------------------------------------------------------------------------------- /content/finished-projects/weather-viewer.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Погода' 3 | weight = 5 4 | bookTOC = false 5 | +++ 6 | 7 | # Погода 8 | 9 | [ТЗ проекта](../projects/weather-viewer.md) 10 | 11 | 97 реализаций на Java, Python, Kotlin, C#, Go, PHP. 37 ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | | [WeatherViewer](https://github.com/immagixe/WeatherViewer) | [immagixe](https://github.com/immagixe) | Java | | | 18 | | [weather-tracker](https://github.com/krios2146/weather-tracker) | [krios2146](https://github.com/krios2146) | Java | 🎬 [Видео](https://www.youtube.com/watch?v=yLBn7qmyCOk) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 19 | | [Tenki](https://github.com/yakaska/Tenki) | [yakaska](https://github.com/yakaska) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/197150aa3691f6f711f4d622526cad2a) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 20 | | [weather](https://github.com/garaninnv/weather) | [garaninnv](https://github.com/garaninnv) | Java | 🎬 [Видео](https://t.me/zhukovsd_it_chat/16352) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 21 | | [weather](https://github.com/RomanV79/weather) | [RomanV79](https://github.com/RomanV79) | Java | 🎬 [Видео](https://t.me/zhukovsd_it_chat/39396), 📝 [Заметки](https://gist.github.com/zhukovsd/fa57fc82447fc6d4d4f0f44f9ae3ef1b) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 22 | | [WeatherApp](https://github.com/d-klokov/WeatherApp) | [d-klokov](https://github.com/d-klokov) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/bee1b4885d854f0cad02ae1a4ac5ff85) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 23 | | [Weather](https://github.com/gassion88/Weather) | [gassion88](https://github.com/gassion88) | Java | | | 24 | | [weather-viewer](https://github.com/farneser/weather-viewer/) | [farneser](https://github.com/farneser) | Java | | | 25 | | [WeatherApp](https://github.com/u-cha/WeatherApp/) | [u-cha](https://github.com/u-cha) | Python | | | 26 | | [WeatherService](https://github.com/ArtemPronkin/WeatherService) | [ArtemPronkin](https://github.com/ArtemPronkin) | Java | 📝 [Заметки](https://gist.github.com/zhukovsd/8988a29b39cef0e651b046e1148ad340) | Сергей [@zhukovsd](https://t.me/zhukovsd) | 27 | | [WeatherViewer](https://github.com/Crystaliseddx/WeatherViewer) | [Crystaliseddx](https://github.com/Crystaliseddx) | Java | | | 28 | | [weather-viewer](https://github.com/urgmaker/weather-viewer) | [urgmaker](https://github.com/urgmaker) | Java | | | 29 | | [WeatherSmalko](https://github.com/Smalko1/WeatherSmalko) | [Smalko1](https://github.com/Smalko1) | Java | | | 30 | | [weather-viewer](https://github.com/Icekubit/weather-viewer) | [Icekubit](https://github.com/Icekubit) | Java | | | 31 | | [Weather](https://github.com/Cofisweak/Weather) | [Cofisweak](https://github.com/Cofisweak) | Java | 📝 [Заметки](https://gist.github.com/Asenim/5a25beeb49ce44e8ee4b1e50ba1d295e) | Костя [@hungryman9](https://t.me/hungryman9) | 32 | | [my-weather](https://github.com/KostaPo/my-weather) | [KostaPo](https://github.com/KostaPo) | Java | | | 33 | | [weather-app](https://github.com/skostia91/weather-app) | [skostia91](https://github.com/skostia91) | Java | | | 34 | | [weather](https://github.com/Aselivm/weather) | [Aselivm](https://github.com/Aselivm) | Java | | | 35 | | [kweather](https://github.com/makeitvsolo/kweather) | [makeitvsolo](https://github.com/makeitvsolo) | Kotlin | | | 36 | | [WeatherViewer](https://github.com/kostinvv/WeatherViewer) | [kostinvv](https://github.com/kostinvv) | C# | | | 37 | | [weather](https://github.com/Victor-Smirnoff/weather) | [Victor-Smirnoff](https://github.com/Victor-Smirnoff) | Python | | | 38 | | [Weather](https://github.com/AleksandrKamen/Weather) | [AleksandrKamen](https://github.com/AleksandrKamen) | Java | 📝 [Заметки](https://gist.github.com/Asenim/5a32d27f0d4af24d575ef786ff9b2f35) | Костя [@hungryman9](https://t.me/hungryman9) | 39 | | [WeatherAPI](https://github.com/YuriyNekludov/WeatherAPI) | [YuriyNekludov](https://github.com/YuriyNekludov) | Java | 📝 [Заметки](https://gist.github.com/Asenim/0983720d9df0363329bab2eb66a3e04c) | Костя [@hungryman9](https://t.me/hungryman9) | 40 | | [weather](https://github.com/nikron173/weather) | [nikron173](https://github.com/nikron173) | Java | 📝 [Заметки](https://gist.github.com/Asenim/ab9ebe9d4f044cbedd18d3f936a24fdc) | Костя [@hungryman9](https://t.me/hungryman9) | 41 | | [WeatherApp](https://github.com/timmawv/WeatherApp) | [timmawv](https://github.com/timmawv) | Java | 📝 [Заметки](https://gist.github.com/Asenim/cdb1300b41e51bec015c0a5be84ae176) | Иван [@makeitvsolo](https://t.me/makeitvsolo) | 42 | | [weather-viewer](https://github.com/StarkovAleksandr1992/weather-viewer) | [StarkovAleksandr1992](https://github.com/StarkovAleksandr1992) | Java | | | 43 | | [Weather](https://github.com/privetEdik/Weather/tree/master) | [privetEdik](https://github.com/privetEdik) | Java | 📝 [Заметки](https://gist.github.com/Asenim/f1839d80ec217032a30225fbf9962fa5) | Иван [@makeitvsolo](https://t.me/makeitvsolo) | 44 | | [weather-tracker](https://github.com/Darfik43/weather-tracker/tree/master) | [Darfik43](https://github.com/Darfik43) | Java | | | 45 | | [WeatherForecast](https://github.com/Nikitavj/WeatherForecast) | [Nikitavj](https://github.com/Nikitavj) | Java | | | 46 | | [weather_viewer](https://github.com/Mich4107/weather_viewer) | [Mich4107](https://github.com/Mich4107) | Java | 📝 [Заметки](https://gist.github.com/Asenim/ced45fd79c4aa27a2510ee8cdafad910) | Костя [@calmekd](https://t.me/calmekd) | 47 | | [Weather_viewer](https://github.com/medvedev888/Weather_viewer) | [medvedev888](https://github.com/medvedev888) | Java | | | 48 | | [WeatherApp](https://github.com/KADI001/WeatherApp/tree/master-unmodules) | [KADI001](https://github.com/KADI001) | Java | 📝 [Заметки](https://gist.github.com/Asenim/461fdfa4cc8f27d275ec26d1f35bd830) | Иван [@makeitvsolo](https://t.me/makeitvsolo) | 49 | | [weather-viewer](https://github.com/XuTpoKoT/weather-viewer) | [XuTpoKoT](https://github.com/XuTpoKoT) | Java | | | 50 | | [WeatherApp](https://github.com/BondarevM/WeatherApp) | [BondarevM](https://github.com/BondarevM) | Java | 📝 [Заметки](https://gist.github.com/Asenim/d6e149730f740acbb422142527d40c38) | Илья [@coderilya](https://t.me/coderilya) | 51 | | [weather](https://github.com/tonkoshkur/weather) | [tonkoshkur](https://github.com/tonkoshkur) | Java | | | 52 | | [weather-viewer](https://github.com/escape-8/weather-viewer) | [escape-8](https://github.com/escape-8) | PHP | | | 53 | | [weather_tracker](https://github.com/VladislavLevchikIsAProger/weather_tracker) | [VladislavLevchikIsAProger](https://github.com/VladislavLevchikIsAProger) | Java | 📝 [Заметки](https://gist.github.com/Asenim/e62220c86a91e2a3a4543ec9cb4ccea5) | Владимир [@krios2146](https://t.me/krios2146) | 54 | | [WeatherApp](https://github.com/Solo83/WeatherApp) | [Solo83](https://github.com/Solo83) | Java | | | 55 | | [weather-oracle](https://github.com/dDevusS/weather-oracle) | [dDevusS](https://github.com/dDevusS) | Java | | | 56 | | [weather](https://github.com/DragomirovCode/weather) | [DragomirovCode](https://github.com/DragomirovCode) | Java | | | 57 | | [Weather](https://github.com/Mibak87/Weather) | [Mibak87](https://github.com/Mibak87) | Java | | | 58 | | [WeatherApp](https://github.com/DarkRubin/WeatherApp) | [DarkRubin](https://github.com/DarkRubin) | Java | | | 59 | | [weather-app](https://github.com/liemartt/weather-app) | [liemartt](https://github.com/liemartt) | Java | 📝 [Заметки](https://gist.github.com/krios2146/8a9c38bf66dbbfabd8645c7f1a61f567) | Владимир [@krios2146](https://t.me/krios2146) | 60 | | [WeatherViewer](https://github.com/sergei-neretin/WeatherViewer) | [sergei-neretin](https://github.com/sergei-neretin) | Java | | | 61 | | [WeatherAPI](https://github.com/AlisaDank/WeatherAPI) | [AlisaDank](https://github.com/AlisaDank) | Java | 📝 [Заметки](https://gist.github.com/Asenim/5fd757413752c107e165daf037d3a486) | Илья [@coderilya](https://t.me/coderilya) | 62 | | [Weather](https://github.com/ArturChegur/Weather) | [ArturChegur](https://github.com/ArturChegur) | Java | 📝 [Заметки](https://gist.github.com/krios2146/79b5cc0325dae7b36d54a6fa16eb855b) | Владимир [@krios2146](https://t.me/krios2146) | 63 | | [weather_viewer](https://github.com/user873475320/weather_viewer) | [user873475320](https://github.com/user873475320) | Java | | | 64 | | [weather_servlet_app](https://github.com/chskela/weather_servlet_app) | [chskela](https://github.com/chskela) | Kotlin | | | 65 | | [weather-app](https://github.com/shinchik17/weather-app) | [shinchik17](https://github.com/shinchik17) | Java | 📝 [Заметки](https://github.com/shinchik17/weather-app/issues/1) | Aleos [@HTSWT](https://t.me/HTSWT) | 66 | | [WeatherForecastAPI](https://github.com/grafkust/WeatherForecastAPI) | [grafkust](https://github.com/grafkust) | Java | | | 67 | | [Weather](https://github.com/Nasstyaaa/Weather) | [Nasstyaaa](https://github.com/Nasstyaaa) | Java | 📝 [Заметки](https://gist.github.com/liemartt/0f99d8b3bfe7c423ace964cce436d40a) | Артем [@liemartt](https://t.me/liemartt) | 68 | | [weatherViewerApp](https://github.com/SahaPWNZ/weatherViewerApp) | [SahaPWNZ](https://github.com/SahaPWNZ) | Java | 📝 [Заметки](https://gist.github.com/Asenim/93c8495ce55028e1f11978af0403c1d4) | Илья [@coderilya](https://t.me/coderilya) | 69 | | [weather_tracker](https://github.com/ssss1131/weather_tracker) | [ssss1131](https://github.com/ssss1131) | Java | | | 70 | | [go-weather-viewer](https://github.com/albakov/go-weather-viewer) | [albakov](https://github.com/albakov) | Go | 📝 [Заметки](https://gist.github.com/albakov/a538ebc1ce539e4e70661ce97d66584d) | Автор неизвестен | 71 | | [WeatherApp](https://github.com/karimpatvari/WeatherApp) | [karimpatvari](https://github.com/karimpatvari) | Java | | | 72 | | [weather-viewer](https://github.com/leofinder/weather-viewer) | [leofinder](https://github.com/leofinder) | Java | | | 73 | | [weather-tracker](https://github.com/aleos-dev/weather-tracker) | [aleos-dev](https://github.com/aleos-dev) | Java | | | 74 | | [weather-tracker](https://github.com/salavei-a/weather-tracker) | [salavei-a](https://github.com/salavei-a) | Java | | | 75 | | [WeatherApp](https://github.com/Wh4tisl0ve/WeatherApp) | [Wh4tisl0ve](https://github.com/Wh4tisl0ve) | Python | | | 76 | | [weatherViewer](https://github.com/EgorFurman/weatherViewer) | [EgorFurman](https://github.com/EgorFurman) | Python | 📝 [Заметки](https://gist.github.com/Asenim/aa26e724619a4273c623b6e620438a6c) | Сергей [@grandpraline](https://t.me/grandpraline) | 77 | | [weather-tracker](https://github.com/VladShi/weather-tracker) | [VladShi](https://github.com/VladShi) | Java | | | 78 | | [weather-tracker](https://github.com/Ilyalapin/weather-tracker) | [Ilyalapin](https://github.com/Ilyalapin) | Java | 📝 [Заметки](https://gist.github.com/OlegTihii/0156b365e8f9dc9effb45f26fd619348) | Артем [@pronkin_artem](https://t.me/pronkin_artem) | 79 | | [DjangoProjectWeather](https://github.com/Pashosi/DjangoProjectWeather) | [Pashosi](https://github.com/Pashosi) | Python | | | 80 | | [WeatherApp.git](https://github.com/slavik-gassiev/WeatherApp.git) | [slavik-gassiev](https://github.com/slavik-gassiev) | Java | | | 81 | | [weather-app](https://github.com/Yvnushevskiy/weather-app) | [Yvnushevskiy](https://github.com/Yvnushevskiy) | Java | | | 82 | | [weather-app-rest-api](https://github.com/MrShoffen/weather-app-rest-api) | [MrShoffen](https://github.com/MrShoffen) | Java | | | 83 | | [weather-tracker-kotlin](https://github.com/fakechitor/weather-tracker-kotlin) | [fakechitor](https://github.com/fakechitor) | Kotlin | | | 84 | | [WeatherApp](https://github.com/mak7im-05/WeatherApp) | [mak7im-05](https://github.com/mak7im-05) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/99f70e7e1af2472370ad1a8533b8fd19) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 85 | | [com.saymk.weathertracker](https://github.com/saymkarjat/com.saymk.weathertracker) | [saymkarjat](https://github.com/saymkarjat) | Java | | | 86 | | [WeatherApp](https://github.com/Dmitry-Strog/WeatherApp) | [Dmitry-Strog](https://github.com/Dmitry-Strog) | Python | | | 87 | | [weather-app](https://github.com/as1iva/weather-app) | [as1iva](https://github.com/as1iva) | Java | 📝 [Заметки](https://gist.github.com/krios2146/addd8b6b0f7dcb16f7a2739da6e35984) | Владимир [@krios2146](https://t.me/krios2146) | 88 | | [weather-viewer](https://github.com/Iposhka54/weather-viewer) | [Iposhka54](https://github.com/Iposhka54) | Java | | | 89 | | [weather](https://github.com/at0m-cat/weather) | [at0m-cat](https://github.com/at0m-cat) | Java | | | 90 | | [weather_roadmap](https://github.com/Daniyal-Akhadov/weather_roadmap) | [Daniyal-Akhadov](https://github.com/Daniyal-Akhadov) | Java | | | 91 | | [weather-tracker](https://github.com/nebarrow/weather-tracker) | [nebarrow](https://github.com/nebarrow) | Java | 📝 [Заметки](https://gist.github.com/Kek20703/6ee5d13078d015c022fccf05906f6b4f) | Артем [@legotin212](https://t.me/legotin212) | 92 | | [WeatherApp](https://github.com/anton-kulakov/WeatherApp) | [anton-kulakov](https://github.com/anton-kulakov) | Java | | | 93 | | [WeatherRestApi](https://github.com/progrohan/WeatherRestApi) | [progrohan](https://github.com/progrohan) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/c8248131deea611127eea1f5c66c6084) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 94 | | [Weather_viewer](https://github.com/PavelFurochkin/Weather_viewer) | [PavelFurochkin](https://github.com/PavelFurochkin) | Python | | | 95 | | [WeatherService-](https://github.com/Awakary/WeatherService-) | [Awakary](https://github.com/Awakary) | Python | 📝 [Заметки](https://gist.github.com/OlegTihii/c0011664cc4f716a91a5c61772905963) | Сергей [@grandpraline](https://t.me/grandpraline) | 96 | | [WeatherApp](https://github.com/Kek20703/WeatherApp) | [Kek20703](https://github.com/Kek20703) | Java | 📝 [Заметки](https://gist.github.com/nebarrow/726159d574e7ce2086b66756d0da50c0) | Тарас [@nebarrow](https://t.me/nebarrow) | 97 | | [Weather](https://github.com/Sss330/Weather) | [Sss330](https://github.com/Sss330) | Java | | | 98 | | [Weather](https://github.com/Bigtoyka/Weather) | [Bigtoyka](https://github.com/Bigtoyka) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/70aee8d4a3fd980a1782bfed716be35a) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 99 | | [weather-tracker](https://github.com/AriiSib/weather-tracker) | [AriiSib](https://github.com/AriiSib) | Java | 📝 [Заметки](https://github.com/evg-rdm-reviews/project-reviews/blob/master/weather-app/REVIEW_299792458.md) | Евгений [@solid_jdk](https://t.me/solid_jdk) | 100 | | [weather](https://github.com/artyyoom/weather) | [artyyoom](https://github.com/artyyoom) | Java | | | 101 | | [weather](https://github.com/0-Luntik-0/weather) | [0-Luntik-0](https://github.com/0-Luntik-0) | Java | | | 102 | | [weatherAppV2](https://github.com/sess-128/weatherAppV2) | [sess-128](https://github.com/sess-128) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/1d76c729c7c7e68b65b31779f943c50f) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 103 | | [Weather.git](https://github.com/Gichie/Weather.git) | [Gichie](https://github.com/Gichie) | Python | | | 104 | | [weather-app](https://github.com/cakeslayer00/weather-app) | [cakeslayer00](https://github.com/cakeslayer00) | Java | 📝 [Заметки](https://gist.github.com/DarkRubin/926b3617d5e581c460124d50211c7dcc) | Вадим [@oneQwerty2](https://t.me/oneQwerty2) | 105 | | [WeatherProject-Dev](https://github.com/PivovarJV/WeatherProject-Dev) | [PivovarJV](https://github.com/PivovarJV) | Java | | | 106 | | [Weather_Radar](https://github.com/MaksKav/Weather_Radar) | [MaksKav](https://github.com/MaksKav) | Java | | | 107 | | [weather.git](https://github.com/Chukcha1337/weather) | [Chukcha1337](https://github.com/Chukcha1337) | Java | 📝 [Заметки](https://gist.github.com/Badbadr/c756a52f8d82110396a06976d7d74478) | Камил [@badrbad](https://t.me/badrbad) | 108 | | [weather](https://github.com/Rinvel/weather) | [Rinvel](https://github.com/Rinvel) | Java | | | 109 | | [Weather](https://github.com/GogAndMagog/Weather) | [GogAndMagog](https://github.com/GogAndMagog) | Java | | | 110 | | [weather-tracker](https://github.com/ratmeow/weather-tracker) | [ratmeow](https://github.com/ratmeow) | Python | | | 111 | | [Weather](https://github.com/Dimas-Ukimas/Weather) | [Dimas-Ukimas](https://github.com/Dimas-Ukimas) | Java | | | 112 | | [Weather-tracker](https://github.com/grunder-96/Weather-tracker) | [grunder-96](https://github.com/grunder-96) | Java | | | 113 | | [Weather](https://github.com/makson4986/Weather) | [makson4986](https://github.com/makson4986) | Java | | | 114 | -------------------------------------------------------------------------------- /content/plan.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'План действий' 3 | weight = 1 4 | +++ 5 | 6 | # План действий от начала учёбы до трудоустройства 7 | 8 | Вводные - вы выбрали бэкенд разработку и определились с языком. Топ 4 варианта, по моему мнению: 9 | - Java + Spring 10 | - Python + Django/Flask/FastAPI 11 | - .NET 12 | - PHP + Laravel 13 | 14 | Советую посмотреть: 15 | - [Стрим](https://www.youtube.com/watch?v=Y1SmjcSGQjQ) с обзором рынка труда и актуальных вариантов на ноябрь 2023. 16 | 17 | Мои материалы в данном роадмапе ориентированы на Java, есть адаптация роадмапа под Python - [https://zhukovsd.github.io/python-backend-learning-course/](https://zhukovsd.github.io/python-backend-learning-course/). 18 | 19 | ## Постановка целей и подготовка 20 | 21 | Очень важно поставить главную цель (трудоустройство) и отфильтровать всё ненужное - идеи сделать коммерческий проект будучи новичком, идеи подрабатывать на фрилансе. У этих идей есть право на жизнь, но они отнимают время у главной цели. 22 | 23 | Советую почитать: 24 | - Факторы, которые определят ваш успех (или неуспех) в учебе и трудоустройстве - [https://telegra.ph/Faktory-uspeha-v-uchebe-i-trudoustrojstve-08-28](https://telegra.ph/Faktory-uspeha-v-uchebe-i-trudoustrojstve-08-28) 25 | - Этапы развития начинающего разработчика и типовые ошибки - [https://telegra.ph/EHtapy-razvitiya-nachinayushchego-razrabotchika-i-tipovye-oshibki-01-13](https://telegra.ph/EHtapy-razvitiya-nachinayushchego-razrabotchika-i-tipovye-oshibki-01-13) 26 | 27 | ## Изучение синтаксиса 28 | 29 | Разработка - практический навык, но для того, чтобы начать писать проекты, необходим базовый минимум. Для его изучения подойдут книги и видеокурсы. В материалах роадмапа есть [список](./technologies/java.md#избранные-курсы-и-учебные-ресурсы) избранных учебных материалов по Java. 30 | 31 | Параллельно изучению книги и курса, рекомендую набивать руку на несложных алгоритмических задачах. Найти их можно, например, на [https://leetcode.com/](https://leetcode.com/) (подойдут задачи сложности easy). 32 | 33 | На этом этапе принципиально важно встроить процесс учёбы в вашу жизнь. Учёба - марафон, и достичь результата поможет только рутинная работа. Минимум - 10-15 часов в неделю, больше - лучше, но в разумных пределах. Послушать, как с этим справлялись уже трудоустроенные студенты можно у меня на YouTube, в плейлисте ["подкасты со студентами"](https://www.youtube.com/playlist?list=PLOVOZrcS3XMbjLwcF9uxbjsdHuMqbvPdp). 34 | 35 | Для перехода к первому практическому проекту хватит базовых знаний по синтаксису (переменные, типы данных, ветвления, циклы, операторы, коллекции, дженерики) и 20-30 решенных задач. 36 | 37 | Советую почитать: 38 | - Как эффективно читать книги по разработке - https://t.me/zhukovsd_it_mentor/60 39 | - Привычки для эффективной учёбы и работы - https://t.me/zhukovsd_it_mentor/91 40 | - Мои практики повышения продуктивности и профилактики выгорания - https://t.me/zhukovsd_it_mentor/65 41 | 42 | ## Работа над пет проектами 43 | 44 | Основной этап обучения. Роадмап включает в себя [матрицу навыков](./_index.md#схема-навыков) и [технические задания](./_index.md#проекты-с-техзаданиями) 7 проектов, которые помогают поступательно освоить навыки из матрицы ([стрим](https://www.youtube.com/watch?v=4B21MDbtbWE) по матрице). 45 | 46 | Процесс работы над каждым проектом: 47 | - Ознакомиться с ТЗ и списком необходимых для реализации проекта технологий и идей. 48 | - В разделе [Требуемые знания и технологии](./_index.md#требуемые-знания-и-технологии) (или в другом месте по вкусу) выбрать образовательные ресурсы для изучения новых для вас вещей. Важно [не увлекаться](https://t.me/zhukovsd_it_mentor/92) с подготовительным этапом, и не затягивать с переходом к реализации проекта. 49 | - Работа над проектом - реализация функционала, решение проблем. Рекомендую не смотреть чужие реализации проекта, до тех пор пока не доведёте свою до работающего состояния. 50 | - Работа над ошибками - в конце ТЗ есть список типовых ошибок проблем. Проверьте свой проект на их наличие, чтобы послушать, как исправлять эти ошибки, ознакомьтесь с [моими ревью](./finished-projects/_index.md) к проектам из ТЗ. 51 | - Скиньте готовую реализацию мне в [чат](https://t.me/zhukovsd_it_chat). По возможности, я и другие студенты даём обратную связь. 52 | - Переход к следующему проекту. 53 | 54 | Советую почитать: 55 | - Зачем писать пет-проекты с устаревшими технологиями? - [https://t.me/zhukovsd_it_mentor/81](https://t.me/zhukovsd_it_mentor/81) 56 | - Разница между рабочими и учебными проектами - https://t.me/zhukovsd_it_mentor/49 57 | 58 | ## Поиск работы 59 | 60 | На этапе работы над 5-6 пет проектами можно приступать к составлению резюме и пробовать откликаться. Если вы - студент университета, то можете рассмотреть стажировки. Прохождение собеседований - навык, требующий практики. Первая цель - обеспечить себе поток технических собеседований, для этого ваше резюме должно проходить актуальные на данный момент фильтры по наличию необходимых технологий и опыта. 61 | 62 | Советую почитать: 63 | - Сложности поиска первой работы и накрутка опыта в резюме - [https://t.me/zhukovsd_it_mentor/96](https://t.me/zhukovsd_it_mentor/96) 64 | 65 | --- 66 | 67 | Пишите о своих успехах мне в личные сообщения и в чат. В итогах месяца на [Telegram канале](https://t.me/zhukovsd_it_mentor) я публикую истории трудоустройств. 68 | 69 | --- 70 | 71 | Дополнительные ресурсы в помощь: 72 | - [Чат](https://t.me/zhukovsd_it_chat) сообщества для обсуждения любых вопросов связанных с проектами и учёбой 73 | - [Личные консультации](https://t.me/zhukovsd_it_mentor/98) со мной 74 | -------------------------------------------------------------------------------- /content/projects/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'ТЗ Проектов' 3 | weight = 10 4 | bookFlatSection = true 5 | +++ 6 | -------------------------------------------------------------------------------- /content/projects/cloud-file-storage.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Облачное хранилище файлов' 3 | weight = 6 4 | +++ 5 | 6 | # Проект "Облачное хранилище файлов" 7 | 8 | Многопользовательское файловое облако. Пользователи сервиса могут использовать его для загрузки и хранения файлов. Источником вдохновения для проекта является Google Drive. 9 | 10 | ## Что нужно знать 11 | 12 | - [Java](../technologies/java.md) - коллекции, ООП 13 | - [Maven/Gradle](../technologies/build-systems.md) 14 | - [Backend](../technologies/backend.md) 15 | - Spring Boot, Spring Security, Spring Sessions 16 | - REST, Swagger, Upload файлов 17 | - Cookies, cессии 18 | - [Базы данных](../technologies/databases.md) 19 | - SQL 20 | - Spring Data JPA 21 | - Миграции 22 | - Представление о NoSQL хранилищах 23 | - [Frontend](../technologies/frontend.md) - HTML/CSS, Bootstrap 24 | - [Тесты](../technologies/tests.md) - интеграционное тестирование, JUnit, Testcontainers 25 | - [Docker](../technologies/microservices.md#docker) - контейнеры, образы, volumes, Docker Compose 26 | - [Деплой](../technologies/dev-ops.md#деплой) - облачный хостинг, командная строка Linux, Tomcat 27 | 28 | ## Мотивация проекта 29 | 30 | - Использование возможностей Spring Boot 31 | - Практика с Docker и Docker Compose 32 | - Первый проект, где студент самостоятельно разрабатывает структуру БД 33 | - Знакомство с NoSQL хранилищами - S3 для файлов, Redis для сессий 34 | - Интеграция по REST с одностраничным frontend приложением на React 35 | 36 | ## Функционал приложения 37 | 38 | Работа с пользователями: 39 | 40 | - Регистрация 41 | - Авторизация 42 | - Logout 43 | 44 | Работа с файлами и папками: 45 | 46 | - Загрузка (upload) файлов и папок 47 | - Создание новой пустой папки (аналогично созданию новой папки в проводнике) 48 | - Удаление 49 | - Переименование и перемещение 50 | - Скачивание файлов и папок 51 | 52 | ## REST API 53 | 54 | - Архитектурный стиль - RPC для авторизации и регистрации, REST для всего остального. 55 | - Все эндпоинты существуют под общим путём `/api`. Пути ниже относительны его, пример - `/api/auth/sign-up`. 56 | - Механизм авторизации - сессии. 57 | - Формат запросов и ответов - JSON, кроме скачивания и аплоада файлов. 58 | 59 | ### Ответ в случае ошибки 60 | 61 | Актуально для всех методов. 62 | 63 | Тело ответа: 64 | ```json 65 | { 66 | "message": "Текст ошибки" 67 | } 68 | ``` 69 | 70 | Тело ответа может содержать другие поля (которые по-умолчанию отправляет Spring). 71 | 72 | ### Регистрация и авторизация 73 | 74 | #### Регистрация 75 | 76 | `POST /auth/sign-up` 77 | 78 | Тело запроса (`application/json`): 79 | ```json 80 | { 81 | "username": "user_1", 82 | "password": "password" 83 | } 84 | ``` 85 | 86 | Ответ в случае успеха: `201 Created` со следующим телом: 87 | 88 | ```json 89 | { 90 | "username": "user_1" 91 | } 92 | ``` 93 | 94 | При регистрации юзеру сразу создаётся сессия и выставляется кука. 95 | 96 | Коды ошибок: 97 | - 400 - ошибки валидации (пример - слишком короткий username) 98 | - 409 - username занят 99 | - 500 - неизвестная ошибка 100 | 101 | #### Авторизация 102 | 103 | `POST /auth/sign-in` 104 | 105 | Тело запроса (`application/json`): 106 | ```json 107 | { 108 | "username": "user_1", 109 | "password": "password" 110 | } 111 | ``` 112 | 113 | Тело ответа в случае успеха (`200 OK`): 114 | ```json 115 | { 116 | "username": "user_1" 117 | } 118 | ``` 119 | 120 | Коды ошибок: 121 | - 400 - ошибки валидации (пример - слишком короткий username) 122 | - 401 - неверные данные (такого пользователя нет, или пароль неправильный) 123 | - 500 - неизвестная ошибка 124 | 125 | #### Выход из аккаунта (логаут) 126 | 127 | `POST /auth/sign-out` 128 | 129 | Тела запроса нет. 130 | 131 | Тело ответа в случае успеха (`204 No Content`) пустое. 132 | 133 | Коды ошибок: 134 | - 401 - запрос исполняется неавторизованным юзером 135 | - 500 - неизвестная ошибка 136 | 137 | ### Пользователи 138 | 139 | #### Текущий пользователь 140 | 141 | `GET /user/me` 142 | 143 | Ответ в случае успеха: `200 OK` со следующим телом: 144 | 145 | ```json 146 | { 147 | "username": "user_1" 148 | } 149 | ``` 150 | 151 | Коды ошибок: 152 | - 401 - пользователь не авторизован 153 | - 500 - неизвестная ошибка 154 | 155 | ### Работа с файлами и папками 156 | 157 | Терминология: 158 | - Ресурс - файл или папка 159 | - Путь к ресурсу - полный путь состоит из иерархии папок, плюс имени ресурса 160 | - Пример для файла - `folder1/folder2/file.txt` 161 | - Пример для папки - `folder1/folder2/` 162 | 163 | #### Ресурсы 164 | 165 | **Получение информации о ресурсе** 166 | 167 | `GET /resource?path=$path` 168 | 169 | Для всех запросов ниже, параметр path - полный путь к ресурсу в url-encoded формате. Путь к папке должен заканчиваться на `/`. Это необходимо, чтобы отличить папку и файл с одинаковым названием, которые могут сосуществовать вместе в одной корневой директории. 170 | 171 | Ответ в случае успеха: `200 OK` со следующим телом (`appication/json`): 172 | 173 | ```json 174 | { 175 | "path": "folder1/folder2/", // путь к папке, в которой лежит ресурс 176 | "name": "file.txt", 177 | "size": 123, // размер файла в байтах. Если ресурс - папка, это поле отсутствует 178 | "type": "FILE" // DIRECTORY или FILE 179 | } 180 | ``` 181 | 182 | Коды ошибок: 183 | - 400 - невалидный или отсутствующий путь 184 | - 401 - пользователь не авторизован 185 | - 404 - ресурс не найден 186 | - 500 - неизвестная ошибка 187 | 188 | **Удаление ресурса** 189 | 190 | `DELETE /resource?path=$path` 191 | 192 | Ответ в случае успеха: `204 No Content` без тела: 193 | 194 | Коды ошибок: 195 | - 400 - невалидный или отсутствующий путь 196 | - 401 - пользователь не авторизован 197 | - 404 - ресурс не найден 198 | - 500 - неизвестная ошибка 199 | 200 | **Скачивание ресурса** 201 | 202 | `GET /resource/download?path=$path` 203 | 204 | Ответ в случае успеха - `200 OK` и бинарное содержимое файла с Content-Type: `application/octet-stream`. 205 | 206 | Папка скачивается в формате zip архива её содержимого. 207 | 208 | Коды ошибок: 209 | - 400 - невалидный или отсутствующий путь 210 | - 401 - пользователь не авторизован 211 | - 404 - ресурс не найден 212 | - 500 - неизвестная ошибка 213 | 214 | **Переименование/перемещение ресурса** 215 | 216 | `GET /resource/move?from=$from&to=$to` 217 | 218 | GET параметры - старый и новый полные пути к ресурсу в URL-encoded формате. 219 | 220 | - При переименовании меняется только имя файла 221 | - При перемещении меняется только путь к файлу 222 | 223 | Ответ в случае успеха - `200 OK`. Тело (`application/json`): 224 | ```json 225 | { 226 | "path": "folder1/folder2/", // путь к папке, в которой лежит перемещённый ресурс 227 | "name": "file.txt", // имя перемещённого ресурса 228 | "size": 123, // размер файла в байтах. Если ресурс - папка, это поле отсутствует 229 | "type": "FILE" // DIRECTORY или FILE 230 | } 231 | ``` 232 | 233 | Коды ошибок: 234 | - 400 - невалидный или отсутствующий путь 235 | - 401 - пользователь не авторизован 236 | - 404 - ресурс не найден 237 | - 409 - ресурс, лежащий по пути `to` уже существует 238 | - 500 - неизвестная ошибка 239 | 240 | **Поиск** 241 | 242 | `GET /resource/search?query=$query` 243 | 244 | GET параметр `query` - поисковый запрос в URL-encoded формате. 245 | 246 | Ответ в случае успеха: `200 OK` со следующим телом (`appication/json`). Коллекция найденных ресурсов: 247 | 248 | ```json 249 | [ 250 | { 251 | "path": "folder1/folder2/", // путь к папке, в которой лежит ресурс 252 | "name": "file.txt", 253 | "size": 123, // размер файла в байтах. Если ресурс - папка, это поле отсутствует 254 | "type": "FILE" // DIRECTORY или FILE 255 | } 256 | ] 257 | ``` 258 | 259 | Коды ошибок: 260 | - 400 - невалидный или отсутствующий поисковый запрос 261 | - 401 - пользователь не авторизован 262 | - 500 - неизвестная ошибка 263 | 264 | **Аплоад** 265 | 266 | POST `resource?path=$path` 267 | 268 | GET параметр `path` - путь к папке, в которую мы загружаем ресурс(ы). 269 | 270 | Тело запроса содержит данные из file input в формате MultipartFile. Если в имени файла будет указана поддиректория (например, upload_folder/test.txt) - то при загрузке в storage_folder/ будет создана такая директория. В итоге файл после загрузки будет находиться в storage_folder/upload_folder/test.txt. Это позволяет загружать файлы, папки и рекурсивно вложенные подпапки одним запросом. 271 | 272 | Ответ в случае успеха: `201 Created` со следующим телом (`appication/json`). Коллекция загруженных ресурсов: 273 | 274 | ```json 275 | [ 276 | { 277 | "path": "folder1/folder2/", // путь к папке, в которой лежит ресурс 278 | "name": "file.txt", 279 | "size": 123, // размер файла в байтах. Если ресурс - папка, это поле отсутствует 280 | "type": "FILE" // DIRECTORY или FILE 281 | } 282 | ] 283 | ``` 284 | 285 | Коды ошибок: 286 | - 400 - невалидное тело запроса 287 | - 409 - файл уже существует 288 | - 401 - пользователь не авторизован 289 | - 500 - неизвестная ошибка 290 | 291 | **Папки** 292 | 293 | Получение информации о содержимом папки 294 | 295 | `GET /directory?path=$path` 296 | 297 | Ответ в случае успеха: `200 OK` со следующим телом (`appication/json`). Коллекция ресурсов, лежащих в папке (не рекурсивно): 298 | 299 | ```json 300 | [ 301 | { 302 | "path": "folder1/folder2/", // путь к папке, в которой лежит ресурс 303 | "name": "file.txt", 304 | "size": 123, // размер файла в байтах. Если ресурс - папка, это поле отсутствует 305 | "type": "FILE" // DIRECTORY или FILE 306 | } 307 | ] 308 | ``` 309 | 310 | Коды ошибок: 311 | - 400 - невалидный или отсутствующий путь 312 | - 401 - пользователь не авторизован 313 | - 404 - папка не существует 314 | - 500 - неизвестная ошибка 315 | 316 | **Создание пустой папки** 317 | 318 | `POST /directory?path=$path` 319 | 320 | Ответ в случае успеха: `201 Created` со следующим телом (`appication/json`). Ресурс созданной папки: 321 | 322 | ```json 323 | [ 324 | { 325 | "path": "folder1/folder2/", // путь к папке, в которой лежит ресурс 326 | "name": "folder3", 327 | "size": 123, // размер файла в байтах. Если ресурс - папка, это поле отсутствует 328 | "type": "DIRECTORY" // DIRECTORY или FILE 329 | } 330 | ] 331 | ``` 332 | 333 | Коды ошибок: 334 | - 400 - невалидный или отсутствующий путь к новой папке 335 | - 401 - пользователь не авторизован 336 | - 404 - Родительская папка не существует 337 | - 409 - папка уже существует 338 | - 500 - неизвестная ошибка 339 | 340 | ## Работа с сессиями, авторизацией, регистрацией 341 | 342 | В предыдущем проекте мы управляли сессиями пользователей вручную, в этом проекте воспользуемся возможности экосистемы Spring Boot. 343 | 344 | За авторизацию, управление доступом к страницам отвечает Spring Security. 345 | 346 | За работу с сессиями отвечает Spring Sessions. По умолчанию Spring Boot хранит сессии внутри приложения, и они теряются после каждого перезапуска приложения. Мы воспользуемся Redis для хранения сессий. Пример - [https://www.baeldung.com/spring-session](https://www.baeldung.com/spring-session). Redis - NoSQL хранилище, имеющее встроенный TTL (time to live) атрибут для записей, что делает его удобным для хранения сессий - истекшие сессии автоматически удаляются. 347 | 348 | ## Swagger 349 | 350 | Задачи: 351 | - Интегрировать Swagger в проект 352 | - Документировать REST API методы с помощью аннотаций 353 | - Проверить работоспособность методов API через Swagger UI 354 | 355 | ## SQL база данных 356 | 357 | В этом проекте студент самостоятельно разрабатывает структуру базы данных для хранения пользователей (файлы и сессии располагаются в других хранилищах). Предлагаю использовать Postgres/MySQL/MariaDB. 358 | 359 | Ориентироваться стоит на интеграцию с Spring Security. Эта библиотека экосистемы Spring подразумевает определённые атрибуты, которыми должен обладать пользователь, и список которых и станет основой колонок для таблицы `Users`. 360 | 361 | Пример интеграции между Spring Security и Spring Data JPA - [https://www.baeldung.com/registration-with-spring-mvc-and-spring-security](https://www.baeldung.com/registration-with-spring-mvc-and-spring-security). 362 | 363 | Важно помнить о создании необходимых индексов в таблице `Users`. Например, логин пользователя должен быть уникальным. 364 | 365 | ### Миграции 366 | 367 | Схема БД в этом проекте очень простая, но тем не менее рекомендую попрактиковаться с миграциями. Если в прошлом проекте вы использовали Flyway, в этом можно взять Liquibase, или наоборот. 368 | 369 | ## Хранилище файлов S3 370 | 371 | Для хранения файлов будем пользоваться S3 - simple storage service. Проект, разработанный Amazon Cloud Services, представляет из себя облачный сервис и протокол для файлового хранилища. Чтобы не зависеть от платных сервисов Amazon в этом проекте, воспользуемся альтернативным S3-совместимым хранилищем, которое можно запустить локально - [https://min.io/](https://min.io/) 372 | 373 | - Докер образ для локального запуска MinIO - [https://hub.docker.com/r/minio/minio/](https://hub.docker.com/r/minio/minio/) 374 | - Для работы с протоколом S3 воспользуемся [Minio Java SDK](https://min.io/docs/minio/linux/developers/java/minio-java.html) 375 | 376 | ### Структура S3 хранилища 377 | 378 | В SQL мы оперируем таблицами, в S3 таблиц не существует, вместо этого S3 оперирует бакетами (bucket - корзина) с файлами. Чтобы понять что такое бакет, можно провести аналогию с диском или флешкой. 379 | 380 | Внутри бакета можно создавать файлы и папки. 381 | 382 | Для хранения файлов всех пользователей в проекте создадим для них бакет под названием `user-files`. В корне бакета для каждого пользователя будет создана папка с именем в формате `user-${id}-files`, где `id` является идентификатором пользователя из SQL базы. 383 | 384 | Каждая из таких папок является корнем для хранения папок данного пользователя. Пример - файл `docs/test.txt` пользователя с id `1` должен быть сохранён в путь `user-1-files/docs/test.txt`. 385 | 386 | ### Работа с S3 из Java 387 | 388 | Как было упомянуто выше, для работы с S3 воспользуемся Minio Java SDK. Необходимо будет научиться пользоваться этой библиотекой, чтобы: 389 | - Создавать файлы 390 | - Переименовывать файлы 391 | - "Переименовывать" папки. Насколько знаю в S3 нет такой операции, переименование папки по сути представляет собой создание папки под новым именем и перенос туда файлов 392 | - Удалять файлы 393 | 394 | ## Фронтенд 395 | 396 | Для проекта написан одностраничный фронтенд на React, спасибо Андрею [@MrShoffen](https://t.me/MrShoffen) - [https://github.com/zhukovsd/cloud-storage-frontend/](https://github.com/zhukovsd/cloud-storage-frontend/). 397 | 398 | Демо фронтенда с мокнутным API (фронтенд отображает "фейковые" данные) - [https://zhukovsd.github.io/cloud-storage-frontend/files/](https://zhukovsd.github.io/cloud-storage-frontend/files/). 399 | 400 | Основные страницы: 401 | 402 | - Содержимое [корневой папки](https://zhukovsd.github.io/cloud-storage-frontend/files/) 403 | - Содержимое [вложенной папки](https://zhukovsd.github.io/cloud-storage-frontend/files/mocked_folder1/) 404 | - Формы регистрации и авторизации доступны через пункт "Выход" в меню заголовка [главной страницы](https://zhukovsd.github.io/cloud-storage-frontend/files/) 405 | 406 | ### Интеграция фронтенда 407 | 408 | Собранное React приложение представляет собой набор статических файлов - [https://github.com/zhukovsd/cloud-storage-frontend/tree/master/dist](https://github.com/zhukovsd/cloud-storage-frontend/tree/master/dist). 409 | 410 | Самый простой способ интеграции - добавить эти файлы в Spring Boot проекта. 411 | 412 | Необходимо, чтобы точка входа в React приложение (`index.html`) была доступна по корневому адресу в запущенном Spring Boot сервисе. Остальные файлы должны быть доступны по соответствующим относительным путям. 413 | 414 | Эндпоинты API при этом будут существовать под общим путём `/api`. Пример - `/api/auth/sign-up`. 415 | 416 | #### Альтернативный способ интеграции - Docker 417 | 418 | В случае, если есть желание отделить фронтенд от бека, можно раздавать статику React приложения через запущенный в Docker контейнере Nginx: 419 | 420 | 1. В файле [public/config.js](https://github.com/zhukovsd/cloud-storage-frontend/blob/master/public/config.js) необходимо прописать адрес API бэкенда. Пример - `http://localhost:8080` 421 | 2. Пересобрать Docker образ из Dockerfile 422 | 3. Запустить контейнер вручную или через Docker Compose 423 | 424 | ## Тесты 425 | 426 | ### Интеграционные тесты сервиса по работе с пользователями 427 | 428 | Как и в прошлом проекте, покроем тестами связку слоя данных с классами-сервисами, отвечающими за пользователей. 429 | 430 | Вместо с H2 предлагаю воспользоваться Testcontainers для запуска тестов в контексте полноценной (а не in-memory) базы данных. Это позволяет приблизить окружение тестов к рабочему окружению, и тестировать нюансы, специфичные для конкретных движков БД. 431 | 432 | Примеры тест кейсов: 433 | - Вызов метода "создать пользователя" в сервисе, отвечающем за работу с пользователями, приводит к появлению новой записи в таблице `users` 434 | - Создание пользователя с неуникальным username приводит к ожидаемому типу исключения 435 | 436 | ### Интеграционные тесты сервиса по работе с файлами и папками 437 | 438 | Опциональное задание повышенной сложности - покрыть тестами взаимодействие с сервисом хранения данных, работающим Minio. 439 | 440 | Примеры тест кейсов: 441 | - Загрузка файла приводит к его появлению в bucket'е Minio в корневой папке текущего пользователя 442 | - Переименование, удаление файлов и папок приводит к ожидаемому результату 443 | - Проверка прав доступа - пользователь не должен иметь доступа к чужим файлам 444 | - Поиск - пользователь может находить свои файлы, но не чужие 445 | 446 | Что потребуется: 447 | - Интеграция JUnit и Spring Security 448 | - Реализация [GenericContainer](https://www.testcontainers.org/features/creating_container/) для интеграции Minio и Testcontainers 449 | 450 | ## Docker 451 | 452 | В данном проекте впервые воспользуемся Docker для удобного запуска необходимых приложений - SQL базы, файлового хранилища MinIO и хранилища сессий Redis. 453 | 454 | Необходимо: 455 | - Найти образы для каждого нужного приложения из списка выше 456 | - Написать Docker Compose файл для запуска стека с приложениями (по контейнеру для каждого) 457 | - Знать Docker Compose команды для работы со стеком 458 | 459 | Как будет выглядеть работа с Docker: 460 | - Для работы над проектом запускаем стек из контейнеров 461 | - Уничтожаем или останавливаем контейнеры (с сохранением данных на volumes), когда работа не ведётся 462 | - По необходимости уничтожаем данные на volumes, если хотим очистить то или иное хранилище, запустить 463 | 464 | ## Деплой 465 | 466 | Будем вручную деплоить jar артефакт. Для его запуска не требуется Tomcat, потому что в собранное Spring Boot приложение уже встроен веб-сервер. Все остальные приложения этого проекта (SQL, Redis, MinIO) запускаем через Docker Compose. 467 | 468 | Шаги: 469 | - Локально собрать jar артефакт приложения 470 | - В хостинг-провайдере по выбору арендовать облачный сервер на Linux 471 | - Установить JRE, Docker 472 | - Скопировать на удалённый сервер Docker Compose файл для запуска Postgres, Redis, MinIO 473 | - Скопировать на удалённый сервер локально собранный jar, запустить 474 | 475 | Ожидаемый результат - приложение доступно по адресу `http://$server_ip:8080/`. 476 | 477 | ## План работы над приложением 478 | 479 | - Docker Compose - добавить Postgres 480 | - Spring Boot - с помощью Spring Security и Spring Data JPA реализовать методы API для регистрации и авторизации пользователей 481 | - Интеграционные тесты для сервиса регистрации 482 | - Docker Compose - добавить MinIO 483 | - Spring Boot - интегрировать Minio Java SDK и научиться совершать операции с файлами в бакете, написать сервис, инкапсулирующий необходимые для приложения операции 484 | - Реализовать загрузку файлов и папок 485 | - Реализовать методы API для получения файлов и папок, действия с файлами (удаление, переименование) 486 | - Поиск файлов - сервис, контроллер и методы API 487 | - Интегрировать фронтенд 488 | - (Опционально) интеграционные тесты для сервиса, отвечающего за работу с файлами и папками 489 | - Docker Compose - добавить Redis 490 | - Spring Sessions - сконфигурировать хранение сессий внутри Redis 491 | - Деплой 492 | 493 | ## Ресурсы для работы над ошибками 494 | 495 | - [Реализации проекта](../finished-projects/cloud-file-storage.md) другими студентами и мои ревью этих реализаций 496 | - Чеклист для самопроверки с типовыми ошибками (в конце страницы) 497 | - Присылайте законченные проекты в [чат](https://t.me/zhukovsd_it_chat), добавляю их в [список](../finished-projects/cloud-file-storage.md), сообщество делает ревью проектов 498 | 499 | ## Чеклист для самопроверки 500 | 501 | ❗️**Спойлеры**: советую не читать этот список до того момента, пока не допишете первую самостоятельную работающую версию проекта❗️ 502 | 503 | Функциональные проблемы: 504 | 505 | - Несоответствие формата API требуемому, особенно в вопросах обработки ошибок 506 | - Ошибки валидации имён файлов и папок 507 | - Затирание файлов и папок при переименовании или перемещении (пример - папка с файлами `1` и `2`, при переименовании `2` в `1`, оригинальный файл `1` будет утерян) 508 | - Битые файлы и архивы папок при скачивании 509 | - Возможность попасть в несуществующую папку, вместо того чтобы увидеть ошибку 404 510 | - Возможность найти файл другого пользователя через поиск, или скачать его (через ручное формирование ссылки) 511 | - Низкие лимиты на максимальный размер загружаемого файла или папки 512 | 513 | "Протекание" деталей реализации хранения файлов в Minio в пользовательский интерфейс, REST ответы, код контроллеров: 514 | 515 | - В пути к файлу содержится его корневая папка `user-${id}-files` 516 | - Не скрытая в сервисах, отвечающих за работу с Minio работа с пустыми папками (через, например, создание 0-byte файла с особым именем) 517 | 518 | 519 | -------------------------------------------------------------------------------- /content/projects/currency-exchange.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Обмен валют' 3 | weight = 3 4 | +++ 5 | 6 | ## Проект "Обмен валют" 7 | 8 | REST API для описания валют и обменных курсов. Позволяет просматривать и редактировать списки валют и обменных курсов, и совершать расчёт конвертации произвольных сумм из одной валюты в другую. 9 | 10 | Веб-интерфейс для проекта не подразумевается. 11 | 12 | Комментарии по проекту - [https://www.youtube.com/watch?v=013b_b7PszM](https://www.youtube.com/watch?v=013b_b7PszM). 13 | 14 | ## Что нужно знать 15 | 16 | - [Java](../technologies/java.md) - коллекции, ООП 17 | - [Паттерн MVC(S)](../technologies/java.md#mvc) 18 | - [Maven/Gradle](../technologies/build-systems.md) 19 | - [Backend](../technologies/backend.md) 20 | - Java сервлеты 21 | - HTTP - GET и POST запросы, коды ответа 22 | - REST API, JSON 23 | - [Базы данных](../technologies/databases.md) - SQLite, JDBC 24 | - [Деплой](../technologies/dev-ops.md#деплой) - облачный хостинг, командная строка Linux, Tomcat 25 | 26 | Фреймворки не используем. 27 | 28 | ## Мотивация проекта 29 | 30 | - Знакомство с MVC 31 | - REST API - правильное именование ресурсов, использование HTTP кодов ответа 32 | - SQL - базовый синтаксис, создание таблиц 33 | 34 | ## База данных 35 | 36 | В качестве базы данных предлагаю использовать SQLite. Это позволит встроить в ресурсы проекта файл с заполненными таблицами БД, что упростит деплой (детали ниже). 37 | 38 | ### Таблица `Currencies` 39 | 40 | | Колонка | Тип | Комментарий | 41 | |----------|---------|--------------------------------------------| 42 | | ID | int | Айди валюты, автоинкремент, первичный ключ | 43 | | Code | Varchar | Код валюты | 44 | | FullName | Varchar | Полное имя валюты | 45 | | Sign | Varchar | Символ валюты | 46 | 47 | Пример записи в таблице для австралийского доллара: 48 | 49 | | ID | Code | FullName | Sign | 50 | |-----|------|-------------------|------| 51 | | 1 | AUD | Australian dollar | A$ | 52 | 53 | Коды валют мира - [https://www.iban.com/currency-codes](https://www.iban.com/currency-codes). 54 | 55 | Индексы: 56 | - Первичный ключ по полю ID 57 | - Unique индекс по полю Code для гарантий уникальности валюты в таблице, и для ускорения поиска валюты по её аббревиатуре 58 | 59 | ### Таблица `ExchangeRates` 60 | 61 | | Колонка | Тип | Комментарий | 62 | |------------------|------------|-------------------------------------------------------------| 63 | | ID | int | Айди курса обмена, автоинкремент, первичный ключ | 64 | | BaseCurrencyId | int | ID базовой валюты, `внешний ключ на Currencies.ID` | 65 | | TargetCurrencyId | int | ID целевой валюты, `внешний ключ на Currencies.ID` | 66 | | Rate | Decimal(6) | Курс обмена единицы базовой валюты к единице целевой валюты | 67 | 68 | `Decimal(6)` - десятичное число с 6 знаками после запятой. Полезно для валют, отличающихся на порядки. Например, одна Японская иена равна 0.0073 USD. 69 | 70 | Индексы: 71 | - Первичный ключ по полю ID 72 | - Unique индекс по паре полей BaseCurrencyId, TargetCurrencyId для гарантий уникальности валютной пары, и для ускорения поиска курса по паре валют 73 | 74 | ## REST API 75 | 76 | Методы REST API реализуют [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) интерфейс над базой данных - позволяют создавать (C - create), читать (R - read), редактировать (U - update). В целях упрощения, опустим удаление (D - delete). 77 | 78 | ### Валюты 79 | 80 | #### GET `/currencies` 81 | 82 | Получение списка валют. Пример ответа: 83 | ```json 84 | [ 85 | { 86 | "id": 0, 87 | "name": "United States dollar", 88 | "code": "USD", 89 | "sign": "$" 90 | }, 91 | { 92 | "id": 0, 93 | "name": "Euro", 94 | "code": "EUR", 95 | "sign": "€" 96 | } 97 | ] 98 | ``` 99 | 100 | HTTP коды ответов: 101 | - Успех - 200 102 | - Ошибка (например, база данных недоступна) - 500 103 | 104 | #### GET `/currency/EUR` 105 | 106 | Получение конкретной валюты. Пример ответа: 107 | ```json 108 | { 109 | "id": 0, 110 | "name": "Euro", 111 | "code": "EUR", 112 | "sign": "€" 113 | } 114 | ``` 115 | 116 | HTTP коды ответов: 117 | - Успех - 200 118 | - Код валюты отсутствует в адресе - 400 119 | - Валюта не найдена - 404 120 | - Ошибка (например, база данных недоступна) - 500 121 | 122 | #### POST `/currencies` 123 | 124 | Добавление новой валюты в базу. Данные передаются в теле запроса в виде полей формы (`x-www-form-urlencoded`). Поля формы - `name`, `code`, `sign`. Пример ответа - JSON представление вставленной в базу записи, включая её ID: 125 | ```json 126 | { 127 | "id": 0, 128 | "name": "Euro", 129 | "code": "EUR", 130 | "sign": "€" 131 | } 132 | ``` 133 | 134 | HTTP коды ответов: 135 | - Успех - 201 136 | - Отсутствует нужное поле формы - 400 137 | - Валюта с таким кодом уже существует - 409 138 | - Ошибка (например, база данных недоступна) - 500 139 | 140 | ### Обменные курсы 141 | 142 | #### GET `/exchangeRates` 143 | 144 | Получение списка всех обменных курсов. Пример ответа: 145 | ```json 146 | [ 147 | { 148 | "id": 0, 149 | "baseCurrency": { 150 | "id": 0, 151 | "name": "United States dollar", 152 | "code": "USD", 153 | "sign": "$" 154 | }, 155 | "targetCurrency": { 156 | "id": 1, 157 | "name": "Euro", 158 | "code": "EUR", 159 | "sign": "€" 160 | }, 161 | "rate": 0.99 162 | } 163 | ] 164 | ``` 165 | 166 | HTTP коды ответов: 167 | - Успех - 200 168 | - Ошибка (например, база данных недоступна) - 500 169 | 170 | #### GET `/exchangeRate/USDRUB` 171 | 172 | Получение конкретного обменного курса. Валютная пара задаётся идущими подряд кодами валют в адресе запроса. Пример ответа: 173 | ```json 174 | { 175 | "id": 0, 176 | "baseCurrency": { 177 | "id": 0, 178 | "name": "United States dollar", 179 | "code": "USD", 180 | "sign": "$" 181 | }, 182 | "targetCurrency": { 183 | "id": 2, 184 | "name": "Russian Ruble", 185 | "code": "RUB", 186 | "sign": "₽" 187 | }, 188 | "rate": 80 189 | } 190 | 191 | ``` 192 | 193 | HTTP коды ответов: 194 | - Успех - 200 195 | - Коды валют пары отсутствуют в адресе - 400 196 | - Обменный курс для пары не найден - 404 197 | - Ошибка (например, база данных недоступна) - 500 198 | 199 | #### POST `/exchangeRates` 200 | 201 | Добавление нового обменного курса в базу. Данные передаются в теле запроса в виде полей формы (`x-www-form-urlencoded`). Поля формы - `baseCurrencyCode`, `targetCurrencyCode`, `rate`. Пример полей формы: 202 | - `baseCurrencyCode` - USD 203 | - `targetCurrencyCode` - EUR 204 | - `rate` - 0.99 205 | 206 | Пример ответа - JSON представление вставленной в базу записи, включая её ID: 207 | ```json 208 | { 209 | "id": 0, 210 | "baseCurrency": { 211 | "id": 0, 212 | "name": "United States dollar", 213 | "code": "USD", 214 | "sign": "$" 215 | }, 216 | "targetCurrency": { 217 | "id": 1, 218 | "name": "Euro", 219 | "code": "EUR", 220 | "sign": "€" 221 | }, 222 | "rate": 0.99 223 | } 224 | ``` 225 | 226 | HTTP коды ответов: 227 | - Успех - 201 228 | - Отсутствует нужное поле формы - 400 229 | - Валютная пара с таким кодом уже существует - 409 230 | - Одна (или обе) валюта из валютной пары не существует в БД - 404 231 | - Ошибка (например, база данных недоступна) - 500 232 | 233 | #### PATCH `/exchangeRate/USDRUB` 234 | 235 | Обновление существующего в базе обменного курса. Валютная пара задаётся идущими подряд кодами валют в адресе запроса. Данные передаются в теле запроса в виде полей формы (`x-www-form-urlencoded`). Единственное поле формы - `rate`. 236 | 237 | Пример ответа - JSON представление обновлённой записи в базе данных, включая её ID: 238 | ```json 239 | { 240 | "id": 0, 241 | "baseCurrency": { 242 | "id": 0, 243 | "name": "United States dollar", 244 | "code": "USD", 245 | "sign": "$" 246 | }, 247 | "targetCurrency": { 248 | "id": 2, 249 | "name": "Russian Ruble", 250 | "code": "RUB", 251 | "sign": "₽" 252 | }, 253 | "rate": 80 254 | } 255 | 256 | ``` 257 | 258 | HTTP коды ответов: 259 | - Успех - 200 260 | - Отсутствует нужное поле формы - 400 261 | - Валютная пара отсутствует в базе данных - 404 262 | - Ошибка (например, база данных недоступна) - 500 263 | 264 | ### Обмен валюты 265 | 266 | #### GET `/exchange?from=BASE_CURRENCY_CODE&to=TARGET_CURRENCY_CODE&amount=$AMOUNT` 267 | 268 | Расчёт перевода определённого количества средств из одной валюты в другую. Пример запроса - GET `/exchange?from=USD&to=AUD&amount=10`. 269 | 270 | Пример ответа: 271 | ```json 272 | { 273 | "baseCurrency": { 274 | "id": 0, 275 | "name": "United States dollar", 276 | "code": "USD", 277 | "sign": "$" 278 | }, 279 | "targetCurrency": { 280 | "id": 1, 281 | "name": "Australian dollar", 282 | "code": "AUD", 283 | "sign": "A$" 284 | }, 285 | "rate": 1.45, 286 | "amount": 10.00, 287 | "convertedAmount": 14.50 288 | } 289 | ``` 290 | 291 | Получение курса для обмена может пройти по одному из трёх сценариев. Допустим, совершаем перевод из валюты **A** в валюту **B**: 292 | 1. В таблице `ExchangeRates` существует валютная пара **AB** - берём её курс 293 | 2. В таблице `ExchangeRates` существует валютная пара **BA** - берем её курс, и считаем обратный, чтобы получить **AB** 294 | 3. В таблице `ExchangeRates` существуют валютные пары **USD-A** и **USD-B** - вычисляем из этих курсов курс **AB** 295 | 296 | Остальные возможные сценарии, для упрощения, опустим. 297 | 298 | ### Ответ в случае ошибки 299 | 300 | Для всех запросов, в случае ошибки, ответ должен выглядеть так: 301 | ```json 302 | { 303 | "message": "Валюта не найдена" 304 | } 305 | ``` 306 | 307 | Значение `message` зависит от того, какая именно ошибка произошла. 308 | 309 | ## Деплой 310 | 311 | Будем вручную деплоить war артефакт в Tomcat, установленный на удалённом сервере. При использовании встроенной в проект SQLite базы данных, установка внешней SQL БД не требуется. 312 | 313 | Шаги: 314 | - Локально собрать war артефакт приложения 315 | - В хостинг-провайдере по выбору арендовать облачный сервер на Linux 316 | - Установить JRE и Tomcat 317 | - Зайти в админский интерфейс Tomcat, установить собранный war артефакт 318 | 319 | Ожидаемый результат - приложение доступно по адресу `http://$server_ip:8080/$app_root_path`. 320 | 321 | ## План работы над приложением 322 | 323 | - Создать заготовку Java бэкенд приложения с `javax.servlet`/`jakarta.servlet` 324 | - Создать таблицы в базе данных, и вручную их заполнить начальными данными (несколько валют, обменных курсов) 325 | - Реализовать методы REST API для работы с валютами и обменными курсами 326 | - Реализовать метод REST API с подсчётом обмена валюты 327 | - Деплой на удалённый сервер 328 | 329 | ## Ресурсы для работы над ошибками 330 | 331 | - [Реализации проекта](../finished-projects/currency-exchange.md) другими студентами и мои ревью этих реализаций 332 | - Чеклист для самопроверки с типовыми ошибками (в конце страницы) 333 | - Присылайте законченные проекты в [чат](https://t.me/zhukovsd_it_chat), добавляю их в [список](../finished-projects/currency-exchange.md), сообщество делает ревью проектов 334 | 335 | ## Тестовый фронтенд 336 | 337 | Для тестирования ваших реализаций REST API и визуализации результата я написал тестовый фронтенд - [https://github.com/zhukovsd/currency-exchange-frontend](https://github.com/zhukovsd/currency-exchange-frontend). 338 | 339 | Проект представляет из себя набор статических HTML/CSS/JS файлов и состоит из одной веб-страницы, отвечающей за работу со всеми эндпоинтами API. 340 | 341 | Существует 2 способа интеграции фронтенда с проектом: 342 | - Положить статические файлы фронтенда внутрь нашего проекта, чтобы index.html был доступен по корневому пути 343 | - Запустить проект currency-exchange-frontend с помощью Docker. [Cкрипт для запуска](https://github.com/zhukovsd/currency-exchange-frontend/blob/main/launch-local-nginx.sh). Вариант потребует CORS заголовков 344 | 345 | В обоих вариантах важно установить верный корень REST API в app.js - [https://github.com/zhukovsd/currency-exchange-frontend/blob/main/js/app.js#L2](https://github.com/zhukovsd/currency-exchange-frontend/blob/main/js/app.js#L2). 346 | 347 | ## Чеклист для самопроверки 348 | 349 | ❗️**Спойлеры**: советую не читать этот список до того момента, пока не допишете первую самостоятельную работающую версию проекта❗️ 350 | 351 | Функциональные проблемы: 352 | - Реализация не всех сценарий обмена (всего их 3 - по прямому курсу, обратному курсу, кросс-курсу) 353 | - Расхождение с ТЗ в формате REST ответов, особенно в случае ошибок (4xx, 5xx) 354 | - Некорректное или отсутствующие округление сконвертированных сумм до двух десятичных знаков 355 | 356 | Проблемы и ошибки в коде: 357 | - Нечёткие границы между слоями Controller/Service/DAO. 358 | - Контроллеры, отвечающие за слишком большое количество задач. Они должны отвечать за валидацию, сериализацию JSON ответов, обработку ошибок от слоёв DAO/Service 359 | - Неправильная или неполная обработка ошибок. Предпочтительный вариант - слои DAO/Service кидают исключения, контроллер их обрабатывает и формирует ответ с нужным кодом и телом 360 | - Уязвимость к race conditions, подробно на примере разбирал [здесь](https://t.me/zhukovsd_it_chat/16351) (ревью другого проекта, но суть та же) - проверка на существование валюты с таким же кодом через `SELECT` перед `INSERT`, вместо того чтобы положиться на unique индекс и получить исключение от БД в случае нарушения уникальности 361 | - БД: 362 | - Отсутствие unique индексов на поля или комбинации полей, которые должны быть уникальными. Например - код валюты в таблице `currencies` 363 | - Отсутствие внешних ключей между таблицами 364 | - Некорректный тип данных для хранения курсов и сумм (лучше всего подходит `Decimal`) 365 | - Уязвимость к SQL injections, следует использовать параметризированные prepared statements 366 | - Использование `double`/`float` для операций с суммами. Следует использовать `BigDecimal` 367 | - Неиспользование DTO классов для формирования ответов REST API 368 | - Дублирование кода вместо использования filter для установки заголовков ответов во всех сервлетах - content-type и character encoding 369 | 370 | Не обязательно, но полезно: 371 | - Реализовать connection pool вместо того чтобы открывать по новому соединению на каждую SQL операцию 372 | - Использовать MapStruct или ModelMapper чтобы совершать преобразования между классами-моделями и DTO 373 | 374 | Мелочи: 375 | - Неиспользование `.gitignore`, из-за чего в репозиторий попадают лишние файлы и папки (например, `target`, `out`) 376 | - Неаккуратное форматирование кода 377 | - Неиспользование пакетов для структурирования классов, все классы в корне проекта 378 | 379 | ## Расширенные материалы 380 | 381 | Для проекта созданы расширенные материалы - [https://boosty.to/zhukovsd/posts/08a542e8-5503-4331-a82b-7b6bcf04314b](https://boosty.to/zhukovsd/posts/08a542e8-5503-4331-a82b-7b6bcf04314b) 382 | 383 | #### Для кого созданы материалы и какую проблему решают 384 | 385 | - Доведение проекта до конца и рефакторинг до качественного уровня 386 | - Помощь в преодолении трудностей 387 | 388 | Подробнее про цель продукта, баланс между открытыми и закрытыми материалами - [https://telegra.ph/Moj-pervyj-produkt---rasshirennaya-versiya-roadmapa-04-21](https://telegra.ph/Moj-pervyj-produkt---rasshirennaya-versiya-roadmapa-04-21). 389 | 390 | #### Что внутри 391 | 392 | - Эталонная реализация по шагам (7 шагов, включая деплой) 393 | - 3 часа лекций по разработке проекта. Разобранные темы: 394 | - Maven - создание, сборка и запуск проекта, установка Tomcat, jar, war 395 | - Java EE, Jakarta EE - сервлеты, фильтры, валидация запросов 396 | - Работа с БД - создание SQLite БД, создание и заполнение таблиц, JDBC, HikariCP, классы-модели, Lombok 397 | - MVC - DAO, сервисы, контроллеры, модели, обработка ошибок, DTO 398 | - Бизнес логика обмена валют, ручные тесты 399 | - Тестовый фронтенд - интеграция двумя способами, CORS заголовки 400 | - Деплой - сборка war артефакта, создание и подготовка Linux VM, установка Java и Tomcat, разворачивание артефакта 401 | - Лекция с разбором типовых ошибок и тому, как их исправить 402 | - Чат для вопросов по материалам 403 | -------------------------------------------------------------------------------- /content/projects/hangman.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Виселица' 3 | weight = 1 4 | +++ 5 | 6 | # Проект "Виселица" 7 | 8 | Задача - реализовать игру "виселица" на Java. Интерфейс - консольный. Описание правил игры на [Википедии](https://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%81%D0%B5%D0%BB%D0%B8%D1%86%D0%B0_(%D0%B8%D0%B3%D1%80%D0%B0)). 9 | 10 | Комментарии по проекту - [https://www.youtube.com/watch?v=kqLklwFjr5g](https://www.youtube.com/watch?v=kqLklwFjr5g). 11 | 12 | ## Что нужно знать 13 | 14 | - [Java](../technologies/java.md#базовый-синтаксис) - базовый синтаксис 15 | 16 | Если испытываете трудности "не знаю с чего начать", или "не понимаю что именно сделать", советую лекцию про декомпозицию проектов в задачи - [https://youtube.com/live/3ox5DI_xAog](https://youtube.com/live/3ox5DI_xAog). 17 | 18 | ## Мотивация проекта 19 | 20 | Проект является разминочным, и его основная цель - проверить себя на то, что от теории и решения задач уже можно переходить к реализации цельных проектов. Если приложения подобного уровня вы уже реализовывали без возникновения трудностей, проект можно пропустить. 21 | 22 | ## Функционал приложения и меню консольного интерфейса 23 | 24 | 1. При старте, приложение предлагает начать новую игру или выйти из приложения 25 | 2. При начале новой игры, случайным образом загадывается слово, и игрок начинает процесс по его отгадыванию 26 | 3. После каждой введенной буквы выводим в консоль счётчик ошибок, текущее состояние виселицы (нарисованное ASCII символами) 27 | 4. По завершении игры выводим результат (победа или поражение) и возвращаемся к состоянию #1 - предложение начать новую игру или выйти из приложения 28 | 29 | ## Заметки по реализации 30 | 31 | Даже самая маленькая программа в Java содержит хотя бы 1 класс и точкой входа в приложение является метод этого класса. 32 | 33 | Если у вас уже есть опыт в ООП, предлагаю написать этот проект в ООП-стиле, разбив всю логику на части и реализовав их в нескольких классах. 34 | 35 | Если такого опыта нет, можно реализовать всё в процедурном стиле в виде статических методов класса `Main`. 36 | 37 | ## План работы над приложением 38 | 39 | - Найти в интернете словарь существительных в именительном падеже, отбросить из него слишком короткие слова. Этот словарь будет источником для выбора случайного загаданного слова для каждого раунда игры 40 | - Реализовать игровой цикл отгадывания букв и отображения текущего состояния виселицы 41 | - Реализовать цикл по перезапуску игры после победы/поражения 42 | 43 | ## Ресурсы для работы над ошибками 44 | 45 | - Эталонная [реализация проекта](https://github.com/zhukovsd/tic-tac-toe) похожего уровня сложности, написанного на [стриме](https://www.youtube.com/watch?v=PPikj1qHxrA) 46 | - [Реализации проекта](../finished-projects/hangman.md) другими студентами и мои ревью этих реализаций 47 | - Чеклист для самопроверки с типовыми ошибками (в конце страницы) 48 | - Присылайте законченные проекты в [чат](https://t.me/zhukovsd_it_chat), добавляю их в [список](../finished-projects/hangman.md), сообщество делает ревью проектов 49 | 50 | ## Чеклист для самопроверки 51 | 52 | ❗️**Спойлеры**: советую не читать этот список до того момента, пока не допишете первую самостоятельную работающую версию проекта❗️ 53 | 54 | Функциональные проблемы: 55 | - Отсутствие валидации вводимых символов (валидными могут считаться, например, только маленькие буквы кириллицы). Невалидный ввод не должен увеличивать счётчик ошибок пользователя в игровом раунде 56 | - Повторно вводимый символ, отсутствующий в секретном слове, не должен считаться за ошибку 57 | 58 | Проблемы и ошибки в коде: 59 | - Использование массивов. Лучше применять коллекции `List<>`, `Set<>` 60 | - Путь к файлу со словарем слов указан как абсолютный, следует указывать как относительный 61 | - Недостаточная разбивка кода на функции. Одна функция должна отвечать за одну задачу, например, открывать отгаданную букву в маске загаданного слова 62 | - Излишнее использование "глобальных" переменных (public static полей) 63 | - Неоднозначное именование полей, переменных, методов. Несовпадение имён смысловой нагрузке 64 | - Не следует использовать рекурсию для запусков второго и последующих игровых раундов. Следует применить цикл 65 | 66 | Мелочи: 67 | - Неиспользование `.gitignore`, из-за чего в репозиторий попадают лишние файлы и папки (например, `target`, `out`) 68 | - Неаккуратное форматирование кода 69 | 70 | ## Расширенные материалы 71 | 72 | Для проекта созданы расширенные материалы - [https://boosty.to/zhukovsd/posts/07961b26-59a9-449f-80c5-53c4c070e2b8](https://boosty.to/zhukovsd/posts/07961b26-59a9-449f-80c5-53c4c070e2b8). 73 | 74 | #### Для кого созданы материалы и какую проблему решают 75 | 76 | - Доведение проекта до конца и рефакторинг до качественного уровня 77 | - Помощь в преодолении трудностей 78 | 79 | Подробнее про цель продукта, баланс между открытыми и закрытыми материалами - [https://telegra.ph/Moj-pervyj-produkt---rasshirennaya-versiya-roadmapa-04-21](https://telegra.ph/Moj-pervyj-produkt---rasshirennaya-versiya-roadmapa-04-21). 80 | 81 | #### Что внутри 82 | 83 | - Эталонная реализация по шагам (5 шагов) 84 | - Лекции по разработке проекта, суммарно 2 часа. Разобранные темы: 85 | - Декомпозиция 86 | - Реализация MVP 87 | - Реализация всего требуемого по ТЗ функционала 88 | - Дополнительный функционал - уровни сложности 89 | - Рефакторинг в ООП стиле 90 | - Лекция с разбором типовых ошибок и тому, как их исправить 91 | - ТЗ альтернативного/дополнительного проекта похожего уровня сложности 92 | - Чат для вопросов по материалам 93 | -------------------------------------------------------------------------------- /content/projects/simulation.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Симуляция' 3 | weight = 2 4 | +++ 5 | 6 | # Проект "Симуляция" 7 | 8 | Суть проекта - пошаговая симуляция 2D мира, населённого травоядными и хищниками. Кроме существ, мир содержит ресурсы (траву), которыми питаются травоядные, и статичные объекты, с которыми нельзя взаимодействовать - они просто занимают место. 9 | 10 | 2D мир представляет из себя матрицу NxM, каждое существо или объект занимают клетку целиком, нахождение в клетке нескольких объектов/существ - недопустимо. 11 | 12 | Идея взята [отсюда](https://www.youtube.com/watch?v=SfEZSyvbj2w) и упрощена. 13 | 14 | Комментарии по проекту - [https://www.youtube.com/watch?v=3Vrwx4iryhw](https://www.youtube.com/watch?v=3Vrwx4iryhw). 15 | 16 | ## Что нужно знать 17 | 18 | - [Java](../technologies/java.md) - коллекции, ООП 19 | 20 | ## Мотивация проекта 21 | 22 | Основная цель - демонстрация принципов дизайна архитектуры приложения с помощью ООП. Описанная ниже структура классов не является всеобъемлющей, предполагается что студент будет ей следовать, взяв за основу. 23 | 24 | ## Дизайн классов 25 | 26 | ### `Entity` 27 | 28 | Корневой абстрактный класс для всех существ и объектов существующих в симуляции. 29 | 30 | ### `Grass`, `Rock`, `Tree` 31 | 32 | `Rock`, `Tree` - статичные объекты. `Grass` - ресурс для травоядных. 33 | 34 | ### `Creature` 35 | 36 | Абстрактный класс, наследуется от `Entity`. Существо, имеет скорость (сколько клеток может пройти за 1 ход), количество HP. Имеет метод `makeMove()` - сделать ход. 37 | 38 | ### `Herbivore` 39 | 40 | Травоядное, наследуется от `Creature`. Стремятся найти ресурс (траву), может потратить свой ход на движение в сторону травы, либо на её поглощение. 41 | 42 | ### `Predator` 43 | 44 | Хищник, наследуется от `Creature`. В дополнение к полям класса `Creature`, имеет силу атаки. На что может потратить ход хищник: 45 | - Переместиться (чтобы приблизиться к жертве - травоядному) 46 | - Атаковать травоядное. При этом количество HP травоядного уменьшается на силу атаки хищника. Если значение HP жертвы опускается до 0, травоядное исчезает 47 | 48 | ### `Map` 49 | 50 | Карта, содержит в себе коллекцию для хранения существ и их расположения. Советую не спешить использовать двумерный массив или список списков, а подумать какие ещё коллекции могут подойти. 51 | 52 | ### `Simulation` 53 | 54 | Главный класс приложения, включает в себя: 55 | - Карту 56 | - Счётчик ходов 57 | - Рендерер поля 58 | - Actions - список действий, исполняемых перед стартом симуляции или на каждом ходу (детали ниже) 59 | 60 | Методы: 61 | - `nextTurn()` - просимулировать и отрендерить один ход 62 | - `startSimulation()` - запустить бесконечный цикл симуляции и рендеринга 63 | - `pauseSimulation()` - приостановить бесконечный цикл симуляции и рендеринга 64 | 65 | ### `Actions` 66 | 67 | `Action` - действие, совершаемое над миром. Например - сходить всеми существами. Это действие итерировало бы существ и вызывало каждому `makeMove()`. Каждое действие описывается отдельным классом и совершает операции над картой. Симуляция содержит 2 массива действий: 68 | - `initActions` - действия, совершаемые перед стартом симуляции. Пример - расставить объекты и существ на карте 69 | - `turnActions` - действия, совершаемые каждый ход. Примеры - передвижение существ, добавить травы или травоядных, если их осталось слишком мало 70 | 71 | ### Поиск пути 72 | 73 | Советую писать алгоритм поиска пути полностью с нуля, используя в качестве источника описание алгоритма на википедии. Проще всего начать с алгоритма [поиска в ширину](https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%B2_%D1%88%D0%B8%D1%80%D0%B8%D0%BD%D1%83). Он относительно простой в реализации, но может работать медленно на больших полях, для которых лучше подойдет алгоритм [A\*](https://ru.wikipedia.org/wiki/A*). 74 | 75 | ### Рендерер 76 | 77 | Рендерер ответственен за визуализацию состояния поля, его отрисовку. По желанию студента интерфейс приложения может быть консольным, либо графическим. 78 | 79 | ## Конечная цель 80 | 81 | Реализовать симуляцию и подобрать различные значения так, чтобы взаимодействия внутри мира получились максимально интересными: 82 | - Размер поля 83 | - Диапазоны HP и скорости существ 84 | - Диапазон атаки хищников 85 | 86 | Опциональные идеи для усложнения проекта: 87 | - Механика размножения существ 88 | - Механика голода, когда от отсутствия пищи у них начинает уменьшаться HP 89 | 90 | ## Ресурсы для работы над ошибками 91 | 92 | - Эталонная [реализация проекта](https://github.com/zhukovsd/chess) похожего уровня сложности, написанного на серии [стримов](https://www.youtube.com/watch?v=Pzydm8GZzMs) 93 | - [Реализации проекта](../finished-projects/simulation.md) другими студентами и мои ревью этих реализаций 94 | - Чеклист для самопроверки с типовыми ошибками (в конце страницы) 95 | - Присылайте законченные проекты в [чат](https://t.me/zhukovsd_it_chat), добавляю их в [список](../finished-projects/simulation.md), сообщество делает ревью проектов 96 | 97 | ## Чеклист для самопроверки 98 | 99 | ❗️**Спойлеры**: советую не читать этот список до того момента, пока не допишете первую самостоятельную работающую версию проекта❗️ 100 | 101 | Проблемы и ошибки в коде: 102 | - Интеграция библиотеки графического интерфейса ценой чистоты и понятности кода. Достаточно консольного интерфейса 103 | - Дублирование кода между классами Herbivore и Predator 104 | - Класс Map 105 | - Неоптимальный выбор коллекции для хранения состояния ячеек 106 | - Не использование generics, параллельные коллекции для разных типов существ (для каждого типа своя коллекция) 107 | - Заполнение карты "пустотами" 108 | - Недостаточная инкапсуляция - "протекающее" наружу внутреннее устройство класса Map (например, прямой доступ к коллекции ячеек) вместо набора методов с говорящими названиями (`AddEntity`, `RemoveEntity`, и так далее) 109 | - [Пример чистой инкапсуляции](https://gist.github.com/zhukovsd/7813f34044f69dc160681db88e654b71#map) 110 | - Иерархия классов `Action`'s 111 | - Дублирование кода 112 | - [Пример чистой реализации](https://github.com/immagixe/Matrix2077/tree/master/src/main/java/main/java/Matrix2077/actions) 113 | - Неоднозначное именование полей, переменных, методов. Несовпадение имён смысловой нагрузке 114 | - Поиск пути 115 | - Реализация алгоритма поиска пути внутри классов существ. Следует вынести это в отдельный класс 116 | - Дублирование кода для поиска пути хищниками и травоядными 117 | 118 | Мелочи: 119 | - Неиспользование `.gitignore`, из-за чего в репозиторий попадают лишние файлы и папки (например, `target`, `out`) 120 | - Неаккуратное форматирование кода 121 | - Неиспользование пакетов для структурирования классов, все классы в корне проекта 122 | -------------------------------------------------------------------------------- /content/projects/task-tracker.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Планировщик задач' 3 | weight = 7 4 | +++ 5 | 6 | # Проект "Планировщик задач" 7 | 8 | Многопользовательский планировщик задач. Пользователи могут использовать его в качестве TODO листа. Источником вдохновения для проекта является Trello. 9 | 10 | ## Что нужно знать 11 | 12 | - [Java](../technologies/java.md) - коллекции, ООП 13 | - [Maven/Gradle](../technologies/build-systems.md) 14 | - [Backend](../technologies/backend.md) 15 | - Spring Boot, Spring Security, Spring Session, Spring Kafka, Spring Scheduler, Spring Mail 16 | - JWT 17 | - [Базы данных](../technologies/databases.md) 18 | - SQL 19 | - Spring Data JPA 20 | - Миграции 21 | - [Frontend](../technologies/frontend.md) - HTML/CSS, Bootstrap, Javascript, Ajax 22 | - [Docker и микросервисы](../technologies/microservices.md) 23 | - Контейнеры, образы, volumes, написание Dockerfile, Docker Compose 24 | - Брокер сообщений Kafka 25 | - [Деплой](../technologies/dev-ops.md#деплой) 26 | - Деплой 27 | - CI/CD, GitHub Actions 28 | 29 | ## Мотивация проекта 30 | 31 | - Знакомство с микросервисами на практике - разработка проекта, разделённого на несколько сервисов 32 | - Использование новых модулей Spring Boot - Spring Kafka, Scheduler, Mail 33 | - Практика с Docker и Docker Compose, докеризация Spring Boot приложение (упаковка его в Docker образ) 34 | - Знакомство и практика с брокером сообщений Kafka 35 | - Самостоятельная разработка части схемы REST API 36 | 37 | Дисклеймер. Планировщик задач - довольно простой и легковесный проект для микросервисного подхода. Но проект достаточной сложности вышел бы за рамки учебного по объёму и сложности. 38 | 39 | ## Функционал приложения 40 | 41 | Работа с пользователями: 42 | 43 | - Регистрация 44 | - Авторизация 45 | - Logout 46 | 47 | Работа с задачами: 48 | 49 | - Создание, редактирование (каждая задача имеет заголовок и описание) 50 | - Пометка задачи как сделанной 51 | - Удаление 52 | 53 | Оповещение пользователей по email: 54 | - Приветственное письмо 55 | - Каждый день в полночь оповещение о том, сколько задач было сделано за прошедшие сутки, и какое количество осталось невыполненными 56 | 57 | ## Интерфейс приложения 58 | 59 | Веб-приложение является одностраничным, всё общение с сервером происходит с помощью JavaScript/Ajax, интерфейс обновляется через jQuery. 60 | 61 | - Заголовок 62 | - Для неавторизованных пользователей - кнопки регистрации и авторизации 63 | - По нажатию на кнопки открывается модальный диалог с формой регистрации или авторизации 64 | - Поля диалога регистрации - email, password, repeat password 65 | - Поля диалога авторизации - email, password 66 | - В случае ошибки регистрации или авторизации, приложение должно отобразить ошибку, подобно Thymeleaf 67 | - Для авторизованных пользователей - логин текущего пользователя и кнопка Logout 68 | - Контент (только для авторизованных пользователей) 69 | - Поле ввода заголовка новой задачи с кнопкой "Добавить" 70 | - Список несделанных задач 71 | - Список сделанных задач 72 | 73 | При клике на задачу открывается модальное окно со следующими элементами: 74 | - Поле ввода заголовка задачи 75 | - Многострочное поле ввода описания задачи 76 | - Чекбокс, чтобы пометить задачу сделанной (и обратно - пометить несделанной) 77 | - Кнопка "удалить" 78 | 79 | 80 | У формы модального окна нет кнопки "сохранить". JavaScript должен сохранять состояние задачи с помощью Ajax запросов, каждый раз когда пользователь нажимает на чекбокс, или редактирует поля ввода. 81 | 82 | ## SQL база данных 83 | 84 | В этом проекте студент самостоятельно разрабатывает структуру базы данных для хранения пользователей и задач. Отношение между пользователем и задачами - один ко многим. 85 | 86 | Как и в предыдущем проекте, в таблице `Users` ориентироваться стоит на интеграцию с Spring Security. Эта библиотека экосистемы Spring подразумевает определённые атрибуты, которыми должен обладать пользователь, и список которых и станет основой колонок для таблицы. 87 | 88 | Таблица `Tasks` должна содержать поля для описания заметки - заголовок, текст, владелец, статус, и время (timestamp), когда задача была помечена как сделанная. 89 | 90 | ### Миграции 91 | 92 | Рекомендую воспользоваться инструментом миграций для создания таблиц. Необходимо будет принять решение, какой из сервисов является "главным" по работе со схемой БД, и содержит в себе миграции. 93 | 94 | ## JWT 95 | 96 | Вместо сессий воспользуемся JWT. Можно обойтись без refresh token'ов, выдавать только access token, по которым авторизовывать запросы к REST API. 97 | 98 | Стоит реализовать авторизацию JWT на уровне Spring Security, например, в виде фильтра, который будет доставать access token из запроса, декодировать и валидировать его. 99 | 100 | ## Сервисы 101 | 102 | ### Бэкенд 103 | 104 | Spring Boot приложение, реализующее REST API для работы с пользователями, авторизацией, и задачами. 105 | 106 | #### POST `/user` - регистрация пользователя 107 | 108 | - Тело запроса - поля формы в формате `application/x-www-form-urlencoded` или `application/json` 109 | - Зарегистрированный пользователь сразу же автоматически авторизуется, без отдельного заполнения логин формы 110 | - В случае успешной регистрации, код ответа HTTP 200, HTTP заголовок ответа содержит выданный пользователю JWT access token 111 | - В случае неуспешной регистрации, код и тело ответа описывают ошибку 112 | 113 | Пример ответа с ошибкой - "this email is already taken" - HTTP code 409: 114 | ```json 115 | { 116 | "message": "This email is already taken" 117 | } 118 | ``` 119 | 120 | Структура ответа с ошибкой - JSON объект с полем message. Код ответа должен быть наиболее подходящим к ситуации из 4хх 5хх кодов ошибок - [https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-p2-semantics-18#section-7.4](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-p2-semantics-18#section-7.4). 121 | 122 | Так же, в случае успешной регистрации, пользователю отправляется приветственное письмо (непосредственная отправка письма происходит в другом сервисе, детали ниже в разделе **Kafka**). 123 | 124 | #### GET `/user` - получение текущего пользователя 125 | 126 | - Успешный ответ с кодом HTTP 200 содержит модель пользователя в виде JSON объекта 127 | - В случае, если пользователь неавторизован, его токен истек - тело ответа содержит сообщение об ошибке, код ответа - HTTP 401 128 | 129 | Пример ответа с кодом HTTP 200: 130 | ```json 131 | { 132 | "id": 1, 133 | "email": "my@email.com" 134 | } 135 | ``` 136 | 137 | #### POST `/auth/login` - авторизация пользователя 138 | 139 | - Тело запроса - поля формы в формате `application/x-www-form-urlencoded` или `application/json` 140 | - В случае успешной авторизации, код ответа HTTP 200, HTTP заголовок ответа содержит выданный пользователю JWT access token 141 | - В случае неуспешной авторизации, код и тело ответа описывают ошибку 142 | 143 | #### GET `/tasks` - получение списка заметок текущего пользователя 144 | 145 | - Успешный ответ с кодом HTTP 200 содержит список моделей задач в виде JSON массива 146 | - В случае, если пользователь неавторизован, его токен истек - тело ответа содержит сообщение об ошибке, код ответа - HTTP 401 147 | 148 | --- 149 | 150 | Дизайн методов для создания, редактирования, удаления задач студенту предлагается разработать самостоятельно. 151 | 152 | ### Фронтенд 153 | 154 | Набор статических файлов: 155 | - HTML разметка структуры главной страницы (динамические данные вставляются через JavaScript/jQuery) 156 | - CSS 157 | - JavaScript 158 | 159 | При разработке, файлы можно раздавать локальным сервером встроенным в Intellij IDEA. В контексте Docker Compose стека, образ фронтенд сервиса будет содержать веб-сервер Nginx, раздающий статику. 160 | 161 | - При загрузке страницы, JavaScript делает запрос на получение текущего пользователя. Ответ 401 HTTP означает, что пользователь неавторизован, 200 - авторизован. Для авторизованного пользователя мы можем запросить и отобразить список задач 162 | Работа с JavaScript/Ajax: 163 | - Формы авторизации и регистрации должны быть сверстаны с помощью тега `form`, но вместо обработчика по-умолчанию (POST запрос и перезагрузка страницы), jQuery будет переопределять обработчик, исполнять POST запрос через Ajax и реагировать на результат, в соответствии с кодом ошибки 164 | - Веб-страница и REST API будут доступны на разных портах - 80 и 8080. Для вызова методов REST API через Ajax может потребоваться выдача разрешения браузеру через CORS - https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS 165 | 166 | ### Рассыльщик писем 167 | 168 | Spring Boot приложение с двумя модулями - Spring Mail и Spring AMQP. 169 | 170 | С помощью Spring AMQP приложение подключается к Kafka и создает топик `EMAIL_SENDING_TASKS`, после чего подписывается на сообщения, поступающие от планировщика и бэкенд-сервиса. 171 | 172 | Для каждого полученного сообщения, содержимое которого десериализуется в экземпляр модели, пользуемся модулем Spring Mail, чтобы отправить письмо. Подробнее о том, откуда брать SMTP конфигурацию, написано ниже в разделе **Mailjet**. 173 | 174 | ### Планировщик 175 | 176 | Spring Boot приложение с двумя модулями - Spring Scheduler и Spring AMQP. 177 | 178 | Задача сервиса - раз в сутки итерировать всех пользователей, и формировать для них отчёты о задачах и изменениях в них за сутки. 179 | 180 | - Пользователям, у которых на конец дня есть невыполненные задача, отправляется емейл вида "У вас осталось N несделанных задач". Тело письма содержит заголовки этих задач (не более какого-то разумного количества, например 5) 181 | - Пользователям, у которых за последние сутки 1 или более задач были отмечены как сделанные, отправляется емейл вида "За сегодня вы выполнили N задач". Тело письма содержит заголовки выполненных задач 182 | - Пользователям, для которых верны оба условия выше, отправляется емейл и со списком несделанных, и сделанных за последние сутки задач 183 | 184 | Spring Scheduler по Cron выражению каждую полночь вызывает метод, читающий список пользователей, анализирующий их заметки, и формирующий емейлы для отправки. Сформированные емейлы упаковываются в классы-модели и отправляются в Kafka топик. 185 | 186 | ## Работа с Kafka 187 | 188 | Kafka в контексте Docker Compose стека запускается образов, подобно базе данных. 189 | 190 | - Задачи для отправки емейлов будут попадать в топик `EMAIL_SENDING_TASKS` 191 | - Бэкенд сервис и планировщик записывают сообщения в топик, рассыльщик писем - читает 192 | - Формат сообщений - JSON, сереализуемый и десериализуемый по модели, описанной с помощью класса Java, подобно моделям для контроллеров REST API. Сообщения в топике `EMAIL_SENDING_TASKS` должны содержать следующие поля - адрес получателя, заголовок и текст письма 193 | - Для интеграции Spring c Kafka можно использовать модуль Spring Kafka 194 | 195 | ## Mailjet 196 | 197 | Для отправки SMTP нужны логин, пароль, хост и порт SMTP сервера. У емейл сервисов, например Gmail, mail.ru, есть SMTP сервера, с которыми работают емейл-клиенты. Теоретически, можно воспользоваться их сервером, и отправлять письма, авторизовываясь логином и паролем своего ящика, но в реальности, для писем и рассылок обычно пользуются специальными для этого созданными сервисами, например Mailjet, Mailchimp, Mailgun (и десятками аналогичных). 198 | 199 | Для примера рассмотрим [Mailjet](https://www.mailjet.com/). Бесплатный лимит сервиса - 200 писем на отправку в сутки. Необходимо зарегистрироваться там, сконфигурировать sender адрес (адрес отправителя писем), получить credentials (логин и пароль) и остальные SMTP настройки. 200 | 201 | Логин и пароль - секретные данные, которые не должны попасть в git репозиторий. При запуске приложения из IDE или Docker Compose стека, секреты можно подключить к приложению через переменные окружения. 202 | 203 | ## Docker 204 | 205 | В данном проекте воспользуемся Docker для запуска необходимых приложений, и упаковки наших четырех сервисов в Docker образы. 206 | 207 | Необходимо: 208 | - Найти образы для каждого нужного приложения 209 | - Написать Docker Compose файл для запуска стека с приложениями (по сервису для каждого) 210 | - Упаковать сервисы в Docker образы, написав для них Dockerfile 211 | 212 | Сервисы: 213 | - task-tracker-backend (порт 8080) 214 | - task-tracker-frontend (порт 80) 215 | - task-tracker-scheduler 216 | - task-tracker-email-sender 217 | - Postgres 218 | - Kafka 219 | 220 | ## Структура проекта 221 | 222 | Для структурирования мульти-сервисного проекта существует 2 альтернативных подхода: 223 | - Отдельный репозиторий для каждого сервиса, плюс отдельный репозиторий для Docker Compose. Главный минус - необходимость создавать и управлять несколькими репозиториями 224 | - Единый репозиторий для всех сервисов и Docker Compose. Главный минус - необходимость дополнительной конфигурации Intellij IDEA для работы с несколькими Gradle/Spring Boot приложениями в одной папке 225 | 226 | Выбор того или иного варианта зависит от ситуации. В рамках данного проекта, вариант #2 кажется более предпочтительным. 227 | 228 | ## Деплой 229 | 230 | Приложение целиком будем деплоить через Docker. 231 | 232 | Шаги: 233 | - С помощью GitHub Actions (детали ниже) автоматизировать сборку докер образов для сервисов task-tracker-backend, task-tracker-frontend, task-tracker-scheduler, task-tracker-email-sender 234 | - В хостинг-провайдере по выбору арендовать облачный сервер на Linux 235 | - Установить Docker 236 | - Скопировать на удалённый сервер Docker Compose файл для запуска сервисов приложения, запустить 237 | - Опционально - купить доменное имя и привязать к нему IP сервера 238 | 239 | Ожидаемый результат - приложение доступно по адресу `http://$server_ip`, или `http://$domain_name` (если куплено доменное имя). 240 | 241 | ## CI/CD 242 | 243 | С помощью GitHub Actions автоматизируем сборку докер образов для четырёх сервисов приложения (task-tracker-*): 244 | - У каждого сервиса должен быть отдельный репозиторий 245 | - При push в основную ветку запускается GitHub Actions пайплайн: 246 | - Шаг 1 - сборка образов сервисов. Советую разобраться с multi-stage Dockerfile, первая стадия - сборка jar артефакта с помощью JDK, вторая стадия - копируем jar в легковесный образ с jre 247 | - Шаг 2 - push образов в публичный репозиторий, откуда их можно будет скачать для деплоя. В качестве публичного репозитория проще всего воспользоваться [https://hub.docker.com/](https://hub.docker.com/). Необходимо будет разобраться с безопасным хранением логина/пароля от Docker Hub на GitHub для push'а образов 248 | 249 | ## План работы над приложением 250 | 251 | - Docker Compose - добавить Postgres, Kafka 252 | - Backend - работа с пользователями 253 | - Backend - работа с JWT, авторизация 254 | - Frontend - структура страницы, интерфейс и Javascript для регистрации и авторизации 255 | - Backend - работа с задачами 256 | - Frontend - листинг, добавление, редактирование, удаление задач 257 | - Email sender - реализация получения сообщений от Kafka и отправки емейлов 258 | - Backend - отправка приветственного письма при регистрации 259 | - Scheduler - отправка пользовательских отчетов по сделанным и оставшимся задачам 260 | - CI/CD, деплой 261 | 262 | ## Ресурсы для работы над ошибками 263 | 264 | - [Реализации проекта](../finished-projects/task-tracker.md.md) другими студентами и мои ревью этих реализаций 265 | - Присылайте законченные проекты в [чат](https://t.me/zhukovsd_it_chat), добавляю их в [список](../finished-projects/task-tracker.md), сообщество делает ревью проектов 266 | -------------------------------------------------------------------------------- /content/projects/tennis-scoreboard.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Табло теннисного матча' 3 | weight = 4 4 | +++ 5 | 6 | # Проект "Табло теннисного матча" 7 | 8 | Веб-приложение, реализующее табло счёта теннисного матча. 9 | 10 | Комментарии по проекту - [https://www.youtube.com/watch?v=zAOiNa24jpg](https://www.youtube.com/watch?v=zAOiNa24jpg). 11 | 12 | ## Что нужно знать 13 | 14 | - [Java](../technologies/java.md) - коллекции, ООП 15 | - [Паттерн MVC(S)](../technologies/java.md#mvc) 16 | - [Maven/Gradle](../technologies/build-systems.md) 17 | - [Backend](../technologies/backend.md) 18 | - Java Servlets, JSP 19 | - HTTP - GET и POST запросы, формы 20 | - [Базы данных](../technologies/databases.md) - SQL, Hibernate, H2 (in-memory SQL database) 21 | - [Опционально] [Frontend](../technologies/frontend.md) - HTML/CSS, блочная вёрстка 22 | - [Тесты](../technologies/tests.md) - юнит тестирование, JUnit 5 23 | - [Деплой](../technologies/dev-ops.md#деплой) - облачный хостинг, командная строка Linux, Tomcat 24 | 25 | ## Мотивация проекта 26 | 27 | - Создать клиент-серверное приложение с веб-интерфейсом 28 | - Получить практический опыт работы с ORM Hibernate 29 | - Сверстать простой веб-интерфейс без сторонних библиотек 30 | - Познакомиться с архитектурным паттерном MVC(S) 31 | 32 | Комментарии: 33 | - Проект не подразумевает фреймворки, ради практики с паттерном MVC, Spring Boot начнется с проекта #6 34 | - Не используем Bootstrap, для практики верстки вручную, Bootstrap можно будет использовать в проекте #5 35 | - Проект не многопользовательский, поэтому не используем сессии 36 | 37 | ## Функционал приложения 38 | 39 | Работа с матчами: 40 | 41 | - Создание нового матча 42 | - Просмотр законченных матчей, поиск матчей по именам игроков 43 | - Подсчёт очков в текущем матче 44 | 45 | ## Подсчёт очков в теннисном матче 46 | 47 | В теннисе особая система подсчёта очков - https://www.gotennis.ru/read/world_of_tennis/pravila.html 48 | 49 | Для упрощения, допустим что каждый матч играется по следующим правилам: 50 | - Матч играется до двух сетов (best of 3) 51 | - При счёте 6/6 в сете, играется тай-брейк до 7 очков 52 | 53 | ## Интерфейс приложения 54 | 55 | ### Главная страница 56 | 57 | - Ссылки, ведущие на страницы нового матча и списка завершенных матчей 58 | 59 | ### Страница нового матча 60 | 61 | Адрес - `/new-match`. 62 | 63 | Интерфейс: 64 | - HTML форма с полями “Имя игрока 1”, “Имя игрока 2” и кнопкой “начать”. Для упрощения допустим, что имена игроков уникальны. Игрок не может играть сам с собой. 65 | - Нажатие кнопки “начать” приводить к POST запросу по адресу `/new-match` 66 | 67 | Обработчик POST запроса: 68 | - Проверяет существование игроков в таблице `Players`. Если игрока с таким именем не существует, создаём 69 | - Создаём экземпляр класса, содержащего айди игроков и текущий счёт, и кладём в коллекцию текущих матчей (существующую только в памяти приложения, либо в key-value storage). Ключом коллекции является UUID, значением - счёт в матче 70 | - Редирект на страницу `/match-score?uuid=$match_id` 71 | 72 | ### Страница счёта матча - `/match-score` 73 | 74 | Адрес - `/match-score?uuid=$match_id`. GET параметр `uuid` содержит UUID матча. 75 | 76 | Интерфейс: 77 | - Таблица с именами игроков, текущим счётом 78 | - Формы и кнопки для действий - "игрок 1 выиграл текущее очко", "игрок 2 выиграл текущее очко" 79 | - Нажатие кнопок приводит к POST запросу по адресу `/match-score?uuid=$match_id`, в полях отправленной формы содержится айди выигравшего очко игрока 80 | 81 | Обработчик POST запроса: 82 | - Извлекает из коллекции экземпляр класса Match 83 | - В соответствии с тем, какой игрок выиграл очко, обновляет счёт матча 84 | - Если матч не закончился - рендерится таблица счёта матча с кнопками, описанными выше 85 | - Если матч закончился: 86 | - Удаляем матч из коллекции текущих матчей 87 | - Записываем законченный матч в SQL базу данных 88 | - Рендерим финальный счёт 89 | 90 | ### Страница сыгранных матчей - `/matches` 91 | 92 | Адрес - `/matches?page=$page_number&filter_by_player_name=$player_name`. GET параметры: 93 | - `page` - номер страницы. Если параметр не задан, подразумевается первая страница 94 | - `filter_by_player_name` - имя игрока, матчи которого ищем. Если параметр не задан, отображаются все матчи 95 | 96 | Постранично отображает список сыгранных матчей. Позволяет искать матчи игрока по его имени. Для постраничного отображения потребуется реализация пагинации. 97 | 98 | Интерфейс: 99 | - Форма с фильтром по имени игрока. Поле ввода для имени и кнопка "искать". По нажатию формируется GET запрос вида `/matches?filter_by_player_name=${NAME}` 100 | - Список найденных матчей 101 | - Переключатель страниц, если матчей найдено больше, чем влезает на одну страницу 102 | 103 | ## Фронтенд 104 | 105 | Я считаю, что уметь делать простой фронтенд с нуля - универсально полезный навык для всех разработчиков. Однако, в условиях дефицита времени это может быть нерационально. 106 | 107 | Поэтому, для проекта существует готовая верстка, которую можно взять за основу. Если хотите расширить функционал проекта или сделать верстку с нуля - приветствую желание самостоятельно это сделать. 108 | 109 | Репозиторий с версткой - [https://github.com/zhukovsd/tennis-scoreboard-html-layouts](https://github.com/zhukovsd/tennis-scoreboard-html-layouts). 110 | 111 | Что внутри - макеты четырёх страниц, адаптивная верстка (десктопы, телефоны). Минимальный Javascript для показа меню навигации на телефонах. 112 | 113 | Задеплоенные на GitHub Pages страницы для демонстрации: 114 | - [Главная страница](https://zhukovsd.github.io/tennis-scoreboard-html-layouts/) 115 | - [Новый матч](https://zhukovsd.github.io/tennis-scoreboard-html-layouts/new-match.html) 116 | - [Счёт текущего матч](https://zhukovsd.github.io/tennis-scoreboard-html-layouts/match-score.html) 117 | - [Завершенные матчи](https://zhukovsd.github.io/tennis-scoreboard-html-layouts/matches.html) 118 | 119 | Как пользоваться: 120 | - Перенести в проект нужные для веб-страниц ресурсы - CSS, картинки 121 | - На основе HTML верстки создать шаблонизированные страницы с добавлением тегов JSP 122 | 123 | ## База данных 124 | 125 | В качестве базы данных предлагаю использовать H2. Это in-memory SQL база для Java. In-memory означает то, что движок БД и сами таблицы существуют только внутри памяти Java приложения. При использовании in-memory хранилища необходимо инициализировать таблицы базы данных при каждом старте приложения. 126 | 127 | #### Таблица `Players` - игроки 128 | 129 | | Имя колонки | Тип | Комментарий | 130 | |-------------|---------|-------------------------------| 131 | | ID | Int | Первичный ключ, автоинкремент | 132 | | Name | Varchar | Имя игрока | 133 | 134 | Индексы: 135 | - Уникальный индекс колонки `Name` для эффективности поиска игроков по имени и запрета повторяющихся имён 136 | 137 | ### Таблица `Matches` - завершенные матчи 138 | 139 | Для упрощения, в БД сохраняются только доигранные матчи в момент их завершения. 140 | 141 | | Имя колонки | Тип | Комментарий | 142 | |-------------|-----|-------------------------------------------------| 143 | | ID | Int | Первичный ключ, автоинкремент | 144 | | Player1 | Int | Айди первого игрока, внешний ключ на Players.ID | 145 | | Player2 | Int | Айди второго игрока, внешний ключ на Players.ID | 146 | | Winner | Int | Айди победителя, внешний ключ на Players.ID | 147 | 148 | ## MVCS 149 | 150 | MVCS - архитектурный паттерн, особенно хорошо подходящий под реализацию подобных приложений. Я подразумеваю, что студент самостоятельно изучил что такое MVCS, и ниже приведу только пример того, как он может быть использован в данном проекте. 151 | 152 | ### Учёт счёта матча 153 | 154 | Пример (именование классов и сервисов на мой вкус): 155 | 156 | `MatchScoreController`: 157 | - Обрабатывает POST запросы к `/match-score` 158 | - Через `OngoingMatchesService` получает экземпляр класса `Match` для текущего матча, который является моделью/частью модели `MatchScoreModel` 159 | - Через `MatchScoreCalculationService` обновляет счёт в матче 160 | - Если матч закончился - через `FinishedMatchesPersistenceService` сохраняет законченный матч в базу данных 161 | - С помощью JSP шаблона отображает `MatchScoreModel` в виде отрендеренного HTML 162 | 163 | Каждый из упомянутых сервисов делает конкретную работу: 164 | - `OngoingMatchesService` хранит текущие матчи и позволяет их записывать/читать 165 | - `MatchScoreCalculationService` реализует логику подсчёта счёта матча по очкам/геймам/сетам 166 | - `FinishedMatchesPersistenceService` инкапсулирует чтение и запись законченных матчей в БД 167 | 168 | ## Тесты 169 | 170 | Покроем юнит тестами подсчёт очков в матче. Примеры кейсов: 171 | - Если игрок 1 выигрывает очко при счёте 40-40, гейм не заканчивается 172 | - Если игрок 1 выигрывает очко при счёте 40-0, то он выигрывает и гейм 173 | - При счёте 6-6 начинается тайбрейк вместо обычного гейма 174 | 175 | Предлагаю студентам самостоятельно придумать тест кейсы для покрытия всех вариантов изменения счёта в матче, особенно правила "больше-меньше" и тайбрейк. Набор тестов должен быть реализован с помощью JUnit 5. 176 | 177 | ## Деплой 178 | 179 | Будем вручную деплоить war артефакт в Tomcat, установленный на удалённом сервере. При использовании базы данных H2, установка внешней SQL БД не требуется. 180 | 181 | Шаги: 182 | - Локально собрать war артефакт приложения 183 | - В хостинг-провайдере по выбору арендовать облачный сервер на Linux 184 | - Установить JRE и Tomcat 185 | - Зайти в админский интерфейс Tomcat, установить собранный war артефакт 186 | 187 | Ожидаемый результат - приложение доступно по адресу `http://$server_ip:8080/$app_root_path`. 188 | 189 | ## План работы над приложением 190 | 191 | - Классы-модели Hibernate для таблиц БД 192 | - Страница создания нового матча 193 | - Сервисы для хранения текущих матчей и подсчета очков в матче, юнит тесты для подсчёта очков 194 | - Страница счёта матча 195 | - Сервис для сохранения законченного матча в БД 196 | - Сервис поиска законченных матчей по имени игрока 197 | - Страница отображения законченных матчей, поиска матчей по имени игрока 198 | - Деплой на удалённый сервер 199 | 200 | ## Ресурсы для работы над ошибками 201 | 202 | - [Реализации проекта](../finished-projects/tennis-scoreboard.md) другими студентами и мои ревью этих реализаций 203 | - Присылайте законченные проекты в [чат](https://t.me/zhukovsd_it_chat), добавляю их в [список](../finished-projects/tennis-scoreboard.md), сообщество делает ревью проектов 204 | -------------------------------------------------------------------------------- /content/projects/weather-viewer.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Погода' 3 | weight = 5 4 | +++ 5 | 6 | # Проект "Погода" 7 | 8 | Веб-приложение для просмотра текущей погоды. Пользователь может зарегистрироваться и добавить в коллекцию одну или несколько локаций (городов, сёл, других пунктов), после чего главная страница приложения начинает отображать список локаций с их текущей погодой. 9 | 10 | ## Что нужно знать 11 | 12 | - [Java](../technologies/java.md) - коллекции, ООП 13 | - [Паттерн MVC(S)](../technologies/java.md#mvc) 14 | - [Maven/Gradle](../technologies/build-systems.md) 15 | - [Backend](../technologies/backend.md) 16 | - Spring MVC 17 | - HTTP - GET и POST запросы, HTTP заголовки, cookies 18 | - Thymeleaf 19 | - [Базы данных](../technologies/databases.md) 20 | - SQL 21 | - Hibernate 22 | - Миграции 23 | - [Frontend](../technologies/frontend.md) - HTML/CSS, Bootstrap 24 | - [Тесты](../technologies/tests.md) - интеграционное тестирование, моки, JUnit 5 25 | - [Деплой](../technologies/dev-ops.md#деплой) - облачный хостинг, командная строка Linux, Tomcat 26 | 27 | ## Мотивация проекта 28 | 29 | - Использование cookies и сессий для авторизации пользователей 30 | - Знакомство со Spring 31 | - Работа с внешними API 32 | 33 | ## Функционал приложения 34 | 35 | Работа с пользователями: 36 | - Регистрация 37 | - Авторизация 38 | - Logout 39 | 40 | Работа с локациями: 41 | - Поиск 42 | - Добавление в список 43 | - Просмотр списка локаций, для каждой локации отображается название и температура 44 | - Удаление из списка 45 | 46 | ## Интерфейс приложения 47 | 48 | Для вёрстки предлагаю пользоваться [Bootstrap 5](https://getbootstrap.com/docs/5.0/getting-started/introduction/). 49 | 50 | ### Главная страница 51 | 52 | - Заголовок 53 | - Для неавторизованных пользователей - кнопки регистрации и авторизации 54 | - Для авторизованных пользователей - логин текущего пользователя и кнопка Logout 55 | - Контент 56 | - Поле ввода для поиска локации по названию 57 | - Список добавленных локаций. Каждый элемент списка отображает название, текущую температуру и кнопку "удалить" 58 | 59 | ### Страница результатов поиска локаций по названию 60 | 61 | Переход на эту страницу осуществляется в результате заполнения поля ввода на главной странице, либо на странице результатов поиска. 62 | 63 | Содержимое: 64 | - Заголовок, такой же как на главной странице 65 | - Поле ввода для поиска по названию - такое же, как на главной странице, чтобы не возвращаться туда для каждого нового поиска 66 | - Список найденных локаций с кнопкой "добавить". При нажатии на кнопку происходит переход на главную страницу 67 | 68 | ### Остальное 69 | 70 | - Страницы с формами регистрации и авторизации 71 | 72 | ## Работа с сессиями и cookies 73 | 74 | Чтобы реализовать авторизацию пользователя и позволить браузеру "запоминать" авторизован ли текущий пользователь, необходимы сессии и cookies. Подразумевается, что студент имеет общее представление об этих понятиях. 75 | 76 | При авторизации пользователя бэкенд приложение создаёт сессию с идентификатором, и устанавливает этот идентификатор в cookies HTTP ответа, которым приложение отвечает на POST запрос формы авторизации. К тому же, сессия содержит в себе ID авторизовавшегося юзера. 77 | 78 | Дефолтным `JSESSIONID` не пользуемся! 79 | 80 | Далее, при каждом запросе к любой странице, бэкенд приложение анализирует cookies из запроса и определяет, существует ли сессия для ID из cookies. Если есть - страница рендерится для того пользователя, ID которого соответствует ID сессии из cookies. 81 | 82 | Фреймворки, в том числе Spring, умеют управлять всем этим. Цель этого проекта - поработать с cookies и сессиями вручную, чтобы понимать, как они работают, поэтому принципиально советую не ипользовать Spring Security, Spring Session. 83 | 84 | ## Spring MVC 85 | 86 | Какие компоненты Spring пригодятся в проекте: 87 | 88 | - Beans, Dependency Injection 89 | - Контроллеры 90 | - Профили 91 | - Интеграция с Thymeleaf, Flyway/Liquibase 92 | - Spring Tests 93 | - RestTemplate / WebClient 94 | 95 | ## База данных 96 | 97 | В этом проекте предлагаю использовать Postgres/MySQL/MariaDB. Создавать таблицы будем не через Hibernate, как в предыдущем проекте, а с помощью инструмента миграций - Flyway/Liquibase. 98 | 99 | ### Таблица `Users` 100 | 101 | | Колонка | Тип | Комментарий | 102 | |----------|---------|--------------------------------------------------------------------------------------------| 103 | | ID | int | Айди пользователя, автоинкремент, первичный ключ | 104 | | Login | Varchar | Логин пользователя, username или email | 105 | | Password | Varchar | Хранить пароль в открытом виде небезопасно, лучше использовать шифрование, например BCrypt | 106 | 107 | ### Таблица `Locations` 108 | 109 | Локации пользователя, в которых он хочет знать погоду. Одна и та же локация может повторяться для нескольких пользователей. 110 | 111 | | Колонка | Тип | Комментарий | 112 | |-----------|---------|---------------------------------------------| 113 | | ID | int | Айди локации, автоинкремент, первичный ключ | 114 | | Name | Varchar | Название | 115 | | UserId | int | Пользователь, добавивший эту локацию | 116 | | Latitude | Decimal | Широта локации | 117 | | Longitude | Decimal | Долгота локации | 118 | 119 | ### Таблица `Sessions` 120 | 121 | | Колонка | Тип | Комментарий | 122 | |-----------|----------|----------------------------------------------------------------------| 123 | | ID | UUID | Айди сессии, UUID, первичный ключ | 124 | | UserId | int | Пользователь, для которого сессия создана | 125 | | ExpiresAt | Datetime | Время истечения сессии. Равно времени создания сессии плюс `N` часов | 126 | 127 | ## Получение информации о погоде с помощью OpenWeatherMap API 128 | 129 | В предыдущих проектах мы писали своё API, в этом будем пользоваться чужим. Нам нужно искать локации по названию и получать погоду для локации. 130 | 131 | ### OpenWeather 132 | 133 | Существует множество сервисов, предоставляющих API с таким функционалом, один из них - [https://openweathermap.org/](https://openweathermap.org/). Я выбрал этот вариант, потому что он позволяет бесплатно совершать 60 запросов в минуту. 134 | 135 | Для выполнения запросов к API нужен ключ, для его получения необходимо: 136 | - Зарегистрироваться на [https://openweathermap.org/](https://openweathermap.org/) 137 | - Создать бесплатный ключ с лимитом 60 запросов в минуту 138 | - Дождаться активации ключа (придет на указанную Вами почту) 139 | 140 | ### Работа с API 141 | 142 | Документация - [https://openweathermap.org/api](https://openweathermap.org/api). 143 | 144 | Нам нужно 2 метода API: 145 | - Поиск локаций по названию - [https://openweathermap.org/current#geocoding](https://openweathermap.org/current#geocoding) 146 | - Получение погоды по координатам локации - [https://openweathermap.org/current#one](https://openweathermap.org/current#one) 147 | 148 | Первым делом следует поэкспериментировать с API вручную, чтобы понять как делать запросы, и что приходит в ответе. Сделать это можно в [HTTP клиенте](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html) встроенном в Intellij IDEA, либо воспользоваться отдельным приложением, например [https://insomnia.rest/](https://insomnia.rest/). 149 | 150 | ### Интеграция OpenWeather API с Java приложением 151 | 152 | Шаги: 153 | - Делаем запрос 154 | - Получаем ответ 155 | - Десериализуем ответ в объект Java 156 | 157 | Для работы с API потребуется HTTP Client, например [https://www.baeldung.com/java-9-http-client](https://www.baeldung.com/java-9-http-client). 158 | 159 | С десериализацией поможет `JsonMapper` из библиотеки Jackson. 160 | 161 | ## Фронтенд 162 | 163 | Я считаю, что уметь делать простой фронтенд с нуля - универсально полезный навык для всех разработчиков. Однако, в условиях дефицита времени это может быть нерационально. 164 | 165 | Поэтому, для проекта существует готовая верстка, которую можно взять за основу. Если хотите расширить функционал проекта или сделать верстку с нуля - приветствую желание самостоятельно это сделать. 166 | 167 | Репозиторий с версткой - [https://github.com/zhukovsd/weather-viewer-html-layouts](https://github.com/zhukovsd/weather-viewer-html-layouts). 168 | 169 | Что внутри - макеты четырёх страниц, адаптивная верстка (десктопы, телефоны). Минимальный Javascript для показа меню навигации на телефонах. 170 | 171 | Задеплоенные на GitHub Pages страницы для демонстрации: 172 | - [Главная страница](https://zhukovsd.github.io/weather-viewer-html-layouts/index.html) с 5 карточками погоды 173 | - [Результаты поиска](https://zhukovsd.github.io/weather-viewer-html-layouts/search-results.html) 174 | - [Авторизация](https://zhukovsd.github.io/weather-viewer-html-layouts/sign-in.html), дополнительный [вариант](https://zhukovsd.github.io/weather-viewer-html-layouts/sign-in-with-errors.html) с ошибками валидации 175 | - [Регистрация](https://zhukovsd.github.io/weather-viewer-html-layouts/sign-up.html), дополнительный [вариант](https://zhukovsd.github.io/weather-viewer-html-layouts/sign-up-with-errors.html) с ошибками валидации 176 | - [Страница](https://zhukovsd.github.io/weather-viewer-html-layouts/error.html) с ошибкой 177 | 178 | Как пользоваться: 179 | - Перенести в проект нужные для веб-страниц ресурсы - CSS, картинки 180 | - На основе HTML верстки создать шаблонизированные страницы с добавлением тегов Thymeleaf 181 | 182 | ## Тесты 183 | 184 | ### Интеграционные тесты сервисов по работе с пользователями и сессиями 185 | 186 | Покроем тестами связку слоя данных с классами-сервисами, отвечающими за пользователей и сессии. 187 | 188 | Пример - вызов метода регистрации в классе-сервисе, отвечающем за работу с пользователями, должен привести к тому, что в таблице `Users` появляется новая запись. Если при регистрации автоматически создается сессия, так же можем проверить, что она была создана. 189 | 190 | Что ещё стоит проверить тестами: 191 | - Регистрация юзера с неуникальным логином приводит к exception 192 | - Истекание сессии 193 | 194 | Детали: 195 | - Для тестов должна использоваться БД, отдельная от основной (in-memory БД или независимая schema в основной БД), которая пересоздается (или очищается) перед каждым тест кейсом 196 | - Понадобится 2 конфигурации приложения - основная (для разработки и деплоя) и для прогона тестов. Конфигурации могут отличаться настройками доступа к БД для Hibernate, настройками приложения (длительность сессии, например) 197 | 198 | ### Интеграционные тесты для сервиса по работе с OpenWeather API 199 | 200 | Покроем тестами связку HTTP клиента и класса-сервиса, который пользуется этим клиентом для получения данных. Для того чтобы не делать настоящие запросы к API во время прогона тестов, следует использовать мок HTTP клиента и его ответов. 201 | 202 | Пример - запрашиваем список локаций у сервиса, мок HTTP клиента возвращает заданный в тесте ответ (в виде строки, например), сервис его парсит и возвращает коллекцию объектов-моделей. Проверяем, что коллеция содержит ожидаемую локацию. 203 | 204 | Что ещё стоит проверить тестами: 205 | - В случае ошибки (статусы 4xx, 5xx) от OpenWeather API сервис выбрасывает ожидаемый тип исключения 206 | 207 | ## Деплой 208 | 209 | Будем вручную деплоить war артефакт в Tomcat, установленный на удалённом сервере. Потребуется установка внешней SQL БД по выбору. 210 | 211 | Шаги: 212 | - Локально собрать war артефакт приложения 213 | - В хостинг-провайдере по выбору арендовать облачный сервер на Linux 214 | - Установить JRE, Tomcat, выбранную SQL БД 215 | - Зайти в админский интерфейс Tomcat, установить собранный war артефакт 216 | 217 | Ожидаемый результат - приложение доступно по адресу `http://$server_ip:8080/$app_root_path`. 218 | 219 | ## План работы над приложением 220 | 221 | - Создать заготовку Java бэкенд приложения на Spring MVC 222 | - Написать модели сущностей БД - `User`, `Location`, `Session` и миграции для создания таблиц 223 | - С помощью Thymeleaf создать страницы авторизации и регистрации 224 | - В обработчиках форм авторизации и регистрации реализовать бизнес логику работы с сессиями, cookies, БД 225 | - Интеграционные тесты для сервиса регистрации 226 | - Написать сервис для работы с OpenWeather API 227 | - Интеграционные тесты для OpenWeather API 228 | - Реализовать бизнес логику приложения - поиск, добавление, удаление локаций, просмотр погоды 229 | - Создать интерфейс главной страницы и страницы поиска локаций 230 | - Деплой 231 | 232 | ## Ресурсы для работы над ошибками 233 | 234 | - [Реализации проекта](../finished-projects/weather-viewer.md) другими студентами и мои ревью этих реализаций 235 | - Чеклист для самопроверки с типовыми ошибками (в конце страницы) 236 | - Присылайте законченные проекты в [чат](https://t.me/zhukovsd_it_chat), добавляю их в [список](../finished-projects/weather-viewer.md), сообщество делает ревью проектов 237 | 238 | ## Чеклист для самопроверки 239 | 240 | ❗️**Спойлеры**: советую не читать этот список до того момента, пока не допишете первую самостоятельную работающую версию проекта❗️ 241 | 242 | Функциональные проблемы: 243 | - Отсутствие валидации и визуализации ошибок при регистрации и авторизации 244 | - Несовпадение имени локации, по которой мы её добавляли, с тем именем, по которому она отображается, пример: 245 | - Ищем "Санкт-Петербург", добавляем, сохранив в БД координаты 246 | - Для показа погоды делаем запрос к API по координатам, получаем погоду и имя локации. Найденная по координатам локация называется "Новая Голландия", а не "Санкт-Петербург" 247 | - Невозможность отличить несколько локаций с одним названием. Пример - в мире есть несколько городов с названием "Гомель". Возможные решения - показывать координаты локации 248 | - Возможность удалить локацию чужого пользователя через подмену ID в запросе 249 | 250 | Проблемы и ошибки в коде: 251 | - Уязвимость к race conditions, подробно на примере разбирал [здесь](https://t.me/zhukovsd_it_chat/16351) 252 | - Размытые границы между слоями MVC архитектуры приложения 253 | - БД и сущности: 254 | - Отсутствие unique индексов на поля или комбинации полей, которые должны быть уникальными. Например - логин пользователя 255 | - Отсутствие внешних ключей между таблицами 256 | - Использование @Data из Lombok на классах-сущностях потенциально опасно - [https://habr.com/ru/companies/haulmont/articles/564682/](https://habr.com/ru/companies/haulmont/articles/564682/) 257 | - Излишне сложный дизайн хранения локаций пользователей (например, через many-to-many). Подробно эту тему разбирал тут [https://www.youtube.com/watch?v=yLBn7qmyCOk](https://www.youtube.com/watch?v=yLBn7qmyCOk) 258 | - Недостаточно очевидный нейминг 259 | - Работа с внешним API: 260 | - Ручной парсинг ответов вместо использования Jackson + DTO 261 | 262 | Общее: 263 | - Трудно локально запустить проект, отсутствие README 264 | - Ключ для OpenWeather API не должен попасть в репозиторий 265 | -------------------------------------------------------------------------------- /content/technologies/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Технологии' 3 | weight = 20 4 | bookFlatSection = true 5 | +++ 6 | -------------------------------------------------------------------------------- /content/technologies/backend.md: -------------------------------------------------------------------------------- 1 | +++ 2 | weight = 2 3 | title = 'Backend' 4 | +++ 5 | 6 | # Backend 7 | 8 | Этот документ посвящён необходимым знаниям, относящихся к Java Backend, напрямую или косвенно. 9 | 10 | ## Общие знания 11 | 12 | Владение общими знаниями хорошо тем, что они применимы к любой Backend платформе, будь то Java, .Net, NodeJS или другие. 13 | 14 | ### Клиент-серверное взаимодействие, IP адреса, DNS 15 | 16 | Базовые идеи, о которых необходимо иметь общее представление. Что нужно знать: 17 | - Что такое IP адрес 18 | - Разница между TCP и UDP 19 | - Что такое TCP сокет 20 | - Клиент-серверное взаимодействие. Что именно происходит, когда браузер запрашивает страницу сайта 21 | - Что такое DNS 22 | 23 | #### Избранные курсы и учебные ресурсы 24 | 25 | - [Моя лекция](https://www.youtube.com/watch?v=9rpgE3nZb94) по основам бэкенда и истории развития бэкенд и веб приложений 26 | - [Плейлист](https://www.youtube.com/playlist?list=PLnh8EajVFTl7_p5MgevvA41PvxQWq-jC8) от dmdev - уроки с 1 по 6 27 | - [MDN How does the Internet work](https://developer.mozilla.org/ru/docs/Learn/Common_questions/How_does_the_Internet_work) 28 | - [MDN How the Web works](https://developer.mozilla.org/ru/docs/Learn/Getting_started_with_the_web/How_the_Web_works) 29 | 30 | ### HTTP 31 | 32 | Главный протокол WEB. 33 | 34 | Что нужно знать: 35 | - HTTP методы 36 | - Заголовки, cookies 37 | - Коды ответа 38 | - Form data, multipart form data 39 | 40 | #### Избранные курсы и учебные ресурсы 41 | 42 | - [Статья](https://zametkinapolyah.ru/servera-i-protokoly/chto-nuzhno-znat-pro-http-protokol-veb-razrabotchiku-pravila-http-protokola.html) про HTTP простым языком 43 | - [Моя лекция](https://www.youtube.com/watch?v=yWzesBoTOvE) по HTTP, API, REST 44 | - Практика - проекты, начиная с 3 45 | 46 | ## Авторизация 47 | 48 | При авторизации пользователя, бэкенд приложению необходимо авторизовывать запросы, и понимать, кто их делает. Например, при загрузке страницы личного кабинета, неавторизованному пользователю будет отказано в доступе, а авторизованному - показана его личная информация. 49 | 50 | Для реализации этого на практике используется 2 основных идеи и способов "привязки" сессии текущего пользователя к клиентам (обычно, браузерам). Идеи: 51 | - Сессии 52 | - JWT 53 | 54 | Способы привязки: 55 | - Cookies 56 | - HTTP заголовки 57 | 58 | ### Сессии 59 | 60 | Классическая реализация: 61 | - Сессия генерируется при авторизации, сохраняется в БД (колонки - id, user id), ID сессии устанавливается клиенту через куку 62 | - При каждом запросе клиент отправляет куку, бэкенд приложение ищет сессию в БД по ID из куки, и определяет, какой пользователь сделал запрос 63 | 64 | Spring Boot по-умолчанию берёт всю работу с сессиями на себя, и хранит их в памяти. Поэтому, чтобы понимать как они работают, 5 проект реализует работу с сессиями без фреймворков. 65 | 66 | ### JWT 67 | 68 | JWT расшифровывается как Json Web Token. Информация о сессии целиком хранится внутри токена. При генерации токена он подписывается секретным ключом, который знает только бэкенд приложение, что позволяет защититься от его подделки. 69 | 70 | Основное преимущество - JWT токены не хранятся со стороны бэкенда, а передаются целиком при каждом запросе. 71 | 72 | #### Избранные курсы и учебные ресурсы 73 | 74 | - Ключевые отличия сессий от JWT, плюсы и минусы каждого варианта - [https://stytch.com/blog/jwts-vs-sessions-which-is-right-for-you/](https://stytch.com/blog/jwts-vs-sessions-which-is-right-for-you/) 75 | - Куки - [https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) 76 | - Сессии - [https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Sessions](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Sessions) 77 | - JWT: 78 | - [https://jwt.io/introduction](https://jwt.io/introduction) 79 | - Интеграция JWT со Spring - [https://www.bezkoder.com/spring-boot-jwt-authentication/](https://www.bezkoder.com/spring-boot-jwt-authentication/) 80 | - Практика - проекты #5 ["Погода"](../projects/weather-viewer.md) и #6 ["Облачное хранилище файлов"](../projects/cloud-file-storage.md) (сессии), #7 ["Планировщик задач"](../projects/task-tracker.md) - JWT 81 | 82 | ## REST API 83 | 84 | REST - набор правил взаимодействия клиента и сервера на основе HTTP. Сделать работающее клиент-серверное приложение легко, элегантно его спроектировать - сложно. 85 | 86 | Что нужно знать: 87 | - Отличие RESTful от RESTless 88 | - Базовые дизайн принципы и типовые ошибки, которых следует избегать 89 | 90 | #### Избранные курсы и учебные ресурсы 91 | 92 | - Статьи по REST на хабре - [#1](https://habr.com/ru/post/483202/), [#2](https://habr.com/ru/post/351890/) 93 | - [Моя лекция](https://www.youtube.com/watch?v=yWzesBoTOvE) по HTTP, API, REST 94 | - Книга по дизайну REST API - [https://www.amazon.com/REST-Design-Rulebook-Mark-Masse/dp/1449310508](https://www.amazon.com/REST-Design-Rulebook-Mark-Masse/dp/1449310508) 95 | - Практика - проекты #3 ["Обмен валют"](../projects/currency-exchange.md) и #7 ["Планировщик задач"](../projects/task-tracker.md) содержат примеры дизайна REST API 96 | 97 | ## Java 98 | 99 | Перейдём к знаниям, относящимся напрямую к Backend разработке на Java. 100 | 101 | ### Java Servlets 102 | 103 | Сервлет - сущность в Java, реализующая клиент-серверное взаимодействие со стороны сервера. Сервлет принимает соединения от клиентов, и отвечает на запросы. 104 | 105 | Что нужно уметь: 106 | - Реализовывать сервлеты, пользуясь пакетом `javax.servlet-api` (или `jakarta.servlet`) 107 | - Запускать Java проект с сервлетами с помощью Tomcat 108 | 109 | #### Избранные курсы и учебные ресурсы 110 | 111 | - [Бесплатный курс](https://www.youtube.com/playlist?list=PLAma_mKffTOTTFqIkLXgHqVuL6xJhb0mr) по Java EE от Наиля Алишева 112 | - [Статья](https://javarush.com/groups/posts/2529-chastjh-5-servletih-pishem-prostoe-veb-prilozhenie) с примерами на сайте Javarush 113 | - [Краткое интро](https://www.baeldung.com/intro-to-servlets) в сервлеты от Baeldung 114 | - Практика - проекты с 3 по 4 115 | 116 | ### Spring 117 | 118 | Spring - основной фреймворк Java разработчика с долгой историей. Основная сложность - из-за возраста у фреймворка существует несколько версий (Spring начинал свою историю как Spring Mvc, позднее эволюционировал в Spring Boot), и одно и то же можно сделать несколькими способами, и новичку не всегда понятно, какой способ лучше. 119 | 120 | Что нужно уметь: 121 | - Работать с базами данных 122 | - Реализовывать REST API 123 | - Создавать веб страницы 124 | - Библиотеки экосистемы Spring Boot - Spring Security, Spring Sessions 125 | 126 | #### Избранные курсы и учебные ресурсы 127 | 128 | - Начать можно с [бесплатного плейлиста](https://www.youtube.com/playlist?list=PLAma_mKffTOR5o0WNHnY0mTjKxnCgSXrZ) Алишева на YouTube 129 | - [Spring - полный курс](https://swiftbook.org/courses/438) Наиля Алишева - разделы Spring Core, Spring MVC, Spring Boot, Spring Security 130 | - Практика - проекты с 5 по 7 131 | 132 | ### Шаблонизаторы веб-страниц 133 | 134 | Шаблонизатор - инструмент создания веб-страниц с динамическим контентом. Классический пример - отображение на веб-странице информации, прочитанной из базы данных. 135 | 136 | Для Java и Spring существует множество шаблонизаторов. Остановимся на двух из них, JSP - старый, классический инструмент, который уже не используется, но на примере которого можно понять основные идеи шаблонизации. Thymeleaf - современный и мощный инструмент. 137 | 138 | Что нужно уметь: 139 | - Передавать данные из Java в шаблон 140 | - Thymyleaf - интеграция со Spring Boot, Thymeleaf фрагменты 141 | 142 | #### Избранные курсы и учебные ресурсы 143 | 144 | - [Видео](https://www.youtube.com/watch?v=w1FjeTZxQrQ&list=PLAma_mKffTOR5o0WNHnY0mTjKxnCgSXrZ&index=23) Алишева про Thymeleaf 145 | - [Документация](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html) 146 | - Практика - проекты 5, 6 147 | 148 | ## Что дальше 149 | 150 | Перечисленные выше знания и инструменты - база для Java Junior. Если есть желание и необходимость углубиться в Backend технологии, советую обратить внимание на: 151 | - Websocket 152 | - XML, Protobuf 153 | - Альтернативны REST - SOAP, GRPC, GraphQL 154 | - Более новые Java фреймворки - Micronaut, Quarkus 155 | - Реактивный бэкенд - Spring Webflux 156 | -------------------------------------------------------------------------------- /content/technologies/build-systems.md: -------------------------------------------------------------------------------- 1 | +++ 2 | weight = 4 3 | title = 'Системы сборки' 4 | +++ 5 | 6 | # Системы сборки 7 | 8 | Документ посвящён необходимым знаниям, относящимся к системам сборки Java проектов - Maven и Gradle. 9 | 10 | Первой популярной системой сборки для Java был Apache Ant, но он морально устарел и на практике почти не встречается. 11 | 12 | Начинать знакомство с системами сборки предлагаю с Maven, практиковаться с ним на проектах до пятого включительно, в проектах 6 и 7 попробовать Gradle. Maven появился раньше, поэтому, логичнее начинать с него, потом переключиться на Gradle. 13 | 14 | На уровне Junior, принципиальной разницы между двумя этими инструментами нет, в работе можно встретить обе системы сборки, поэтому есть смысл попрактиковаться с обеими. 15 | 16 | ## Maven 17 | 18 | Для изучения Maven нет необходимости читать целые книги или проходить курсы. Начать стоит с краткой выжимки основных идей (например - https://habr.com/ru/post/77382/), далее, немного углубиться в теорию и заняться практикой. 19 | 20 | Что нужно знать, где почитать: 21 | 22 | - Идеи Maven - что такое Maven lifecycle, phase и goal - [https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html) 23 | - Локальный репозиторий Maven - [https://www.baeldung.com/maven-local-repository](https://www.baeldung.com/maven-local-repository) 24 | - Maven POM - что это такое, основные секции файла - зависимости, build плагины, свойства проекта - [https://maven.apache.org/guides/introduction/introduction-to-the-pom.html](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html) 25 | - Практика - создание Spring Boot Maven проекта с помощью Spring Initializr: 26 | - На сайте [https://start.spring.io/](https://start.spring.io/) 27 | - В среде Intellij IDEA 28 | - Практика - использование Maven в Java проектах 29 | - Добавление зависимостей 30 | - Сборка и запуск приложения через командную строку 31 | 32 | Полезно, но не обязательно: 33 | 34 | - Что такое Maven BOM - [https://www.baeldung.com/spring-maven-bom](https://www.baeldung.com/spring-maven-bom) 35 | - Что такое транзитивные зависимости, и как Maven BOM решает эту проблему - [https://reflectoring.io/maven-bom/](https://reflectoring.io/maven-bom/) 36 | - Maven BOM фреймворка Spring Boot - [https://www.baeldung.com/spring-maven-bom](https://www.baeldung.com/spring-maven-bom) 37 | - Multi-module проекты - [https://www.baeldung.com/maven-multi-module](https://www.baeldung.com/maven-multi-module) 38 | - Maven архетипы - [https://www.baeldung.com/maven-archetype](https://www.baeldung.com/maven-archetype) 39 | - Развёртывание своих Maven репозиториев 40 | - Релиз артефактов в Maven репозитории 41 | 42 | #### Избранные курсы и учебные ресурсы 43 | 44 | - [Плейлист](https://www.youtube.com/playlist?list=PLnh8EajVFTl5fusY9MRBEOoLjbv8Trms5) от dmdev 45 | - Курс ["Продвинутая Java"](https://www.udemy.com/course/javarussia/) Наиля Алишева - глава "Maven" 46 | - Практика - проекты с 3 по 7 47 | 48 | ## Gradle 49 | 50 | Gradle - более новая система сборки, чем Maven. Для Java Backend проектов на Spring, Maven и Gradle в большинстве случаев взаимозаменяемы, лично я предпочитаю Gradle из-за производительности. Для Android проектов используется только Gradle. 51 | 52 | Что нужно знать, где почитать: 53 | 54 | - Отличия Gradle от Maven - [https://gradle.org/maven-vs-gradle/](https://gradle.org/maven-vs-gradle/) 55 | - Gradle Lifecycle - [https://docs.gradle.org/current/userguide/build_lifecycle.html](https://docs.gradle.org/current/userguide/build_lifecycle.html) 56 | - Что такое DSL. Gradle Groovy/Kotlin DSL - [https://docs.gradle.org/current/userguide/tutorial_using_tasks.html](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html) 57 | - Практика - создание Spring Boot Gradle проекта с помощью Spring Initializr: 58 | - На сайте [https://start.spring.io/](https://start.spring.io/) 59 | - В среде Intellij IDEA 60 | - Практика - использование Gradle в Java проектах 61 | - Добавление зависимостей 62 | - Сборка и запуск приложения через командную строку 63 | 64 | #### Избранные курсы и учебные ресурсы 65 | 66 | - [Плейлист](https://www.youtube.com/playlist?list=PLnh8EajVFTl7JYxhZs7CfxyTsUMvcBXig) от dmdev 67 | - Практика - проекты с 3 по 7 68 | -------------------------------------------------------------------------------- /content/technologies/databases.md: -------------------------------------------------------------------------------- 1 | +++ 2 | weight = 3 3 | title = 'Базы данных' 4 | +++ 5 | 6 | # Базы данных 7 | 8 | Документ посвящен знаниям по базам данных, необходимых для реализации проектов. Базы данных - один из ключевых навыков для бэкенд разработчиков. 9 | 10 | ## SQL 11 | 12 | Что нужно знать: 13 | - SQL синтаксис - `SELECT`, `INSERT`, `UPDATE/DELETE`, `GROUP BY`, `JOIN` 14 | - Создание таблиц 15 | - Индексы и первичный ключ 16 | - Внешние ключи 17 | - RDBMS - MySQL, Postgres. Различные книги/курсы, как правило, учат SQL в контексте в одной из этих двух систем управления баз данных. Для джуниров разница между ними не принципиальна 18 | - Embedded SQL databases - SQLite, H2 19 | 20 | #### Избранные курсы и учебные ресурсы 21 | 22 | - Курс ["SQL для начинающих: с нуля до сертификата Oracle"](https://stepik.org/course/115617) Заура Трегулова - для начала достаточно глав 1-3, 6-7, 10-12 23 | - Практика: 24 | - Интерактивные задачи для практики синтаксиса - [https://app.codesignal.com/](https://app.codesignal.com/) (раздел Arcade - Databases) 25 | - Проект #3 - ["Обмен валют"](../projects/currency-exchange.md) 26 | 27 | ## JDBC 28 | 29 | Базовый инструмент для работы с SQL из Java. 30 | 31 | Что нужно уметь: 32 | - Подключать библиотеки для работы JDBC через Maven/Gradle в проекты со Spring и без него 33 | - Создавать таблицы 34 | - Читать данные из таблиц в ассоциативные массивы или в экземпляры классов-моделей 35 | 36 | #### Избранные курсы и учебные ресурсы 37 | 38 | - Уроки 25-27 из [бесплатного курса](https://www.youtube.com/playlist?list=PLAma_mKffTOR5o0WNHnY0mTjKxnCgSXrZ) от Наиля Алишева 39 | - [Статья](https://www.baeldung.com/java-jdbc) от Baeldung 40 | - Практика: 41 | - Проект #3 - ["Обмен валют"](../projects/currency-exchange.md) 42 | 43 | ## ORM 44 | 45 | ORM - инструмент для стирания границ между записями в базе и Java объектами. Таблицы в базе данных, как правило, описывают некоторые сущности - пользователей, заказы, товары. ORM позволяют описать эти сущности с помощью Java классов и совершать действия над таблицами (чтение, вставку, и прочие) без написания SQL синтаксиса - ORM это берёт на себя. 46 | 47 | Сфокусируемся на ORM Hibernate и Spring Data JPA. 48 | 49 | Что нужно уметь: 50 | - Подключать библиотеки для работы Hibernate через Maven/Gradle в проекты со Spring и без него 51 | - Писать классы, описывающие хранящиеся в таблицах сущности 52 | - Hibernate - CRUD операции (create, read, update, delete) 53 | - Spring Data JPA - CRUD операции с помощью интерфейсов-репозиториев Spring Data 54 | 55 | #### Избранные курсы и учебные ресурсы 56 | 57 | - [Плейлист](https://www.youtube.com/playlist?list=PLnh8EajVFTl7dQ77iqr55gFLcyYjedAlE) по Hibernate от dmdev 58 | - [Плейлист](https://www.youtube.com/playlist?list=PLV_vplloSltGFfLBI-Eun-X849eVxCZvR) по Hibernate от канала "Хьюстон у нас проблемы" 59 | - [Spring - полный курс](https://swiftbook.org/courses/438) Наиля Алишева - разделы Hibernate, Spring Data JPA 60 | - Практика - проекты с 4 по 7 61 | 62 | ## Инструменты миграций 63 | 64 | Миграции SQL баз данных - инструмент версионирования структуры таблиц и данных, который позволяет бесшовно менять и дополнять состояние таблиц в проектах. Проводя аналогию с git, одна миграция эквивалента одному коммиту, только субъектом изменения является база данных, а не репозиторий с кодом. Широко используется в коммерческих проектах. 65 | 66 | 2 самых популярных инструмента для Java Backend приложений - FlyWay, Liquibase. Советую попробовать оба. 67 | 68 | Что нужно уметь: 69 | - Интегрировать инструмент миграций в приложение на Spring MVC / Spring Boot 70 | - Понимать взаимодействия миграций и автоматического создания таблиц через ORM Hibernate 71 | - Писать миграции, меняющие схему БД и данные внутри таблиц 72 | - Применять новые миграции к уже задеплоенному проекту 73 | 74 | #### Избранные курсы и учебные ресурсы 75 | 76 | - [Видео](https://www.youtube.com/watch?v=5XUjsCL3KaU) по Flyway от dmdev, [документация](https://documentation.red-gate.com/flyway/flyway-cli-and-api/welcome-to-flyway) 77 | - [Видео](https://www.youtube.com/watch?v=prLt2LHbA8o) по Liquibase от Константина Шибкова, [документация](https://docs.liquibase.com/home.html) 78 | - Практика - проекты с 5 по 7 79 | 80 | ## NoSQL базы данных 81 | 82 | SQL - самая нужная для джуниора технология для хранения, но кроме SQL существуют десятки инструментов для хранения данных, созданных под ситуации, где SQL излишен, либо неудобен. Знать все типы баз данных на практике - невозможно, их слишком много. Достаточно иметь представление о том, какие из них и в каких ситуациях используются. 83 | 84 | В рамках проектов, на практике поработаем с S3/Minio - хранилищем файлов, и Redis - хранилищем кэша/сессий. 85 | 86 | #### Избранные курсы и учебные ресурсы 87 | 88 | - Практика - проекты 6 и 7 89 | 90 | ## Что дальше 91 | 92 | Перечисленные выше знания и инструменты - база для Java Junior (кроме NoSQL). Если есть желание и необходимость углубиться в Backend технологии, советую обратить внимание на: 93 | 94 | - Document-oriented DB - MongoDB 95 | - Key-value storage - Redis 96 | - Интеграции хранилищ кэша со Spring Boot - Hazelcast, Ehcache 97 | -------------------------------------------------------------------------------- /content/technologies/dev-ops.md: -------------------------------------------------------------------------------- 1 | +++ 2 | weight = 5 3 | title = 'DevOps' 4 | +++ 5 | 6 | # DevOps 7 | 8 | DevOps - размытый термин. Его "эталонное" значение - набор практик по разработке ПО, направленных на повышение качества, сокращения длительности циклов разработки между релизами. Основная идея - объединить разработку, тестирование и эксплуатацию в единый цикл. Проблема, которую решает DevOps, происходит из времён, когда разработкой и эксплуатацией продуктов занимались независимые друг от друга люди и отделы, что создавало излишнее трение и задержки. 9 | 10 | Степень и глубина применения DevOps практик варьируются от проекта к проекту. Общими элементами, как правило, являются: 11 | 12 | - Методология разработки и управления проектами - Agile/Scrum/Kanban 13 | - CI/CD - инструменты автоматизации сборки, деплоя, тестирования проекта 14 | - Git flow, [pull request](https://www.atlassian.com/ru/git/tutorials/making-a-pull-request) - модель ветвления git репозитория для эффективной командной работы и максимально безболезненных частых релизов 15 | - Покрытие кода тестами, автоматическое тестирование - исполнитель задачи ответственен за покрытие её автоматизированными тестами. При каждом пуше в репозиторий тесты прогоняются, таким образом, если очередное изменение сломает какой-то функционал, мы узнаем об этом автоматически 16 | - Работа с инфраструктурой - создание (ручное или автоматическое), деплой туда проекта 17 | 18 | В данном документе я опишу то, что относится к DevOps в контексте бэкенд Java разработки, и что полезно знать на уровне Junior. 19 | 20 | ## Методология разработки и управления проектами 21 | 22 | В мире разработки существует 3 основных методологии управления проектами - Agile, Scrum, Kanban. Зачастую, строго методологии не следуют, каждая компания и тимлид имеют свои представления о том, как управлять разработкой. 23 | 24 | Постараюсь описать наиболее часто встречающиеся практики: 25 | 26 | - Система постановки и формализации задач - Jira, Trello. Тимлид ставит задачи, делегирует их членам команды, принимает работу 27 | - Коммуникация внутри команды и между ними - ежедневные daily созвоны, подведение итогов каждые 1-2 недели 28 | - Ревью кода в pull request'ах тимлидом и другими опытными членами команды 29 | 30 | Учить заранее здесь особо нечего. При попадании в команду постарайтесь понять, какими практиками она пользуется, и начать делать так же. 31 | 32 | ## Git 33 | 34 | Git - система контроля версий. Здравая разработка немыслима без системы контроля версий, и Git стал доминирующим инструментом в этой области, поэтому есть смысл учить именно его. Что касается сервисов хранения репозиториев, тут конкуренции больше, для проектов курса предлагаю пользоваться GitHub. 35 | 36 | Что нужно знать и уметь Junior'у: 37 | - Основы Git - add, commit, push, pull, clone 38 | - GitHub - создание репозиторев, работа с доступом (токены, SSH ключи) - [https://docs.github.com/ru](https://docs.github.com/ru) 39 | - Pull requests, умение решать конфликты слияния - [https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) 40 | - GitFlow - [https://habr.com/ru/post/106912/](https://habr.com/ru/post/106912/). Достаточно понять основные идеи, чтобы быть готовым пользоваться этими практиками в командной работе 41 | 42 | #### Избранные курсы и учебные ресурсы 43 | 44 | - Курс по Git - [https://githowto.com/ru](https://githowto.com/ru) 45 | - Интерактивный учебник по ветвлению в git - [https://learngitbranching.js.org/?locale=ru_RU](https://learngitbranching.js.org/?locale=ru_RU) 46 | 47 | ## Деплой 48 | 49 | Деплой - процесс развёртывания и запуска приложения. Самый простой способ деплоя, который можно представить - ручное копирование файлов сайта, написанного на PHP, с помощью FTP клиента на удалённый сервер. 50 | 51 | Практики деплоя со временем эволюционировали. Я считаю, что для понимания современных практик полезно знать, как деплой делался в прошлом, поэтому на примере проектов со 2 по 7 мы будем начинать со старых подходов к деплою и постепенно переходить к более новым. 52 | 53 | Подробности о деплое каждого проекта описаны в ТЗ, ниже приведу примерные шаги эволюции деплоя бэкенд Java приложений: 54 | - Вручную - установка JDK на удалённый сервер, установка необходимых инструментов (например, базы данных), веб-сервера (Tomcat, Jetty), собранного приложения в веб-сервер 55 | - Автоматизированное развёртывание веб-серверов, где уже предустановлено всё необходимое, установка веб-приложения в развёрнутые сервера 56 | - Docker и микросервисы - упаковка сервисов приложения в Docker образы, ручной или автоматический деплой стека сервисов на удалённый сервер 57 | - Облака - ручное или автоматическое развёртывание приложение из микросервисов в облаке, ручное или автоматическое создание инфраструктуры (сервера, базы данных, и всё остальное), на которой разворачивается приложение 58 | 59 | На практике необходимо уметь: 60 | - Работать с хостингами - создавать и управлять виртуальными машинами (в основном на Linux) 61 | - Работа с терминалом Linux на языке bash - базовые команды, установка пакетов 62 | - SSH ключи, подключения 63 | - Веб-сервера для развёртывания Java приложений - Tomcat 64 | - Docker 65 | 66 | ## CI/CD 67 | 68 | CI/CD - непрерывная интеграция и деплой. Основная идея в автоматизации и быстрой реакции на возникающие проблемы, примеры: 69 | - Разработчик делает push в свою ветку. Инструмент автоматизации прогоняет тесты и проверки на качество кода, если что-то не пройдено - разработчик (и другие заинтересованные лица) уведомляются об этом 70 | - Ответственный за релиз делает слияние в релизную ветку. Инструмент автоматизации проводит деплой и оповещает о результате 71 | 72 | Существует множество CI/CD инструментов. В [седьмом проекте](../projects/task-tracker.md) воспользуемся [GitHub Actions](https://docs.github.com/en/actions) для автоматизации сборки Docker образов и деплоя. 73 | 74 | ## Что дальше 75 | 76 | DevOps технология для уровня Middle и выше: 77 | 78 | - Деплой приложений в Kubernetes 79 | - Деплой в облака - AWS, Azure, другие 80 | - Infrastructure as Code (Terraform, AWS CDK) 81 | -------------------------------------------------------------------------------- /content/technologies/frontend.md: -------------------------------------------------------------------------------- 1 | +++ 2 | weight = 6 3 | title = 'Frontend' 4 | +++ 5 | 6 | # Frontend 7 | 8 | Рабочие задачи бэкенд разработчика часто косвенно касаются фронтенда - внедрить в Spring Boot макеты веб-страниц, отобразить на веб-интерфейсе данные с бэкенда. Поэтому, проекты данного курса включают в себя базовый фронтенд. 9 | 10 | ## Вёрстка 11 | 12 | Что нужно знать: 13 | - Основные идеи HTML (теги, атибуты тегов, DOM) 14 | - Основные идеи CSS - каскадность, селекторы 15 | - HTML формы 16 | - Блочная вёрстка, Flexbox 17 | 18 | #### Избранные курсы и учебные ресурсы 19 | 20 | - [Интерактивный курс](https://www.freecodecamp.org/learn/2022/responsive-web-design/) по верстке от FreeCodeCamp 21 | - Практика - в проекте #4 ["Табло теннисного матча"](../projects/tennis-scoreboard.md) необходимо сверстать простой веб-интерфейс с нуля 22 | 23 | ## Bootstrap 24 | 25 | [Bootstrap](https://getbootstrap.com/docs/5.0/getting-started/introduction/) - одна из библиотек, предоставляющих разработчику набор готовых элементов для построения веб-интерфейса. С его помощью можно быстро сверстать несложный интерфейс без глубоких знаний фронтенда. 26 | 27 | Что нужно знать: 28 | - Как интегрировать Bootstrap в веб-приложение 29 | - Сетка Bootstrap - [https://getbootstrap.com/docs/5.0/layout/grid/](https://getbootstrap.com/docs/5.0/layout/grid/) 30 | - Контейнеры - [https://getbootstrap.com/docs/5.0/layout/containers/](https://getbootstrap.com/docs/5.0/layout/containers/) 31 | - Доступные компоненты - кнопки, формы 32 | 33 | #### Избранные курсы и учебные ресурсы 34 | 35 | - Раздел про Bootstrap на FreeCodeCamp - [https://www.freecodecamp.org/learn/front-end-development-libraries/](https://www.freecodecamp.org/learn/front-end-development-libraries/) 36 | - Практика - проекты c 5 по 7 37 | 38 | ## Javascript, jQuery/Fetch 39 | 40 | Javascript - основной фронтенд язык, главная задача которого - управлять динамическим контентом на веб-странице. Примеры - интерактивные элементы интерфейса, браузерные игры, карты. 41 | 42 | Javascript является опциональным для большинства бэкенд разработчиков, поэтому я включил его только в последний, седьмой проект. 43 | 44 | Что нужно знать: 45 | - Основы синтаксиса - переменные, ветвления, циклы, функции, коллекции 46 | - Взимодействие с DOM деревом - изменять, создавать, удалять элементы 47 | - AJAX - фоновое исполнение запросов к бэкенду без перезагрузки страницы. На выбор: 48 | - jQuery - старый и классический способ исполнять AJAX запросы, считается устаревшим 49 | - Fetch API, более новый способ, не требующий подключения библиотек - [https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) 50 | 51 | #### Избранные курсы и учебные ресурсы 52 | 53 | - [https://learn.javascript.ru/](https://learn.javascript.ru/) 54 | - Практика: 55 | - Решение задач на синтаксис, например, на [https://app.codesignal.com/](https://app.codesignal.com/) 56 | - [Проект "Планировщик задач"](../projects/task-tracker.md) включает в себя разработку одностраничного веб-приложения на Javascript 57 | 58 | ## Что дальше 59 | 60 | Если хотите развиваться в сторону Full stack разработчика: 61 | - Javascript 2015 62 | - Системы сборки фронтенда - Webpack 63 | - Javascript фреймворки - React, Vue 64 | - CSS препроцессоры - SCSS/SASS 65 | -------------------------------------------------------------------------------- /content/technologies/java.md: -------------------------------------------------------------------------------- 1 | +++ 2 | weight = 1 3 | title = 'Java' 4 | +++ 5 | 6 | # Java 7 | 8 | Java/Spring - одна из лидирующих платформ для бэкенд разработки, несмотря на возраст. Хороша для старта карьеры, после чего можно углубляться в другие платформы и смежные области, потому что технологии не стоят на месте и их актуальность меняется со временем. 9 | 10 | ## Базовый синтаксис 11 | 12 | Под базовым синтаксисом понимаются основы - переменные и типы данных, ветвления, циклы for/while. 13 | 14 | По этому материалу существует огромное количество ресурсов, книг, курсов. Я советую совмещать теорию с практикой. В качестве теории - книгу или курс, в качестве практики по закреплению синтаксиса - задачи. 15 | 16 | #### Избранные курсы и учебные ресурсы 17 | 18 | - Курс ["Java для начинающих"](https://www.udemy.com/course/beginners-java/) Наиля Алишева, глава - "Основы Java", курс [Продвинутая Java](https://www.udemy.com/course/javarussia/), глава "Коллекции" 19 | - Курс ["Java (Джава) для начинающих: с нуля до сертификата Oracle"](https://www.udemy.com/course/java-oca-oracle/) Заура Трегулова, первые 20 уроков 20 | - "Философия Java" Эккеля, главы 3, 4, 11, 13 21 | - Мой live-coding стрим по написанию проекта в процедурном стиле - [https://www.youtube.com/watch?v=PPikj1qHxrA](https://www.youtube.com/watch?v=PPikj1qHxrA) 22 | - Практика: 23 | - Cайты с задачами, например [https://www.codewars.com/](https://www.codewars.com/). Важно не переборщить с задачами, как только рука будет набита, и задачи уровня medium решаются легко, следует идти дальше. 24 | - Практика - [Проект "Виселица"](../projects/hangman.md) 25 | 26 | Предлагаемый порядок изучения материалов: 27 | - Выбрать 1 курс или книгу, точечно изучить те главы, которые я озвучил 28 | - Параллельно изучению теории решать задачи 29 | - Написать проект "Виселица" 30 | 31 | ## Объектно-ориентированное программирование 32 | 33 | ООП является одной из ключевых идей в основе Java. Потенциальная "глубина" владения ООП велика, и этот навык растет годами. 34 | 35 | Что нужно знать и уметь: 36 | - Синтаксис описания классов, работы с объектами 37 | - Основные идеи ООП - инкапсуляция, наследование, полиморфизм 38 | - Писать свои классы и иерархии 39 | - Понимать чем плохой ООП код отличается от хорошего 40 | 41 | #### Избранные курсы и учебные ресурсы 42 | 43 | - Курс ["Java для начинающих"](https://www.udemy.com/course/beginners-java/) Наиля Алишева, главы со второй по пятую 44 | - Курс ["Java (Джава) для начинающих: с нуля до сертификата Oracle"](https://www.udemy.com/course/java-oca-oracle/), уроки с 21 до конца 45 | - "Философия Java" Эккеля, главы 1, 2, 6, 7, 8, 12, 19 46 | - Серия моих live-coding стримов по написанию проекта в ООП стиле - [https://www.youtube.com/watch?v=Pzydm8GZzMs](https://www.youtube.com/watch?v=Pzydm8GZzMs) 47 | - Практика: 48 | - Практика - [Проект "Симуляция"](../projects/simulation.md) 49 | 50 | Предлагаемый порядок изучения материалов: 51 | - Выбрать 1 курс или книгу, точечно изучить те главы, которые я озвучил 52 | - Написать проект "Симуляция" 53 | 54 | ## Паттерны проектирования 55 | 56 | Паттерны проектирования, общепринятые "рецепты" классов для решения повторяющихся задач. Большинство задач являются типовыми, или их вариациями, и паттерны проектирования предлагают набор решения для этих задач. 57 | 58 | Каждый паттерн - класс или семейство классов с определённым стилем именования и набором методов. 59 | 60 | Что нужно знать: 61 | - Постепенно осваивать паттерны, начиная с самых популярных - Singleton, Factory 62 | 63 | #### Избранные курсы и учебные ресурсы 64 | 65 | - Книги - "Java Head First Design Patterns", "GoF Design Patterns" 66 | - Репозиторий с примерами реализации паттернов на Java - [https://github.com/iluwatar/java-design-patterns](https://github.com/iluwatar/java-design-patterns) 67 | - Примеры применения паттернов в стандартных библиотеках Java - [https://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns-in-javas-core-libraries](https://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns-in-javas-core-libraries) 68 | - [https://refactoring.guru/](https://refactoring.guru/) - коллекция кратких статей по каждому паттерну с примерами 69 | - Практика - проекты курса, начиная со 2, содержат проблемы, которые можно решить с помощью паттернов 70 | 71 | ## MVC 72 | 73 | MVC - самый актуальный для бэкенд приложений паттерн. 74 | 75 | Суть MVC - разбиение кода на "слои", каждый из которых занимается одной задачей: 76 | - Модели - описывают сущности приложения 77 | - View - отображение состояния, в виде веб-страниц, JSON ответов 78 | - Контроллер - принятие запросов от пользователя, создание модели и view для генерации ответа 79 | 80 | Зачастую, MVC расширяется ещё одним слоем - Service, такой паттерн называется MVCS. Классы-сервисы описывают [бизнес-логику](https://ru.wikipedia.org/wiki/%D0%91%D0%B8%D0%B7%D0%BD%D0%B5%D1%81-%D0%BB%D0%BE%D0%B3%D0%B8%D0%BA%D0%B0) приложения. 81 | 82 | #### Избранные курсы и учебные ресурсы 83 | 84 | - Моя лекция по MVC - [https://www.youtube.com/watch?v=syjOb_jPJWE](https://www.youtube.com/watch?v=syjOb_jPJWE) 85 | - Статья с примерами - [https://github.com/iluwatar/java-design-patterns/tree/master/model-view-controller](https://github.com/iluwatar/java-design-patterns/tree/master/model-view-controller) 86 | - Книги - "Java Head First Design Pattern", глава 12 "составные паттерны" 87 | - Практика - проекты курса, начиная с 4 88 | 89 | ## Чистый код 90 | 91 | Чистый код - это код, понятный и податливый к изменениям. Писать такой код - навык, развивающийся на протяжении всей карьеры. 92 | 93 | Практические проекты в рамках этого курса подразумевают не только использование фреймворков и библиотек, но и реализацию логики своими силами. Написание кода с нуля и работа над ним является главным элементом в повышении чистоты своего кода. 94 | 95 | #### Избранные курсы и учебные ресурсы 96 | 97 | - Книги - "Чистый код" Мартина, "Рефакторинг" Фаулера, "Совершенный код" Макконнелла 98 | - Плейлист с моими live coding стримами, где я пишу код, озвучивая свои мысли и комментируя принятые решения - [https://www.youtube.com/playlist?list=PLOVOZrcS3XMZ-QJDHowJQ3abxNHoW8pV3](https://www.youtube.com/playlist?list=PLOVOZrcS3XMZ-QJDHowJQ3abxNHoW8pV3) 99 | - Плейлист с избранными ревью реализаций проектов курса студентами - [https://www.youtube.com/playlist?list=PLOVOZrcS3XMbS4iInU-7p6TbIQW-kATfz](https://www.youtube.com/playlist?list=PLOVOZrcS3XMbS4iInU-7p6TbIQW-kATfz) 100 | - Практика: 101 | - Писать проекты курса, отправлять их мне на ревью. Примеры [готовых проектов с ревью](../finished-projects/_index.md) 102 | - Читать чужой код и оценивать его качество 103 | 104 | ## Что дальше 105 | 106 | - Kotlin - совместимый с Java язык с более "чистым" синтаксисом 107 | - Реактивное программирование 108 | -------------------------------------------------------------------------------- /content/technologies/microservices.md: -------------------------------------------------------------------------------- 1 | +++ 2 | weight = 8 3 | title = 'Контейнеры и микросервисы' 4 | +++ 5 | 6 | # Контейнеры и микросервисы 7 | 8 | Документ посвящён необходимым знаниям, относящимся к контейнерам и микросервисам. 9 | 10 | ## Docker 11 | 12 | Docker - основная технология контейнеризации, ежедневно используемая большинством backend разработчиков. 13 | 14 | Преимущества и удобства: 15 | - Локальная разработка - развернуть копию проекта, запустить одной командой конкретную версию нужной базы данных 16 | - Деплой - единожды собранный образ работает одинаково и локально, и на удалённом сервисе 17 | 18 | Что нужно знать: 19 | - Отличие контейнеров от виртуальных машин - [https://www.youtube.com/watch?v=cjXI-yxqGTI](https://www.youtube.com/watch?v=cjXI-yxqGTI) 20 | - Базовые идеи и понятия - контейнеры, образы, Docker layers, volumes 21 | - Поиск образов нужных приложений на Docker Hub и их локальный запуск 22 | - Работа с Docker CLI - сборка образов, запуск контейнеров, доступ к терминалу контейнера и логам 23 | - Dockerfile - написание сценариев сборки образа 24 | - Docker Compose - запуск стека из нескольких взаимодействующих друг с другом контейнеров 25 | - Docker в контексте Spring Boot - Dockerfile для сборки и запуска приложения 26 | 27 | #### Избранные курсы и учебные ресурсы 28 | 29 | - Курс ["Docker Mastery"](https://www.udemy.com/course/docker-mastery/) (English) 30 | - ["Docker - Полный курс"](https://www.udemy.com/course/docker-ru/) от Богдана Стащука 31 | - Практика: 32 | - Проект #6 ["Облачное хранилище"](../projects/cloud-file-storage.md) - написание Docker Compose стека с базами данных (SQL, MinIO, Redis), деплой 33 | - Проект #7 ["Планировщик задач"](../projects/task-tracker.md) - упаковка Spring Boot приложения в Docker образ, написание своего Docker Compose стека (Spring Boot сервисы, хранилища), сборка образов в CI/CD пайплайне, деплой 34 | 35 | Что дальше (уровень middle и выше): 36 | - Kubernetes 37 | - Облачные технологии 38 | 39 | ## Микросервисы 40 | 41 | Микросервисы - актуальный подход к проектированию систем, основанный на разбиении системы на небольшие независимые сервисы, каждый из которых решает свою задачу. Идеи микросервисного подхода можно почерпнуть из первой главы книги "Building Microservices: Designing Fine-Grained Systems". 42 | 43 | На уровне Junior, как правило, не приходится заниматься проектированием, поэтому для начала важно сконцентрироваться на прикладных аспектах разработки микросервисов: 44 | - Подходы кросс-сервисного взаимодействия - REST, RPC, брокеры очередей 45 | - Docker Compose для локальной разработки стека из микросервисов 46 | - Организация микросервисных проектов, монорепозиторий (один репозиторий для всех сервисов) против отдельного репозитория для каждого сервиса - [Mono vs Multi Repos - Pick Your Poison](https://www.raftt.io/post/development-challenges-of-working-with-monorepos-and-multirepos) 47 | 48 | #### Избранные курсы и учебные ресурсы 49 | 50 | - Введение в тему - "O'Reilly - Software Architecture Patterns" - главы Event-driver Architecture, Microservices Architecture 51 | - "Building Microservices: Designing Fine-Grained Systems" by Sam Newman 52 | - Kafka: 53 | - Документация модуля Spring Kafka - [https://docs.spring.io/spring-kafka/reference/index.html](https://docs.spring.io/spring-kafka/reference/index.html) 54 | - Практика: 55 | - Проект #7 ["Планировщик задач"](../projects/task-tracker.md) - разработка проекта в миросервисном стиле (3 сервиса на Spring Boot и 1 сервис для фронтенда), кросс-сервисное общение через брокер сообщений Kafka 56 | 57 | Что дальше (уровень middle и выше): 58 | - Проектирование микросервисных систем, микросервисные паттерны 59 | - Мониторинг 60 | - Отказоустойчивость и масштабирование 61 | - Нагрузочное тестирование 62 | -------------------------------------------------------------------------------- /content/technologies/tests.md: -------------------------------------------------------------------------------- 1 | +++ 2 | weight = 7 3 | title = 'Тестирование' 4 | +++ 5 | 6 | # Тестирование 7 | 8 | Тесты - кусочки кода, проверяющие корректную функциональность вашего приложения, или его частей. Один такой кусочек называется тест кейсом, процесс их написания называется покрытием кода приложения тестами. 9 | 10 | Тесты бывают различных видов, в зависимости от того, что они тестируют. Рассмотрим основные: 11 | 1. Юнит тесты тестируют небольшие части кода, методы или классы. Типичный пример юнит теста - создать объект, вызвать его метод, проверить результат. 12 | 2. Интеграционные тесты проверяют, что ваше приложение работает правильно в контексте внешних инструментов и сервисов, например, баз данных. Пример интеграционного теста - вызов метода REST API вашего приложения, проверка корректности результата. 13 | 3. E2E (end to end) тесты тестируют приложение в целом, затрагивая максимальное количество его частей. Пример - на веб-интерфейсе приложения есть форма, при заполнении и отправки которой в базе появляется новая запись. E2E тест может сэмулировать действия на веб-интерфейсе, после чего проверить состояние базы данных. 14 | 15 | Кроме видов тестов, важно понимать некоторые смежные с тестированием идеи: 16 | - Mocking - техника эмуляции поведения какого-то метода или внешнего сервиса. Например, если метод возвращает какое-то постоянно меняющееся значение (биржевую котировку, например), то в целях предсказуемого теста можно создать мок объект, возвращающий заранее известную котировку, провести с ней некоторые вычисления и проверить результат на корректность. 17 | - Edge case (пограничный случай) - маловероятный, но тем не менее возможный пример входных данных, который может получить тестируемый код. Пример - длина email при регистрации пользователя. Маловероятная, но максимально возможная длина email адреса - 320 символов, и тестируя пограничные случае мы можем быть уверены, что приложение корректно их обрабатывает. 18 | - Тестовые данные - если код работает с данными, то для его тестирование нужны тестовые данные. Такие данные редко являются данными реальных пользователей (это было бы небезопасно), а их целью зачастую является воссоздание различных пограничных случаев. 19 | - Тестовое окружение - во время тестов редко используется реальные база данных и внешние сервисы, это создавало бы ненужную активность (например, вставку тестовых данных в базу). Поэтому, частая практика - создать отдельное тестовое окружение, которое рождается перед стартом тестов и уничтожается после. Пример - вместо внешней Postgres базы данных, можно запустить in-memory H2 базу и инициализорвать её состояние таблицами с тестовыми данными. Другой популярный инструмент - [Testcontainer](https://www.testcontainers.org/), позволяет запускать необходимые для тест кейсов инструменты (базы данных, очереди) в Docker контейнерах. 20 | 21 | В работе с тестами, как и везде, есть плюсы и минусы. 22 | 23 | Плюсы: 24 | - Написание тестов помогает формализовать работу приложения 25 | - Для написания качественных тестов покрываемый ими код должен быть спроектирован на наобходимом уровне. Необходимость тестов может стать поводом для рефакторинга, улучшающего качество кода 26 | - Тесты являются ещё одной формой документации, хочешь понять в деталях что делает класс - прочитай юнит тесты к нему 27 | - Тесты помогают поддерживать работоспособность кода при его рефакторинге и добавления нового функционала. В случае некорректных изменений часть тестов сломается 28 | - Тесты помогают поддерживать здоровый уровень качества кода приложения, особенно в больших командах, где люди приходят и уходят 29 | 30 | Минусы: 31 | - Дополнительная работа. Написание тестов и их поддержка требуют времени 32 | - Плохо написанные тесты создают больше проблем, чем приносят пользы 33 | 34 | Практически любой устоявшийся проект содержит тесты, и разработчикам необходимо уметь с ними работать. Уровень проникровения тестов в проект, их глубина и количество зависят от конкретных условий и варьируются от поверхностного тестирования до полного [TDD](https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D1%87%D0%B5%D1%80%D0%B5%D0%B7_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5). 35 | 36 | ## Unit тестирование 37 | 38 | Для написания юнит тестов студент должен уметь писать качественные тест кейсы и пользоваться одним из популярных фреймворков для юнит тестирования (самый популярный - JUnit 5). Важнее всего понять идеи качественных кейсов, применять эти идеи к конкретной библиотеке - дело практики. 39 | 40 | Основные рекоммендации для написания качественных юнит тестов: 41 | - Проверяйте пограничные случаи 42 | - Тестируйте не только успешные сценарии работы кода, но и неудачные - покрывайте тестами случаи, когда метод должен вернуть ошибку или кинуть exception 43 | - Давайте тест кейсам короткие и понятные имена, описывающие суть их проверки. Если имя слишком длинное или непонятное, возможно, тест слишком общий или сложный 44 | 45 | Что нужно знать о [JUnit 5](https://junit.org/junit5/): 46 | - Как добавить JUnit 5 в Maven проект и написать первый тест - [https://www.baeldung.com/junit-5](https://www.baeldung.com/junit-5) 47 | - [Аннотации JUnit](https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations) 48 | - [Assertions](https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions) 49 | - [Assumptions](https://junit.org/junit5/docs/current/user-guide/#writing-tests-assumptions) 50 | 51 | #### Избранные курсы и учебные ресурсы 52 | 53 | - Курс ["Продвинутая Java"](https://www.udemy.com/course/javarussia/) Наиля Алишева, глава "JUnit" 54 | - [Плейлист](https://www.youtube.com/playlist?list=PLnh8EajVFTl5AqvBosxUefReW4nC35P0x) по JUnit5 от dmdev. Тоже самое в виде курса - [https://www.udemy.com/course/junit5-dmdev/](https://www.udemy.com/course/junit5-dmdev/) 55 | 56 | ## Интеграционные тесты 57 | 58 | Интеграционные тесты проверяют взаимодействия между частями системы (например - между слоем сервисов и слоем хранения данных) и взаимодействия с внешними сервисами. Для написания таких тестов нужно знать и уметь пользоваться следующими идеями и инструментами: 59 | - Фреймворк для написания тестов - [JUnit 5](https://junit.org/junit5/) 60 | - Моки, как инструмент симуляции поведения классов или внешних сервисов. Библиотека для создания моков - [Mockito](https://site.mockito.org/) 61 | - Тестовые данные, независимые от основных данных приложения. Например - in-memory H2 DB, создаваемая для прогона тестов, и уничтожаемая после 62 | - [Testcontainer](https://www.testcontainers.org/) - библиотека для запуска экземпляров нужных для тестов инструментов внутри Docker контейнеров 63 | 64 | Примеры интеграционного тестирования в контексте бэкенд приложений: 65 | - Взимодействия класса-сервиса и БД, куда сервис вставляет данные. Можно проверить реакцию БД на вставку некорректных данных и реакцию сервиса на возникшую при этом ошибку 66 | - Взаимодействия класса-сервиса и внешнего REST API. Проверки парсинга ответов, реакции на HTTP ошибки (коды 4xx, 5xx) и сетевые ошибки (таймаут подключения) 67 | - Взаимодействия REST контроллера и сервисов. Проверка того, что REST API вернёт ожидаемые ответы на различные наборы параметров в запросах. Реакции на ошибки - неправильно сформированные запросы, отсутствие необходимой авторизации 68 | 69 | #### Избранные курсы и учебные ресурсы 70 | 71 | - [Доклад](https://www.youtube.com/watch?v=PEVVvZOt7bY) по TestContainers с конференции Joker 72 | -------------------------------------------------------------------------------- /hugo.toml: -------------------------------------------------------------------------------- 1 | baseURL = 'https://zhukovsd.github.io/java-backend-learning-course/' 2 | languageCode = 'ru' 3 | title = 'Java Роадмап Сергея Жукова' 4 | theme = 'hugo-book' 5 | enableGitInfo = true 6 | 7 | [params] 8 | BookTheme = 'auto' 9 | BookSection = '*' 10 | BookPortableLinks = true 11 | BookRepo = 'https://github.com/zhukovsd/java-backend-learning-course' 12 | BookCommitPath = 'commit' 13 | BookEditPath = 'edit/main' 14 | 15 | [menu] 16 | [[menu.after]] 17 | name = "Telegram канал" 18 | url = "https://t.me/zhukovsd_it_mentor" 19 | weight = 10 20 | 21 | [[menu.after]] 22 | name = "Telegram чат" 23 | url = "https://t.me/zhukovsd_it_chat" 24 | weight = 20 25 | 26 | [[menu.after]] 27 | name = "YouTube" 28 | url = "https://www.youtube.com/@zhukovsd_it_mentor" 29 | weight = 30 30 | 31 | [services] 32 | [services.googleAnalytics] 33 | id = "G-VXVWNDJ3VL" 34 | -------------------------------------------------------------------------------- /resources/_gen/assets/book.scss_e129fe35b8d0a70789c8a08429469073.content: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";:root{--gray-100:#f8f9fa;--gray-200:#e9ecef;--gray-500:#adb5bd;--color-link:#0055bb;--color-visited-link:#8440f1;--body-background:white;--body-font-color:black;--icon-filter:none;--hint-color-info:#6bf;--hint-color-warning:#fd6;--hint-color-danger:#f66}@media(prefers-color-scheme:dark){:root{--gray-100:rgba(255, 255, 255, 0.1);--gray-200:rgba(255, 255, 255, 0.2);--gray-500:rgba(255, 255, 255, 0.5);--color-link:#84b2ff;--color-visited-link:#b88dff;--body-background:#343a40;--body-font-color:#e9ecef;--icon-filter:brightness(0) invert(1);--hint-color-info:#6bf;--hint-color-warning:#fd6;--hint-color-danger:#f66}}/*!normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.flex{display:flex}.flex-auto{flex:auto}.flex-even{flex:1 1}.flex-wrap{flex-wrap:wrap}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.align-center{align-items:center}.mx-auto{margin:0 auto}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.hidden{display:none}input.toggle{height:0;width:0;overflow:hidden;opacity:0;position:absolute}.clearfix::after{content:"";display:table;clear:both}html{font-size:16px;scroll-behavior:smooth;touch-action:manipulation}body{min-width:20rem;color:var(--body-font-color);background:var(--body-background);letter-spacing:.33px;font-weight:400;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box}body *{box-sizing:inherit}h1,h2,h3,h4,h5{font-weight:400}a{text-decoration:none;color:var(--color-link)}img{vertical-align:baseline}:focus{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}aside nav ul{padding:0;margin:0;list-style:none}aside nav ul li{margin:1em 0;position:relative}aside nav ul a{display:block}aside nav ul a:hover{opacity:.5}aside nav ul ul{padding-inline-start:1rem}ul.pagination{display:flex;justify-content:center;list-style-type:none;padding-inline-start:0}ul.pagination .page-item a{padding:1rem}.container{max-width:80rem;margin:0 auto}.book-icon{filter:var(--icon-filter)}.book-brand{margin-top:0;margin-bottom:1rem}.book-brand img{height:1.5em;width:1.5em;margin-inline-end:.5rem}.book-menu{flex:0 0 16rem;font-size:.875rem}.book-menu .book-menu-content{width:16rem;padding:1rem;background:var(--body-background);position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-menu a,.book-menu label{color:inherit;cursor:pointer;word-wrap:break-word}.book-menu a.active{color:var(--color-link)}.book-menu input.toggle+label+ul{display:none}.book-menu input.toggle:checked+label+ul{display:block}.book-menu input.toggle+label::after{content:"▸"}.book-menu input.toggle:checked+label::after{content:"▾"}body[dir=rtl] .book-menu input.toggle+label::after{content:"◂"}body[dir=rtl] .book-menu input.toggle:checked+label::after{content:"▾"}.book-section-flat{margin:2rem 0}.book-section-flat>a,.book-section-flat>span,.book-section-flat>label{font-weight:bolder}.book-section-flat>ul{padding-inline-start:0}.book-page{min-width:20rem;flex-grow:1;padding:1rem}.book-post{margin-bottom:3rem}.book-header{display:none;margin-bottom:1rem}.book-header label{line-height:0}.book-header img.book-icon{height:1.5em;width:1.5em}.book-search{position:relative;margin:1rem 0;border-bottom:1px solid transparent}.book-search input{width:100%;padding:.5rem;border:0;border-radius:.25rem;background:var(--gray-100);color:var(--body-font-color)}.book-search input:required+.book-search-spinner{display:block}.book-search .book-search-spinner{position:absolute;top:0;margin:.5rem;margin-inline-start:calc(100% - 1.5rem);width:1rem;height:1rem;border:1px solid transparent;border-top-color:var(--body-font-color);border-radius:50%;animation:spin 1s ease infinite}@keyframes spin{100%{transform:rotate(360deg)}}.book-search small{opacity:.5}.book-toc{flex:0 0 16rem;font-size:.75rem}.book-toc .book-toc-content{width:16rem;padding:1rem;position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-toc img{height:1em;width:1em}.book-toc nav>ul>li:first-child{margin-top:0}.book-footer{padding-top:1rem;font-size:.875rem}.book-footer img{height:1em;width:1em;margin-inline-end:.5rem}.book-comments{margin-top:1rem}.book-languages{margin-block-end:2rem}.book-languages .book-icon{height:1em;width:1em;margin-inline-end:.5em}.book-languages ul{padding-inline-start:1.5em}.book-menu-content,.book-toc-content,.book-page,.book-header aside,.markdown{transition:.2s ease-in-out;transition-property:transform,margin,opacity,visibility;will-change:transform,margin,opacity}@media screen and (max-width:56rem){#menu-control,#toc-control{display:inline}.book-menu{visibility:hidden;margin-inline-start:-16rem;font-size:16px;z-index:1}.book-toc{display:none}.book-header{display:block}#menu-control:focus~main label[for=menu-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#menu-control:checked~main .book-menu{visibility:initial}#menu-control:checked~main .book-menu .book-menu-content{transform:translateX(16rem);box-shadow:0 0 .5rem rgba(0,0,0,.1)}#menu-control:checked~main .book-page{opacity:.25}#menu-control:checked~main .book-menu-overlay{display:block;position:absolute;top:0;bottom:0;left:0;right:0}#toc-control:focus~main label[for=toc-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#toc-control:checked~main .book-header aside{display:block}body[dir=rtl] #menu-control:checked~main .book-menu .book-menu-content{transform:translateX(-16rem)}}@media screen and (min-width:80rem){.book-page,.book-menu .book-menu-content,.book-toc .book-toc-content{padding:2rem 1rem}}@font-face{font-family:roboto;font-style:normal;font-weight:400;font-display:swap;src:local(""),url(fonts/roboto-v27-latin-regular.woff2)format("woff2"),url(fonts/roboto-v27-latin-regular.woff)format("woff")}@font-face{font-family:roboto;font-style:normal;font-weight:700;font-display:swap;src:local(""),url(fonts/roboto-v27-latin-700.woff2)format("woff2"),url(fonts/roboto-v27-latin-700.woff)format("woff")}@font-face{font-family:roboto mono;font-style:normal;font-weight:400;font-display:swap;src:local(""),url(fonts/roboto-mono-v13-latin-regular.woff2)format("woff2"),url(fonts/roboto-mono-v13-latin-regular.woff)format("woff")}body{font-family:roboto,sans-serif}code{font-family:roboto mono,monospace}@media print{.book-menu,.book-footer,.book-toc{display:none}.book-header,.book-header aside{display:block}main{display:block!important}}.markdown{line-height:1.6}.markdown>:first-child{margin-top:0}.markdown h1,.markdown h2,.markdown h3,.markdown h4,.markdown h5,.markdown h6{font-weight:400;line-height:1;margin-top:1.5em;margin-bottom:1rem}.markdown h1 a.anchor,.markdown h2 a.anchor,.markdown h3 a.anchor,.markdown h4 a.anchor,.markdown h5 a.anchor,.markdown h6 a.anchor{opacity:0;font-size:.75em;vertical-align:middle;text-decoration:none}.markdown h1:hover a.anchor,.markdown h1 a.anchor:focus,.markdown h2:hover a.anchor,.markdown h2 a.anchor:focus,.markdown h3:hover a.anchor,.markdown h3 a.anchor:focus,.markdown h4:hover a.anchor,.markdown h4 a.anchor:focus,.markdown h5:hover a.anchor,.markdown h5 a.anchor:focus,.markdown h6:hover a.anchor,.markdown h6 a.anchor:focus{opacity:initial}.markdown h4,.markdown h5,.markdown h6{font-weight:bolder}.markdown h5{font-size:.875em}.markdown h6{font-size:.75em}.markdown b,.markdown optgroup,.markdown strong{font-weight:bolder}.markdown a{text-decoration:none}.markdown a:hover{text-decoration:underline}.markdown a:visited{color:var(--color-visited-link)}.markdown img{max-width:100%;height:auto}.markdown code{direction:ltr;unicode-bidi:embed;padding:0 .25rem;background:var(--gray-200);border-radius:.25rem;font-size:.875em}.markdown pre{direction:ltr;unicode-bidi:embed;padding:1rem;background:var(--gray-100);border-radius:.25rem;overflow-x:auto}.markdown pre code{padding:0;background:0 0}.markdown p{word-wrap:break-word}.markdown blockquote{margin:1rem 0;padding:.5rem 1rem .5rem .75rem;border-inline-start:.25rem solid var(--gray-200);border-radius:.25rem}.markdown blockquote :first-child{margin-top:0}.markdown blockquote :last-child{margin-bottom:0}.markdown table{overflow:auto;display:block;border-spacing:0;border-collapse:collapse;margin-top:1rem;margin-bottom:1rem}.markdown table tr th,.markdown table tr td{padding:.5rem 1rem;border:1px solid var(--gray-200)}.markdown table tr:nth-child(2n){background:var(--gray-100)}.markdown hr{height:1px;border:none;background:var(--gray-200)}.markdown ul,.markdown ol{padding-inline-start:2rem;word-wrap:break-word}.markdown dl dt{font-weight:bolder;margin-top:1rem}.markdown dl dd{margin-inline-start:0;margin-bottom:1rem}.markdown .highlight{direction:ltr;unicode-bidi:embed}.markdown .highlight table tr td:nth-child(1) pre{margin:0;padding-inline-end:0}.markdown .highlight table tr td:nth-child(2) pre{margin:0;padding-inline-start:0}.markdown details{padding:1rem;border:1px solid var(--gray-200);border-radius:.25rem}.markdown details summary{line-height:1;padding:1rem;margin:-1rem;cursor:pointer}.markdown details[open] summary{margin-bottom:0}.markdown figure{margin:1rem 0}.markdown figure figcaption p{margin-top:0}.markdown-inner>:first-child{margin-top:0}.markdown-inner>:last-child{margin-bottom:0}.markdown .book-expand{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden}.markdown .book-expand .book-expand-head{background:var(--gray-100);padding:.5rem 1rem;cursor:pointer}.markdown .book-expand .book-expand-content{display:none;padding:1rem}.markdown .book-expand input[type=checkbox]:checked+.book-expand-content{display:block}.markdown .book-tabs{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden;display:flex;flex-wrap:wrap}.markdown .book-tabs label{display:inline-block;padding:.5rem 1rem;border-bottom:1px transparent;cursor:pointer}.markdown .book-tabs .book-tabs-content{order:999;width:100%;border-top:1px solid var(--gray-100);padding:1rem;display:none}.markdown .book-tabs input[type=radio]:checked+label{border-bottom:1px solid var(--color-link)}.markdown .book-tabs input[type=radio]:checked+label+.book-tabs-content{display:block}.markdown .book-tabs input[type=radio]:focus+label{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}.markdown .book-columns{margin-left:-1rem;margin-right:-1rem}.markdown .book-columns>div{margin:1rem 0;min-width:10rem;padding:0 1rem}.markdown a.book-btn{display:inline-block;font-size:.875rem;color:var(--color-link);line-height:2rem;padding:0 1rem;border:1px solid var(--color-link);border-radius:.25rem;cursor:pointer}.markdown a.book-btn:hover{text-decoration:none}.markdown .book-hint.info{border-color:#6bf;background-color:rgba(102,187,255,.1)}.markdown .book-hint.warning{border-color:#fd6;background-color:rgba(255,221,102,.1)}.markdown .book-hint.danger{border-color:#f66;background-color:rgba(255,102,102,.1)} -------------------------------------------------------------------------------- /resources/_gen/assets/book.scss_e129fe35b8d0a70789c8a08429469073.json: -------------------------------------------------------------------------------- 1 | {"Target":"book.min.4964903a822a7acb10dac6d1ab524833c97fb5f99b141976bcb8a47d539be9c0.css","MediaType":"text/css","Data":{"Integrity":"sha256-SWSQOoIqessQ2sbRq1JIM8l/tfmbFBl2vLikfVOb6cA="}} -------------------------------------------------------------------------------- /templates/cloud-file-storage.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Облачное хранилище файлов' 3 | weight = 6 4 | bookTOC = false 5 | +++ 6 | 7 | # Облачное хранилище файлов 8 | 9 | [ТЗ проекта](../projects/cloud-file-storage.md) 10 | 11 | {{ projects | project_count }} реализаций на {{ projects | unique_languages }}. {{ projects | review_count }} ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | {% for p in projects -%} 18 | | {{ p | repo }} | {{ p | author }} | {{ p | language }} | {{ p | review }} | {{ p | review_author }} | 19 | {% endfor %} 20 | -------------------------------------------------------------------------------- /templates/currency-exchange.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Обмен валют' 3 | weight = 3 4 | bookTOC = false 5 | +++ 6 | 7 | # Обмен валют 8 | 9 | [ТЗ проекта](../projects/currency-exchange.md) 10 | 11 | {{ projects | project_count }} реализаций на {{ projects | unique_languages }}. {{ projects | review_count }} ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | {% for p in projects -%} 18 | | {{ p | repo }} | {{ p | author }} | {{ p | language }} | {{ p | review }} | {{ p | review_author }} | 19 | {% endfor %} 20 | -------------------------------------------------------------------------------- /templates/hangman.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Виселица' 3 | weight = 1 4 | bookTOC = false 5 | +++ 6 | 7 | # Виселица 8 | 9 | [ТЗ проекта](../projects/hangman.md) 10 | 11 | {{ projects | project_count }} реализаций на {{ projects | unique_languages }}. {{ projects | review_count }} ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | {% for p in projects -%} 18 | | {{ p | repo }} | {{ p | author }} | {{ p | language }} | {{ p | review }} | {{ p | review_author }} | 19 | {% endfor %} 20 | -------------------------------------------------------------------------------- /templates/simulation.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Симуляция' 3 | weight = 2 4 | bookTOC = false 5 | +++ 6 | 7 | # Симуляция 8 | 9 | [ТЗ проекта](../projects/simulation.md) 10 | 11 | {{ projects | project_count }} реализаций на {{ projects | unique_languages }}. {{ projects | review_count }} ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | {% for p in projects -%} 18 | | {{ p | repo }} | {{ p | author }} | {{ p | language }} | {{ p | review }} | {{ p | review_author }} | 19 | {% endfor %} 20 | -------------------------------------------------------------------------------- /templates/task-tracker.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Планировщик задач' 3 | weight = 7 4 | bookTOC = false 5 | +++ 6 | 7 | # Планировщик задач 8 | 9 | [ТЗ проекта](../projects/task-tracker.md) 10 | 11 | {{ projects | project_count }} реализаций на {{ projects | unique_languages }}. {{ projects | review_count }} ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | {% for p in projects -%} 18 | | {{ p | repo }} | {{ p | author }} | {{ p | language }} | {{ p | review }} | {{ p | review_author }} | 19 | {% endfor %} 20 | -------------------------------------------------------------------------------- /templates/tennis-scoreboard.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Табло теннисного матча' 3 | weight = 4 4 | bookTOC = false 5 | +++ 6 | 7 | # Табло теннисного матча 8 | 9 | [ТЗ проекта](../projects/tennis-scoreboard.md) 10 | 11 | {{ projects | project_count }} реализаций на {{ projects | unique_languages }}. {{ projects | review_count }} ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | {% for p in projects -%} 18 | | {{ p | repo }} | {{ p | author }} | {{ p | language }} | {{ p | review }} | {{ p | review_author }} | 19 | {% endfor %} 20 | -------------------------------------------------------------------------------- /templates/weather-viewer.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Погода' 3 | weight = 5 4 | bookTOC = false 5 | +++ 6 | 7 | # Погода 8 | 9 | [ТЗ проекта](../projects/weather-viewer.md) 10 | 11 | {{ projects | project_count }} реализаций на {{ projects | unique_languages }}. {{ projects | review_count }} ревью. 12 | 13 | Присылайте ваши реализации в чат сообщества - [@zhukovsd_it_chat](https://t.me/zhukovsd_it_chat). 14 | 15 | | Репозиторий | Автор | Язык | Ревью | Автор ревью | 16 | |-------------|-------|------|-------|-------------| 17 | {% for p in projects -%} 18 | | {{ p | repo }} | {{ p | author }} | {{ p | language }} | {{ p | review }} | {{ p | review_author }} | 19 | {% endfor %} 20 | --------------------------------------------------------------------------------