-
Notifications
You must be signed in to change notification settings - Fork 1
/
Show.java
142 lines (124 loc) · 6.45 KB
/
Show.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package com.example.cinema.domain;
import com.example.cinema.domain.ShowCommand.CancelSeatReservation;
import com.example.cinema.domain.ShowCommand.ConfirmReservationPayment;
import com.example.cinema.domain.ShowCommand.CreateShow;
import com.example.cinema.domain.ShowCommand.ReserveSeat;
import com.example.cinema.domain.ShowEvent.SeatReservationCancelled;
import com.example.cinema.domain.ShowEvent.SeatReservationPaid;
import com.example.cinema.domain.ShowEvent.SeatReserved;
import com.example.cinema.domain.ShowEvent.ShowCreated;
import io.vavr.Tuple2;
import io.vavr.collection.HashMap;
import io.vavr.collection.Map;
import io.vavr.control.Either;
import io.vavr.control.Option;
import java.util.List;
import static com.example.cinema.domain.ReservationStatus.CANCELLED;
import static com.example.cinema.domain.ReservationStatus.CONFIRMED;
import static com.example.cinema.domain.ShowCommandError.DUPLICATED_COMMAND;
import static com.example.cinema.domain.ShowCommandError.RESERVATION_NOT_FOUND;
import static com.example.cinema.domain.ShowCommandError.SEAT_NOT_AVAILABLE;
import static com.example.cinema.domain.ShowCommandError.SEAT_NOT_EXISTS;
import static com.example.cinema.domain.ShowCommandError.SHOW_ALREADY_EXISTS;
import static io.vavr.control.Either.left;
import static io.vavr.control.Either.right;
public record Show(String id, String title, Map<Integer, Seat> seats, Map<String, Integer> pendingReservations,
Map<String, FinishedReservation> finishedReservations) {
public static Show create(ShowCreated showCreated) {
InitialShow initialShow = showCreated.initialShow();
List<Tuple2<Integer, Seat>> seats = initialShow.seats().stream().map(seat -> new Tuple2<>(seat.number(), seat)).toList();
return new Show(initialShow.id(), initialShow.title(), HashMap.ofEntries(seats), HashMap.empty(), HashMap.empty());
}
public Either<ShowCommandError, ShowEvent> process(ShowCommand command) {
return switch (command) {
case CreateShow ignored -> left(SHOW_ALREADY_EXISTS);
case ReserveSeat reserveSeat -> handleReservation(reserveSeat);
case ConfirmReservationPayment confirmReservationPayment -> handleConfirmation(confirmReservationPayment);
case CancelSeatReservation cancelSeatReservation -> handleCancellation(cancelSeatReservation);
};
}
private Either<ShowCommandError, ShowEvent> handleConfirmation(ConfirmReservationPayment confirmReservationPayment) {
String reservationId = confirmReservationPayment.reservationId();
return pendingReservations.get(reservationId).fold(
() -> {
if (finishedReservations.get(reservationId).exists(FinishedReservation::isConfirmed)) {
return left(DUPLICATED_COMMAND);
} else {
return left(RESERVATION_NOT_FOUND);
}
},
seatNumber ->
seats.get(seatNumber).<Either<ShowCommandError, ShowEvent>>map(seat ->
right(new SeatReservationPaid(id, reservationId, seatNumber))
).getOrElse(left(SEAT_NOT_EXISTS)));
}
private Either<ShowCommandError, ShowEvent> handleReservation(ReserveSeat reserveSeat) {
int seatNumber = reserveSeat.seatNumber();
if (isDuplicate(reserveSeat.reservationId())) {
return left(DUPLICATED_COMMAND);
} else {
return seats.get(seatNumber).<Either<ShowCommandError, ShowEvent>>map(seat -> {
if (seat.isAvailable()) {
return right(new SeatReserved(id, reserveSeat.walletId(), reserveSeat.reservationId(), seatNumber, seat.price()));
} else {
return left(SEAT_NOT_AVAILABLE);
}
}).getOrElse(left(SEAT_NOT_EXISTS));
}
}
private Either<ShowCommandError, ShowEvent> handleCancellation(CancelSeatReservation cancelSeatReservation) {
String reservationId = cancelSeatReservation.reservationId();
return pendingReservations.get(reservationId).fold(
/*no reservation*/
() -> {
if (finishedReservations.get(reservationId).exists(FinishedReservation::isCancelled)) {
return left(DUPLICATED_COMMAND);
} else {
return left(RESERVATION_NOT_FOUND);
}
},
/*matching reservation*/
seatNumber -> seats.get(seatNumber).<Either<ShowCommandError, ShowEvent>>map(seat ->
right(new SeatReservationCancelled(id, reservationId, seatNumber))
).getOrElse(left(SEAT_NOT_EXISTS))
);
}
private boolean isDuplicate(String reservationId) {
return pendingReservations.containsKey(reservationId) ||
finishedReservations.get(reservationId).isDefined();
}
public Show apply(ShowEvent event) {
return switch (event) {
case ShowCreated ignored -> throw new IllegalStateException("Show is already created, use Show.create instead.");
case SeatReserved seatReserved -> applyReserved(seatReserved);
case SeatReservationPaid seatReservationPaid -> applyReservationPaid(seatReservationPaid);
case SeatReservationCancelled seatReservationCancelled -> applyReservationCancelled(seatReservationCancelled);
};
}
private Show applyReservationPaid(SeatReservationPaid seatReservationPaid) {
Seat seat = getSeatOrThrow(seatReservationPaid.seatNumber());
String reservationId = seatReservationPaid.reservationId();
return new Show(id, title, seats.put(seat.number(), seat.paid()),
pendingReservations.remove(reservationId),
finishedReservations.put(reservationId, new FinishedReservation(reservationId, CONFIRMED)));
}
private Show applyReservationCancelled(SeatReservationCancelled seatReservationCancelled) {
Seat seat = getSeatOrThrow(seatReservationCancelled.seatNumber());
String reservationId = seatReservationCancelled.reservationId();
return new Show(id, title, seats.put(seat.number(), seat.available()),
pendingReservations.remove(reservationId),
finishedReservations.put(reservationId, new FinishedReservation(reservationId, CANCELLED)));
}
private Show applyReserved(SeatReserved seatReserved) {
Seat seat = getSeatOrThrow(seatReserved.seatNumber());
return new Show(id, title, seats.put(seat.number(), seat.reserved()),
pendingReservations.put(seatReserved.reservationId(), seatReserved.seatNumber()),
finishedReservations);
}
private Seat getSeatOrThrow(int seatNumber) {
return seats.get(seatNumber).getOrElseThrow(() -> new IllegalStateException("Seat not exists %s".formatted(seatNumber)));
}
public Option<Seat> getSeat(int seatNumber) {
return seats.get(seatNumber);
}
}