Skip to content

Commit

Permalink
refactor ExamService
Browse files Browse the repository at this point in the history
  • Loading branch information
pionas committed Dec 16, 2023
1 parent e435b19 commit 80e6b45
Show file tree
Hide file tree
Showing 28 changed files with 454 additions and 66 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package info.pionas.quiz.domain.exam;

import info.pionas.quiz.domain.exam.api.AnswerForQuestionNotFoundException;
import info.pionas.quiz.domain.exam.api.PassExamAnswer;
import info.pionas.quiz.domain.quiz.api.Question;
import info.pionas.quiz.domain.quiz.api.Quiz;
import info.pionas.quiz.domain.quiz.api.QuizRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Component
@AllArgsConstructor
class EndExamAnswerForAllQuestionValidationRule implements EndExamValidationRule {

private final QuizRepository quizRepository;

@Override
public void validate(UUID quizId, List<PassExamAnswer> answers) {
quizRepository.findById(quizId)
.map(Quiz::getQuestions)
.orElseGet(Collections::emptyList)
.forEach(question -> checkQuestionHasAnswer(question, answers));
}

private void checkQuestionHasAnswer(Question question, List<PassExamAnswer> answers) {
final var questionId = question.getId();
boolean existAnswerForQuestion = Optional.ofNullable(answers)
.orElseGet(Collections::emptyList)
.stream()
.anyMatch(passExamAnswer -> passExamAnswer.isAnswerForQuestion(questionId));
if (!existAnswerForQuestion) {
throw new AnswerForQuestionNotFoundException(questionId);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package info.pionas.quiz.domain.exam;

import info.pionas.quiz.domain.exam.api.PassExamAnswer;
import info.pionas.quiz.domain.quiz.QuizNotFoundException;
import info.pionas.quiz.domain.quiz.api.QuizRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.UUID;

@Component
@AllArgsConstructor
class EndExamQuizExistValidationRule implements EndExamValidationRule {

private final QuizRepository quizRepository;

@Override
public void validate(UUID quizId, List<PassExamAnswer> answers) {
if (!quizRepository.existById(quizId)) {
throw new QuizNotFoundException(quizId);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package info.pionas.quiz.domain.exam;

import info.pionas.quiz.domain.exam.api.PassExamAnswer;

import java.util.List;
import java.util.UUID;

interface EndExamValidationRule {

void validate(UUID quizId, List<PassExamAnswer> answers);
}
19 changes: 19 additions & 0 deletions src/main/java/info/pionas/quiz/domain/exam/EndExamValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package info.pionas.quiz.domain.exam;

import info.pionas.quiz.domain.exam.api.PassExamAnswer;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.UUID;

@Component
@AllArgsConstructor
class EndExamValidator {

private final List<EndExamValidationRule> examValidationRuleList;

void validate(UUID quizId, List<PassExamAnswer> answers) {
examValidationRuleList.forEach(endExamValidationRule -> endExamValidationRule.validate(quizId, answers));
}
}
43 changes: 0 additions & 43 deletions src/main/java/info/pionas/quiz/domain/exam/ExamFactory.java

This file was deleted.

56 changes: 45 additions & 11 deletions src/main/java/info/pionas/quiz/domain/exam/ExamServiceImpl.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,65 @@
package info.pionas.quiz.domain.exam;

import info.pionas.quiz.domain.exam.api.PassExamAnswer;
import info.pionas.quiz.domain.exam.api.ExamRepository;
import info.pionas.quiz.domain.exam.api.ExamResult;
import info.pionas.quiz.domain.exam.api.ExamService;
import info.pionas.quiz.domain.quiz.QuizNotFoundException;
import info.pionas.quiz.domain.quiz.api.QuizRepository;
import info.pionas.quiz.domain.exam.api.*;
import info.pionas.quiz.domain.quiz.api.QuizAnswerRepository;
import info.pionas.quiz.domain.shared.DateTimeProvider;
import info.pionas.quiz.domain.shared.UuidGenerator;
import jakarta.transaction.Transactional;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Service
@AllArgsConstructor
class ExamServiceImpl implements ExamService {

private final QuizRepository quizRepository;
private final ExamRepository examRepository;
private final ExamFactory examFactory;
private final ExamAnswerRepository examAnswerRepository;
private final QuizAnswerRepository quizAnswerRepository;
private final EndExamValidator endExamValidator;
private final UuidGenerator uuidGenerator;
private final DateTimeProvider dateTimeProvider;

@Override
@Transactional
public ExamResult endExam(String username, UUID quizId, List<PassExamAnswer> answers) {
final var quiz = quizRepository.findById(quizId)
.orElseThrow(() -> new QuizNotFoundException(quizId));
return examRepository.save(uuidGenerator.generate(), examFactory.endExam(username, quiz, answers));
endExamValidator.validate(quizId, answers);
final var resultId = uuidGenerator.generate();
final var dateTime = dateTimeProvider.now();

examRepository.save(getNewExamDetails(resultId, quizId, username, dateTime));
examAnswerRepository.saveAll(getExamAnswers(resultId, answers, dateTime));
return examRepository.getById(resultId);
}

private NewExamDetails getNewExamDetails(UUID resultId, UUID quizId, String username, LocalDateTime dateTime) {
return NewExamDetails.builder()
.id(resultId)
.username(username)
.quizId(quizId)
.created(dateTime)
.build();
}

private List<NewExamAnswer> getExamAnswers(UUID resultId, List<PassExamAnswer> answers, LocalDateTime dateTime) {
return Optional.ofNullable(answers)
.orElseGet(Collections::emptyList)
.stream()
.map(answer -> prepareNewExamAnswer(resultId, answer, dateTime))
.toList();
}

private NewExamAnswer prepareNewExamAnswer(UUID resultId, PassExamAnswer answer, LocalDateTime dateTime) {
if (quizAnswerRepository.isCorrectAnswer(answer.getQuestionId(), answer.getAnswerId())) {
return NewExamAnswer.correct(resultId, dateTime, answer.getQuestionId(), answer.getAnswerId());
} else {
return NewExamAnswer.wrong(resultId, dateTime, answer.getQuestionId(), answer.getAnswerId());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package info.pionas.quiz.domain.exam.api;

import java.util.List;

public interface ExamAnswerRepository {

void saveAll(List<NewExamAnswer> answers);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.Getter;

import java.util.List;
import java.util.UUID;

@Builder
@Getter
Expand All @@ -15,4 +16,8 @@ public class ExamDetails {
private String username;
private Quiz quiz;
private List<ExamAnswer> answers;

public UUID getQuizId() {
return quiz.getId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@

public interface ExamRepository {

ExamResult save(UUID id, ExamDetails examDetails);
void save(NewExamDetails newExamDetails);

ExamResult getById(UUID id);
}
48 changes: 48 additions & 0 deletions src/main/java/info/pionas/quiz/domain/exam/api/NewExamAnswer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package info.pionas.quiz.domain.exam.api;

import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;
import java.util.UUID;

@Builder
@Getter
public class NewExamAnswer {

private UUID resultId;
private UUID questionId;
private UUID answerId;
private Boolean correct;
private LocalDateTime created;

public static NewExamAnswer of(UUID resultId, ExamAnswer answer, LocalDateTime localDateTime) {
return NewExamAnswer.builder()
.resultId(resultId)
.questionId(answer.getQuestionId())
.answerId(answer.getAnswerId())
.correct(answer.getCorrect())
.created(localDateTime)
.build();
}

public static NewExamAnswer correct(UUID resultId, LocalDateTime dateTime, UUID questionId, UUID answerId) {
return NewExamAnswer.builder()
.resultId(resultId)
.questionId(questionId)
.answerId(answerId)
.correct(true)
.created(dateTime)
.build();
}

public static NewExamAnswer wrong(UUID resultId, LocalDateTime dateTime, UUID questionId, UUID answerId) {
return NewExamAnswer.builder()
.resultId(resultId)
.questionId(questionId)
.answerId(answerId)
.correct(false)
.created(dateTime)
.build();
}
}
17 changes: 17 additions & 0 deletions src/main/java/info/pionas/quiz/domain/exam/api/NewExamDetails.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package info.pionas.quiz.domain.exam.api;

import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;
import java.util.UUID;

@Builder
@Getter
public class NewExamDetails {

private UUID id;
private String username;
private UUID quizId;
private LocalDateTime created;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package info.pionas.quiz.domain.quiz.api;

import java.util.UUID;

public interface QuizAnswerRepository {

boolean isCorrectAnswer(UUID questionId, UUID answerId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public interface QuizRepository {

Quiz save(Quiz quiz);

Optional<Quiz> findById(UUID uuid);
Optional<Quiz> findById(UUID id);

boolean existById(UUID id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package info.pionas.quiz.domain.shared;

import java.time.LocalDateTime;

public interface DateTimeProvider {

LocalDateTime now();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package info.pionas.quiz.infrastructure.database.exam;

import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;

import java.time.LocalDateTime;

@Entity
public class ExamAnswerEntity {

@EmbeddedId
private ExamAnswerId id;

private boolean correct;
private LocalDateTime created;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package info.pionas.quiz.infrastructure.database.exam;

import jakarta.persistence.Embeddable;

import java.util.UUID;

@Embeddable
class ExamAnswerId {

private UUID resultId;
private UUID questionId;
private UUID answerId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package info.pionas.quiz.infrastructure.database.exam;

import info.pionas.quiz.domain.exam.api.NewExamAnswer;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring")
interface ExamAnswerJpaMapper {

ExamAnswerEntity map(NewExamAnswer newExamAnswer);

List<ExamAnswerEntity> map(List<NewExamAnswer> newExamAnswers);

}
Loading

0 comments on commit 80e6b45

Please sign in to comment.