Предметная область - блюда и ингредиенты. Есть таблица с данными о блюде, таблица с данными об ингредиенте, таблица связи + словарь (единица измерения ингредиента).
БД: Postgres 16
Скрипты лежат в ./sql
Бд с небольшим количеством тестовых данных доступна
в докере.
docker-compose -f docker-compose-db.yaml up
Перед запуском докер-компоса укажите валидные
пути в вашей системе.
Ветка java-ee:
git checkout java-ee
JakartaEE 10
GlassFish Server 7
Lombok
Конфиги преимущественно аннотациями.
Data layer
Все энтити наследуются либо от BaseLink либо
от BaseEntity (MappedSuperclass'ы).
На репозиториях что-то пошло нет так, они без
общего родителя, хотя половина методов - копипаст.
Soft delete.
Business layer
Сервисы - 2 шт. Сервис ингредиента работает
только с ингредиентом, сервис блюда работает со
всеми сущностями.
Пара слов про логику сохранения
нового блюда: в блюдо сетятся параметры, после чего
оно сразу флашится, так как далее для сохранения в
таблице связи нужен айдишник блюда (подразумевается,
что ингредиенты берутся из заранее определенного списка,
их айдишники существуют). В конце в блюдо сетится
список ингредиентов для того, чтобы объект в памяти
пребывал в согласованном с бд состоянии.
View layer
Сервлеты, переводящие на jsp-страницы.
Есть рутовая страница, страница просмотра всех
ингредиентов, страница просмотра всех
блюд, страница создания блюда, страница создания
ингредиента, страница расширенного просмотра
данных по конкретному блюду.
Ветка java-se:
git checkout java-se
(Далее develop: git checkout develop
)
Java 11 (впрочем, можно билдить и с версиями повыше, 17 н-р)
Spring Boot 2.6
Lombok
Thymeleaf
Приложуха (вместе с бд) доступна в докере:
docker-compose -f docker-compose-all.yaml up
Имадж сбилдится автоматически по докерфайлу.
Перед запуском докер-компоса укажите валидные
пути для бд в вашей системе.
Data layer
Логика аналогична предыдущей работе: энтити наследуются
либо от BaseLink, либо от BaseEntity (MappedSuperclass'ы).
Выкинут BaseDict (дикт тут и так один, шаблон
не нужен). Пофикшено наследование репозиториев.
Теперь все репы наследуются либо от BaseLinkRepo,
либо от BaseEntityRepo, которые наследуются от
JpaRepository. Используется фича с генерацией запроса
по имени метода (всякие findBy...). Шаблонные методы
(достать по айди, удалить по айди) (пере)определены
в BaseEntityRepo и переиспользуются остальными репами.
Business layer
Внутренности сервисов почти один в один совпадают с
со своей предыдущей версией. Добавляются 2 dto -
одна обертка над листом, потому что таймилиф
не может просто так взять и проитерироваться с
сетом по листу, и один plain object для получения
данных для заполнения таблицы связи.
Если создать лист с IngredientForDish и
пытаться сетить сразу объекты, то все падает с ошибками
парсинга. Возможно, если как-то таймилифосовместимо
переопределить тустринг для DictUnit
и Ingredient, ошибка пройдет. Но проще сделать plain
object.
View layer
Страницы остались те же, но вместо jsp -
thymeleaf/html, вместо сервлетов -
обертки сервлетов сервисы, методы
объединены по общей части урла.
Ветка develop:
git checkout develop
Для запуска в докере (с пересборкой образа):
docker-compose -f docker-compose-all.yaml up --build
Основано на Spring-версии лабы. Добавлены xslt (resources/xslt) + рест-контроллеры в controller/v2, урл /api/v2. Старые контроллеры переехали в controller/v1, урл /api/v1.
API:
GET /api/v2/dish/all
GET /api/v2/dish/{id}
POST /api/v2/dish/create
POST /api/v2/dish/delete/{id}
GET /api/v2/ingredient/all
GET /api/v2/ingredient/{id}
POST /api/v2/ingredient/create
POST /api/v2/ingredient/delete/{id}
В новой апишке взаимодействие с вебом ведется через dto (IngredientDTO, DishDTO). Оригинальный xml для Dish слишком тяжеловесный -> пришло время сделать dto. Для Ingredient dto добавлен для единообразия.
В запросах на создание объектов реквест-боди принимается в xml|json. Гет-запросы могут возвращать xml|json либо html (xslt). Если в accept header явно объявлен text/html, то ответ вне зависимости от q будет в формате text/html. Если нет text/html и есть application/xml || application/json || */*, ответ будет в xml|json.
JAX-RS vs SpringREST
JAX-RS - часть джава-ее спецификации, предоставляет легковесную апишку для создания рест-сервисов, можно выбрать реализацию. SpringREST - часть экосистемы спринга, идет как часть большого супового набора (spring web), все фишки коробочных решений включены.
По сути выбор между JAX-RS и SpringREST - это выбор между Spring и Java-EE.
Почему Spring?
- Следить за совместимостью серверов приложений с реализациями апи весело, но мне больше по душе коробочные решения, convention over configuration, вот это все.
- Со спрингом имеется больше опыта работы.
- Спринг бут красиво заворачивается в докер, изначально выбранный глассфиш такой фишки лишен.
- Люблю продукты джетбреинс. И люблю линукс. Интеллиджи идея в коммунити-версии не поддерживает джаву-ее (первую лабу делала в эклипсе). Проблема решается добыванием про-версии либо подписками (но мои студенческие давно закончились) либо вечным триалом (но плагин работает для старых версий на винде, для моей системы скриптов/плагинов не имеется). А спринг коммунити-идея поддерживает.
Ветка develop:
git checkout develop
Для запуска в докере (с пересборкой образа):
docker-compose -f docker-compose-all.yaml up --build
Предварительно накатить вручную новые таблицы
log_event и mail_condition (автомиграции нет).
Отправка писем реализована через google stmp, для тестирования заполнить SPRING_MAIL_USERNAME и SPRING_MAIL_PASSWORD; в mail_condition добавить записи о адресах и условиях. Условия добавляются джейсоном, где ключ - тип изменения в таблице (типы см. в jms/utils/EventType.java), значение - имена таблиц, либо спец-слово any (для уведомлений по любой таблице).
insert into mail_condition (address, condition)
values ('[email protected]', '{delete : [any], create : [dish]}');
Реализовано через AMQP/RabbitMQ. Конфиг для
кролика вынесен отдельно (config/RabbitConfig).
Созданы классы Producer, ConsumerMail и ConsumerLog.
Продюсер создает сообщения LogEvent c информацией
о типе изменения, таблице и доп. информацией (
id + uuid записи). Сообщения дублируются в 2 очереди,
каждый консьюмер смотрит в свою. Логируются создание
объектов и удаление (де-факто update тк.
soft delete) по Ingredient и Dish (очень странное
ощущение, что пытаешься притащить в
бизнес-логику sql-триггер).
Письма отправляются при прохождении
проверки - дергаются все адреса с условиями
и для конкретных {тип изменения, таблица}
в LogEvent определяется, необходимо ли слать
уведомление по данному адресу
(см структуру mail_condition выше).