Лучший дизайн для обработки запросов с помощью DTO, имеющих поле типа, определяющее поведение конечной точки.JAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Лучший дизайн для обработки запросов с помощью DTO, имеющих поле типа, определяющее поведение конечной точки.

Сообщение Anonymous »

Прежде всего, это мой первый вопрос, у меня недостаточно опыта. Но я предоставлю всю необходимую информацию для понимания этого.
Я делаю приложение для проекта колледжа, использую Spring-Boot для серверной части и обрабатываю все запросы из внешнего приложения.
Одной из функций приложения, упрощая его, будет управление различными типами платежей для пользователей и создание журнала в базе данных. Все это через эксклюзивный контроллер для всех конечных точек платежа, который будет вызывать платежный сервис.
Пока я создавал функцию, которая обрабатывает один из типов платежей в сервисе (который получает определенный DTO для этого типа платежа), я понял, что независимо от того, какой тип платежа я получаю, приложение всегда будет генерировать журнал платежей, поэтому я рассматриваю возможность инкапсулировать функциональность конкретного платежа, который я уже сделал, в метод и вызывать его из общего метода, который получает общий запрос платежа DTO (сокращенно gprDTO), проверяет его содержимое и тип и вызывает соответствующую функцию.
Тело общей функции будет примерно таким:

Код: Выделить всё

 @Transactional
public void processGenericPayment(GenericPaymentRequestDTO request) throws AccessDeniedException, IllegalArgumentException{

//0. Check if the user has permission
User manager = userRepository.findById(email).orElseThrow(//handle exception);
if(!userService.checkAdminAccess(email)) {
throw new AccessDeniedException("Not allowed");
}

//1. Create the log
PaymentLog log = new PaymentLog();
//2. Check the type and call the specific function
switch(request.getType()) {
case TYPE_X->  {
if(request.getUserId() == null || request.getcouponId() == null) throw new IllegalArgumentException("Something");
processPaymentX(request.getUserId(), request.getCouponId());
log.setUser(userRepository.findById(request.getUserId()).orElsethrow(//handle exception)
log.setCoupon(couponRepository.findById(request.getCouponId()).orElseThrow(//handle exception))
}
case TYPE_Y-> {
if(request.getItemId() == null) throw new IllegalArgumentException("Something");
processTypeY(request.getItemId());
log.setItem(itemRepository.findById(request.getItemId()).orElseThrow(//handle exception))
}
case TYPE_Z-> {
if(request.getUserId() == null) throw new IllegalArgumentException("Something");
processTypeZ(request.getUserId());
log.setItem(itemRepository.findById(request.getUserId()).orElseThrow(//handle exception))
}
// All cases
}

//3. Finish the payment log

log.setDate(LocalDateTime.now());
log.setMessage(request.getMessage());
//All fields

//4.  Save the log
logRepository.save(log)

}
GenericPaymentRequestDTO (gprDTO упоминался ранее) может быть:

Код: Выделить всё

@Getter
@Setter
@NoArgsConstructor
@Data
public class GenericPaymentRequestDTO {

@NotNull
private PaymentLogType type;

@NotNull
private Long managerId; //This is the user that has managed the payment (The service checks if it has the permissions to do this)

@NotNull
private String message;

@NotNull
private Double price;

private Long userId; //Used in payment type X

private Long itemId; //Used in payment type Y

private Long couponId; //Used in payment type X & Z

//All needed fields...

}
Сейчас у меня есть объект PaymentLog:

Код: Выделить всё

@Getter
@Setter
@Entity
@Table(name = "payment_logs")
public class PaymentLog implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "log_id", nullable = false)
private Long logId;

@NotNull
@Column(name = "price")
private Double price;

@NotNull
@Column(name = "date", nullable = false)
private LocalDateTime date;

@NotNull
@Column(name = "message")
private String message;

@NotNull
@Column(name="type", nullable = false, columnDefinition = "payment_log_type")
private PaymentLogType type;

@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "manager_id", nullable = false)
private User manager;

//This fields will be null or not depending of the type.

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private InventoryItem item;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "coupon_id")
private Coupon coupon;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

//There are more fields, but for simplification, I only added these at the snipet.

}
Я не уверен, что это хорошая практика для моего приложения, поскольку не для всех платежей требуются одни и те же параметры. (некоторым нужен идентификатор пользователя, другим нужен идентификатор элемента, ...) и это может привести к тому, что в gprDTO в общем методе будет много нулевых проверок. Или, может быть, следует создать один dto для каждого типа платежа и избегать всех этих нулевых проверок, добавляя аннотации @NotNull Jakarta в поля конкретных DTO.
И еще один вопрос: хорошо ли написан код, который получает объекты журнала платежей, которые не являются нулевыми (товар, купон, пользователь,...). Конкретные методы будут обрабатывать исключение, если объект не найден в базе данных, и, возможно, orElsethrow() внутри случаев переключателя является избыточным.
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»