У меня есть стандартная служба Spring Boot Java, которая получает некоторые элементы из внешней службы, сохраняет их в базе данных, а затем предлагает некоторые операции для их получения и агрегирования. У меня проблема в том, что я стараюсь использовать как можно больше универсальных шаблонов, но мои промежуточные классы становятся слишком многословными с десятками универсальных c типов параметров, и я хотел бы знать, если у кого-то есть идея, как лучше противостоять этому.
Допустим, приложение получает несколько типов элементов, и у каждого из них могут быть подэлементы и изменения. Они получены от внешней службы, поэтому у меня есть типы DTO ItemDto, SubitemDto и ChangeDto:
public class ItemDto<S extends SubitemDto, C extends ChangeDto> {
private List<S> subitems;
private List<C> changes;
}
и соответствующие им подклассы:
public class Item1Dto<SubItem1Dto, Change1Dto> {}
public class Item2Dto<SubItem2Dto, Change2Dto> {}
Я получаю файл json от внешний сервис со списком этих предметов, и я успешно разбираю их с Джексоном. Теперь я хочу сохранить их в базе данных и использую JPA, поэтому я создал их аналоги сущностей JPA:
@MappedSuperclass
public class Item {
@Id
private int id;
}
@MappedSuperclass
public class SubItem<I extends Item> {
@Id
private int id;
@ManyTo
private I item;
}
@MappedSuperclass
public class Change<I extends Item> {
@Id
private int id;
@ManyTo
private I item;
}
По соображениям производительности я не добавляю часть отношений @OneToMany в Item объекты, так как я буду фильтровать и извлекать их в своем собственном хранилище. Теперь у меня есть конкретные реализации, каждый в своей таблице:
@Entity("item1)
public class Item1 extends Item {
}
@Entity("subitem1")
public class SubItem1 extends SubItem<Item1>{
}
@Entity("change1")
public class Change1 extends Change<Item1> {
}
Затем я создал репозитории Spring Data. Поскольку некоторые операции (но не все) являются общими для разных типов, я создал общий репозиторий c:
@NoBeanRepository
public class ItemRepository<I extends Item> extends CrudRepository<Integer, I> {
}
@NoBeanRepository
public class SubItemRepository<I extends Item, S extends SubItem<I>> extends CrudRepository<Integer, S> {
}
@NoBeanRepository
public class ChangeRepository<I extends Item, C extends Change<I>> extends CrudRepository<Integer, C> {
}
И, конечно, конкретные операции с операциями, которые определены c для каждый подтип:
@ NoBeanRepository publi c класс Item1 extends ItemRepository {}
@NoBeanRepository
public class SubItem1 extends SubItemRepository<SubItem1> {
}
@NoBeanRepository
public class Change1Repository extends ChangeRepository<Change1> {
}
Здесь вы можете начать видеть проблему. Хотя подклассы хранилища довольно лаконичны, реализации generi c начинают становиться очень многословными, но это становится все хуже.
Я получаю Dtos, который мне нужно преобразовать в их аналоги сущностей, поэтому у меня есть:
public class ItemMapper<I extends ItemDto, E extends Item> {}
public class SubItemMapper<S extends SubItemDto, E extends SubItem<I>, I extends Item> {}
...
А теперь мы go до уровня обслуживания. Здесь мы получаем Dtos, преобразуем их в аналог сущности перед сохранением. Чтобы сделать его короче, я go с худшим регистром, который состоит из подпунктов:
public class SubItemService<S extends SubItemDto, E extends SubItem<I>, I extends Item, M extends SubItemMapper<S, E>, R extends SubItemRepository<I, E> {
private M mapper;
private R repository;
public E saveSubItems(I item, List<S> subItems) {
return repository.saveAll(mapper.mapAll(subItems, item));
}
}
Здесь вы можете увидеть проблему: Сервисный лог c тривиален, мы сохраняем много кода, потому что каждый конкретная реализация не должна реализовывать распространенные методы (например, сохранение списка объектов), но обобщенная часть становится очень сложной. Как головоломка, на которую вы должны обратить внимание, имея теперь 5 типов c. И это может ухудшиться. Так как Item, SubItem и Change разделены таблицей, но тесно связаны между собой, у меня будет ItemFacade с ItemService, SubItemService и ChangeService, который становится кошмаром:
public class ItemFacade<I extends Item, S extends SubItem<I>, C extends Change<I>, D extends ItemDto, S1 extends SubItemDto, C1 extends ChangeDto, IM extends Mapper<D, I>, SM extends Mapper<S1, S>, CM extends Mapper<C1, C>, IR extends ItemRepository<I>, SR extends SubItemRepository<S, I>, CR extends ChangeRepository<C, I>, IS extends ItemService<D, I, IM, IR>, SS extends SubItemService<S1, S, SM, SR>, CS extends ChangeService<C1, C, CM, CR> {
private IS itemService;
private SS subItemService;
private CS changeService;
public List<I> addItems(List<D> items) {
for (D itemDto : items) {
I item = itemService.save(itemDto);
List<S> subItems = subItemService.saveAll(item, itemDto.getSubItems());
List<C> changes = changeService.saveAll(item, itemDto.getChanges());
}
........
}
}
И хотя код многократно используется, так как logi c будет разделен между всеми подтипами Item и очень прост из-за разделения интересов между службами, картографическими системами и репозиториями, общая часть становится кошмаром по двум причинам:
нужно 15 !!!! параметры. Невозможно прочитать и склонны к ошибкам. Подклассы более просты, но все же они представляют собой 15 конкретных параметров.
В нем представлены вещи, которые не обязательно должны быть известны фасаду, такие как картографы и хранилища. Они являются внутренними для сервисов и вообще не используются в бизнес-логике фасадов c, но их необходимо указывать в виде типов в обобщенной c части.
Я хотел бы знать, есть ли способ упростить это, чтобы Facade вместо 15 общих параметров c мог быть реализован только с 3 (типами обслуживания) и в то же время сохранял безопасность типов, так как я может игнорировать стандартную часть сервисов, но компилятор предупредит о непроверенных типах, и я хочу, чтобы он был максимально безопасным для типов.
Большое спасибо и извините за длинную экспозицию.