## Выпускной проект [стажировки TopJava](https://javaops.ru/view/topjava) Design and implement a REST API using Spring-Boot/Spring Data JPA **without frontend**. The task is: Build a voting system for deciding where to have lunch. * 2 types of users: admin and regular users * Admin can input a restaurant and it's lunch menu of the day (2-5 items usually, just a dish name and price) * Menu changes each day (admins do the updates) * Users can vote for a restaurant they want to have lunch at today * Only one vote counted per user * If user votes again the same day: - If it is before 11:00 we assume that he changed his mind. - If it is after 11:00 then it is too late, vote can't be changed Each restaurant provides a new menu each day. As a result, provide a link to github repository. It should contain the code, README.md with API documentation and couple curl commands to test it (**better - link to Swagger**). ----------------------------- P.S.: Make sure everything works with latest version that is on github :) P.P.S.: Assume that your API will be used by a frontend developer to build frontend on top of that. ----------------------------- ## ![error](https://cloud.githubusercontent.com/assets/13649199/13672935/ef09ec1e-e6e7-11e5-9f79-d1641c05cbe6.png) Рекомендации **Пишем выпускной проект как тестовое задание на работу** - **Не изобретай велосипедов!** Грубая ошибка - пытаться сделать стандартные вещи по-своему, чаще всего криво. На проекте все должно быть единообразно! Ваш проект TopJava - сделай все МАКСИМАЛЬНО в этом стиле. Если тебе кажется, что есть лучшее решение, чем в TopJava - пишите мне в личку, я всегда открыт для улучшений. - **Рекомендую писать проект на востребованном на рынке стеке**: Spring Boot + Spring Data JPA (работа с БД) + Swagger/OpenAPI 3.0 (REST документация). - **Оптимально подойдет код миграции TopJava на Spring Boot в конце стажировки или [курса BootJava](https://javaops.ru/view/bootjava)** *Представьте себе, что ПМ (лид, архитектор) дал вам ТЗ и некоторое время недоступен. У вас, конечно, есть много мыслей, для чего нужно приложение, как исправить ТЗ, дополнить его и сделать правильно. НО НЕ НАДО ИХ РЕАЛИЗОВЫВАТЬ В КОДЕ. Нужно сделать все строго по ТЗ, максимально просто, удобно для доработок и для использования со стороны клиента.* > Совершенство достигнуто не тогда, когда нечего добавить, а тогда, когда нечего отнять _Антуан де Сент-Экзюпери_ ### 1: ТЗ (Тех.задание) - 1.1: Читай ТЗ ОЧЕНЬ внимательно, НЕ надо ничего своего туда домысливать и творчески изменять - 1.2: Учитывай, что пользователей может быть ОООЧЕНЬ много, а админов - МАЛО - 1.3: Сначала сделай основные сценарии по ТЗ. Все остальное (если очень хочется, 3 раза подумай) - потом. ### 2. API - 2.1: API продумывай с точки зрения не программиста и объектов, а с точки зрения того, кто им будет пользоваться (клиента, UI) - 2.1: Тщательно считайте количество запросов в вашем API для отображения нужной информации - 2.3: Из потребностей приложения (клиента) реализуй только очевидные сценарии. Необходимо и достаточно: ВСЕ НЕОБХОДИМОЕ для клиента и НИЧЕГО ЛИШНЕГО. Процесс творческий, приходит с опытом. - 2.4: Делаем REST API в соответствии с концепцией REST (url в общем имеют вид`{ресурс}/{id_ресурсa}[/{подресурс}/{id_подресурсa}][параметры]`, см. ниже ссылки про REST и иерархию). Имена ресурсов во множественном числе! Самая распространенная и грубая ошибка - не придерживаться этих простых правил. - **[15 тривиальных фактов о правильной работе с протоколом HTTP](https://habrahabr.ru/company/yandex/blog/265569/)** - **[10 Best Practices for Better RESTful API](https://medium.com/@mwaysolutions/10-best-practices-for-better-restful-api-cbe81b06f291)** - **[REST resource hierarchy](https://stackoverflow.com/questions/20951419/what-are-best-practices-for-rest-nested-resources)** - [Лучшие практики разработки REST API: правила 1-7,15-17](https://tproger.ru/translations/luchshie-praktiki-razrabotki-rest-api-20-sovetov/) - 2.5: Разделение на роли я предпочитаю на уровне URL. Сразу и однозначно видно, какой API у админа, какое у пользователя (API админа начинается, например, с */admin/...*). - 2.6: На управление (CRUD) ресторанами и едой должны быть ОТДЕЛЬНЫЕ контроллеры. Не надо все, что может админ, сваливать в одну кучу! Смотрите на результат операции - помещаете в этот контроллер! - 2.7: Проверьте в Swagger, что в POST и PUT нет ничего лишнего, а в GET есть все необходимые данные. Например, при запросе голоса должен в ответе отображаться `id` ресторана, а не весь объект, при создании-редактировании ресторана в примерах swagger не должно быть меню и еды. - 2.8: `Profile` означает, что данные принадлежат профилю пользователя. Все остальное называйте по-другому. - 2.9: Отсутствие данных часто бывает "бизнес кейсом", те НЕ ошибкой в запросе или приложении. Исключение - это ошибка, например неверный `id`. Запрос на данные, которые могут быть, могут нет, не должен приводить к исключениям. Посмотрите в сторону `ResponseEntity.of()` - 2.10: По REST URL должно быть однозначно понятно, какие будут параметры на входе и что ждать на выходе. Без сюрпризов! ### 3: Код: - 3.1: Строго соблюдайте соглашения Java по именованию: пакеты ТОЛЬКО маленькими буквами, методы начинаются с маленькой буквы, классы с большой. Незнания Java Core - тестовое задание сразу в корзину. - 3.2 В проекте (и тестовом задании на работу), в отличие от нашего учебного topjava, оставляйте только необходимый для работы по ТЗ приложения код, ничего лишнего - 3.2.1: НЕ надо делать разные профили базы и работы с ней - 3.2.2: НЕ надо делать абстрактных контроллеров на всякий случай - 3.2.3: НЕ надо делать сервисов, если там нет ничего, кроме делегирования - 3.2.4: НЕ нужны локализация, UI, типы ошибок, Json View - 3.3: Название пакетов, имен классов для `model/to/web` стандартные (например `model/domain`). НЕ надо придумывать своих собственных правил - 3.4: Проверьте, не торчат ли из кода учебные уши TopJava, типа `ProfileRestController.testUTF()`, `AbstractServiceTest.printResult()` или закомментированные `JdbcTemplate`. Назначение выпускного совсем другое - 3.5: Вместо `return ResponseEntity.ok(object)` в контроллерах пишите `return object`. Проще! ### 4: Модель - 4.1: В БД обычно хранятся все введенные пользователем и админом данные c возможность их редактирования. Это означает, что мы не удаляем прошлые меню, а храним их в базе, как и историю голосования. Есть базовые вещи, которые закладываются в архитектуру приложения, и есть неочевидные доработки к ТЗ, делать не надо. - 4.2: Не делайте в модели объектов, которые не будут использоваться в коде (например, не надо двунаправленных связей, если достаточно однонаправленных) - 4.3: еще раз про [hashCode/equals в Entity](https://stackoverflow.com/questions/5031614/the-jpa-hashcode-equals-dilemma): не делайте в модели сравнение по полям! - 4.4: ORM работает с объектами. [Иногда, для упрощения логики, fk_id как поля допустимы](https://stackoverflow.com/questions/6311776/hibernate-foreign-keys-instead-of-entities) ### 5: Архитектура / pom - 5.1: Можно: - или подключить DATA-REST (см.курс [Spring Boot 2.x + HATEOAS](https://javaops.ru/view/bootjava)). Контроллеры генерируются автоматически по репозиториям, требуется настройка ресурсов в кастомных контроллерах - **РЕКОМЕНДУЕТСЯ** делать на основе миграции TopJava / кода [курса BootJava](https://javaops.ru/view/bootjava) Нельзя смешивать эти подходы вместе! Я рекомендую 2-й вариант, без data-rest. Обязательно посмотрите в Swagger, какие контроллеры получились в результате. - 5.2: Не размещайте бизнес-логику приложения и преобразования в TO в слое доступа к DB - 5.3: Не смешивайте TO и Entity вместе. Они должны быть независимыми друг от друга. На TopJava мы смотрели разные варианты [c использованием TO и без](https://stackoverflow.com/a/21569720/548473). Делаем максимально просто. - 5.4: [Use for money in java app](http://stackoverflow.com/a/43051227/548473) - 5.5 Не надо явно указывать версии зависимостей в `pom`, если они наследуются от `spring-boot-starter-parent` ### 6: Доступ к БД - 6.1: Используйте Spring Data JPA (без лишней делегации). Методы Repository можно вызывать напрямую из сервиса или из контроллера. - 6.2: В DATA-JPA 2.x используются `Optional`. Попробуйте работать с ними, это безопасный способ работать с null-значениями (используйте `orElseThrow`) - 6.3: Не делайте при обновлении записи ради экономии пары строчек кода так: ``` if(updateCondition) repository.delete(entity) } repository.save(entity) ``` Обновление записи базы должно быть через `UPDATE`. ### 7: База Данных - 7.1: Берите без установки (H2 или HSQLDB). Одну и **в памяти**! Ваше приложение должно сразу запуститься, без всяких настроек и переменных окружения - 7.2: Тщательно считайте количество обращений в базу на каждый запрос. Особенно при запросах от юзеров, которых очень много! Также на сложность запросов от них, чтобы не положить базу - 7.3: Сделайте [индексы к таблицам](https://ru.wikipedia.org/wiki/Индекс_(базы_данных)). Попробуйте обеспечить UNIQUE (один голос пользователя в день, один уникальный пункт меню в день). Следите за **порядком полей в индексе**, от этого зависит индексирование запросов. - 7.4: При популировании добавь записи за сегодняшний день - `now()`, чтобы всегда были актуальные исходные данные - 7.5: Поля базы case insensitive, не пишите camelStyle (для которых нужны кавычки) - 7.6: Таблицы обычно именуются в единственном числе. Исключение - `users`, `orders` и другие зарезервированные слова - 7.7: `date`/`timestamp` - зарезервированное слово, лучше избегать их при именовании полей ### 8: Security - 8.1: Проверьте, станет ли код проще с `@AuthenticationPrincipal` (урок 11, доступ к AuthorizedUser). - 8.2: Я предпочитаю четкое разделение ролей на основе URL. Для админа URL содержит `/admin` - 8.3: Еще раз - призываю не менять код TopJava ### 9: Кэширование - 9.1: Кэширование желательно для частых и редко меняющихся запросов от пользователей. Тщательно продумайте, что надо кэшировать (самые частые запросы), а что нет (большие или редко запрашиваемые данные) - 9.2: Проверьте соответствие ключей к кэшу (параметры кэшируемого метода) с конфигурацией (например в singleNonExpiryCache, heap=1 в кэше может содержаться только ОДНО значение). ### 10: Валидация - 10.1: Одних аннотаций валидации на полях недостаточно. Должны быть `@Valid/@Validation` в контроллере - 10.2: Прячте `id` при `create/update` в примерах Swagger. Если их передали - проверяйте на соответствие (в TopJava это `ValidationUtil.checkNew()/assureIdConsistent()`) ### 11: Дополнительно - 11.1: По возможности сделать JUnit-тесты. Можно не делать 100% покрытие, только основные сценарии - 11.2: Уделяйте внимание обработке ошибок. ### 12: `readme.md`: - 12.1: Поместите вначале `readme` ТЗ или **ссылку на него** - будет понятно о чем твой проект - 12.2: Если задание на English, описание пиши также на English (то же самое относится к языку резюме: вакансия на English предполагает резюме на English) - 12.3: Требуемые примеры `curl` не прячьте, а пишите здесь! Оптимально сделать **ссылку на `Swagger UI` с креденшелами для выполнения запросов** - 12.4: Проверяют люди с опытом в Java: не надо писать инструкций, как устанавливать Java и Maven ### 13: Git - 13.1: Должна быть история ваших комитов с внятными комментариями. Это смотрят. > Эйчар обращает внимание на дату создания аккаунта и то, как в него заливались коммиты (элементы проекта), постепенно или в один день, насколько технологии в аккаунте коррелируют с технологиями в резюме - 13.2: Не комить служенбые файлы: логи, DB, настройки IDEA и пр., это сразу - уровень Junior. - 13.3: Все служебные файлы должны быть в `.gitignore` ## Проверка - Попробуй подергать свое API по всем типичным сценариям ТЗ! - Удобно использовать? Можно сделать проще? Например, чтобы проголосовать за ресторан залогиненному юзеру, достаточно `restorauntId`. - Сколько раз пришлось его вызвать API для типичного сценария (например посмотреть рестораны с едой на сегодня)? - Сколько запросов к базе было сделано? Можно ли сократить (например с `FETCH/Graph` или через кэширование)? - **Еще раз - проверь все запросы в Sawgger, смотри на формат запросов и данные в ответе. Все должно работать, есть все данные и нет ничего лишнего** - 13.2: API ДОЛЖЕН соответствовать принципам REST (см. ссылки выше) - 13.3: ОБЯЗАТЕЛЬНО: запустите `mvn test` - ошибок быть не должно - 13.4: ОБЯЗАТЕЛЬНО: запустите приложение без всяких предварительных настроек (базы, переменных окружения, ..), лучше на другом компьютере. Приложение должно запускаться и работать! ## Оценка ТЗ очень скромное, не дает понимания, что хочет получить заказчик, именно поэтому моих комментариев к этому ТЗ больше на 2 порядка. Это реальное тестовое задание на работу. При оценке учитывается отсутствие грубых ляпов, простота и красота решения + его расширяемость, если понадобится дорабатывать приложение. Если практический опыт небольшой, еще раз очень рекомендую держаться как можно ближе к нашему коду на Spring Boot. Тестовое задание дается для того, чтобы оценить вас, как программиста. Нужны ли вы компании или нет. Это умение спроектировать грамотную модель и API, придерживаясь принципов REST и без своих велосипедов, и ваш чистый красивый код.