Skip to content

Commit

Permalink
refactor: TicketingCommandService 인터페이스 제거 및 구체 클래스 사용
Browse files Browse the repository at this point in the history
  • Loading branch information
seokjin8678 committed Jun 11, 2024
1 parent c5ec08d commit 7c82096
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 74 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,53 @@
package com.festago.ticketing.application.command;

import com.festago.common.exception.BadRequestException;
import com.festago.common.exception.ErrorCode;
import com.festago.ticket.domain.NewTicket;
import com.festago.ticket.domain.NewTicketType;
import com.festago.ticket.repository.NewTicketDao;
import com.festago.ticketing.domain.Booker;
import com.festago.ticketing.domain.ReserveTicket;
import com.festago.ticketing.domain.TicketingSequenceGenerator;
import com.festago.ticketing.domain.validator.TicketingValidator;
import com.festago.ticketing.dto.TicketingResult;
import com.festago.ticketing.dto.command.TicketingCommand;
import com.festago.ticketing.repository.ReserveTicketRepository;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

public interface TicketingCommandService {
@Service
@Transactional
@RequiredArgsConstructor
public class TicketingCommandService {

TicketingResult reserveTicket(TicketingCommand command);
private final NewTicketDao ticketDao;
private final ReserveTicketRepository reserveTicketRepository;
private final TicketingSequenceGenerator sequenceGenerator;
private final List<TicketingValidator> validators;
private final Clock clock;

public TicketingResult reserveTicket(TicketingCommand command) {
Long ticketId = command.ticketId();
NewTicketType ticketType = command.ticketType();
NewTicket ticket = ticketDao.findByIdWithTicketTypeAndFetch(ticketId, ticketType);
Booker booker = command.booker();
ticket.validateReserve(booker, LocalDateTime.now(clock));
validators.forEach(validator -> validator.validate(ticket, booker));
validate(ticket, booker);
int sequence = sequenceGenerator.generate(ticketId);
ReserveTicket reserveTicket = ticket.reserve(booker, sequence);
reserveTicketRepository.save(ticket.reserve(booker, sequence));
return new TicketingResult(reserveTicket.getTicketId());
}

private void validate(NewTicket ticket, Booker booker) {
long reserveCount = reserveTicketRepository.countByMemberIdAndTicketId(booker.getMemberId(), ticket.getId());
if (reserveCount >= ticket.getMaxReserveAmount()) {
throw new BadRequestException(ErrorCode.RESERVE_TICKET_OVER_AMOUNT);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.festago.ticketing.application.command;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.mock;

import com.festago.common.infrastructure.FakeTicketingRateLimiter;
import com.festago.ticket.domain.FakeTicket;
Expand All @@ -27,31 +30,43 @@ class QuantityTicketingServiceTest {

TicketQuantityRepository ticketQuantityRepository;

ExecutorService executorService = Executors.newFixedThreadPool(8);
TicketingCommandService ticketingCommandService;

TicketingCommand command;

AtomicLong reserveCount;

FakeTicketingRateLimiter fakeMemberRateLimiter = new FakeTicketingRateLimiter(false);
TicketQuantity ticketQuantity;

ExecutorService executorService = Executors.newFixedThreadPool(8);

Long ticketId = 1L;

@BeforeEach
void setUp() {
ticketQuantityRepository = new MemoryTicketQuantityRepository();
ticketingCommandService = mock();
reserveCount = new AtomicLong();
command = TicketingCommand.builder()
.ticketId(ticketId)
.build();
quantityTicketingService = new QuantityTicketingService(
ticketQuantityRepository,
ticketingCommandService,
new FakeTicketingRateLimiter(false)
);
}

@Test
void 티켓팅은_동시성_문제가_발생하지_않아야_한다() {
// given
int ticketAmount = 50;
long tryCount = 100;
TicketQuantity ticketQuantity = ticketQuantityRepository.put(new FakeTicket(1L, ticketAmount));
AtomicLong reserveCount = new AtomicLong();
quantityTicketingService = new QuantityTicketingService(ticketQuantityRepository, command -> {
ticketQuantity = ticketQuantityRepository.put(new FakeTicket(ticketId, ticketAmount));
given(ticketingCommandService.reserveTicket(any())).willAnswer(invoke -> {
reserveCount.incrementAndGet();
return null;
}, fakeMemberRateLimiter);
var command = TicketingCommand.builder()
.ticketId(ticketId)
.build();
});

// when
List<CompletableFuture<Void>> futures = LongStream.rangeClosed(1, tryCount)
Expand All @@ -71,20 +86,16 @@ void setUp() {
// given
int ticketAmount = 50;
long tryCount = 100;
TicketQuantity ticketQuantity = ticketQuantityRepository.put(new FakeTicket(1L, ticketAmount));
AtomicLong reserveCount = new AtomicLong();
AtomicLong counter = new AtomicLong();
quantityTicketingService = new QuantityTicketingService(ticketQuantityRepository, command -> {
ticketQuantity = ticketQuantityRepository.put(new FakeTicket(ticketId, ticketAmount));
given(ticketingCommandService.reserveTicket(any())).willAnswer(invoke -> {
long count = counter.incrementAndGet();
if (count <= 25 && count % 5 == 0) { // 5번 예외 발생
throw new IllegalArgumentException();
}
reserveCount.incrementAndGet();
return null;
}, fakeMemberRateLimiter);
var command = TicketingCommand.builder()
.ticketId(ticketId)
.build();
});

// when
List<CompletableFuture<Void>> futures = LongStream.rangeClosed(1, tryCount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
@SuppressWarnings("NonAsciiCharacters")
class TicketingCommandServiceTest {

TicketingCommandServiceImpl ticketingCommandService;
TicketingCommandService ticketingCommandService;

ReserveTicketRepository reserveTicketRepository;

Expand All @@ -52,7 +52,7 @@ void setUp() {
reserveTicketRepository = new MemoryReserveTicketRepository();
stageTicketRepository = new MemoryStageTicketRepository();
clock = spy(Clock.systemDefaultZone());
ticketingCommandService = new TicketingCommandServiceImpl(
ticketingCommandService = new TicketingCommandService(
new NewTicketDao(stageTicketRepository),
reserveTicketRepository,
new MemoryTicketingSequenceGenerator(),
Expand Down

0 comments on commit 7c82096

Please sign in to comment.