-
Notifications
You must be signed in to change notification settings - Fork 0
/
BookAggregate.kt
140 lines (117 loc) · 4.11 KB
/
BookAggregate.kt
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
package com.cassisi.book
import com.cassisi.common.BaseAggregate
import com.cassisi.reader.ReaderId
import java.time.LocalDate
import java.util.*
class BookAggregate(id: BookId) : Book, BaseAggregate<BookId, BookEvent>(id) {
private var currentLoan: Loan = NoLoan
private var currentReservation: Reservation = NoReservation
override fun borrowBook(readerId: ReaderId, startDate: LocalDate, policy: BorrowBookPolicy): Result<Book> {
// check if this book is already loan
if (currentLoan is ActiveLoan) {
return Result.failure(BookAlreadyLoan(getId()))
}
// check if there is a reservation that was made by another reader
if (currentReservation is ActiveReservation) {
val reservation = (currentReservation as ActiveReservation)
if (reservation.readerId != readerId) {
return Result.failure(BookReservedByOtherReader(readerId, reservation.readerId))
}
}
// validate if student borrow policy
val result = policy.validateIfStudentIsAllowedToBorrowBook(readerId)
result.onFailure { return Result.failure(it) }
// the book can be borrowed, thus an event is created
val loanId = LoanId(UUID.randomUUID())
val endDate = startDate.plusWeeks(6)
val event = BookBorrowed(
getId(),
readerId,
loanId,
startDate,
endDate
)
registerEvent(event)
// clear reservation if book was reserved
if (currentReservation is ActiveReservation) {
clearReservation()
}
// return current instance
return Result.success(this)
}
override fun returnBook(returnDate: LocalDate): Result<Book> {
if (currentLoan is NoLoan) {
return Result.failure(BookNotLent(getId()))
}
val loan = currentLoan as ActiveLoan
val event = BookReturned(
getId(),
loan.loanId,
returnDate
)
registerEvent(event)
return Result.success(this)
}
override fun reserveBook(readerId: ReaderId, reservationDate: LocalDate): Result<Book> {
if (currentLoan !is ActiveLoan) {
// books can only be reserved if currently lent
return Result.failure(BookNotLent(getId()))
}
if (currentReservation is ActiveReservation) {
return Result.failure(BookAlreadyReserved(getId()))
}
val expirationDate = (currentLoan as ActiveLoan).endDate.plusWeeks(1)
val event = BookReserved(
getId(),
readerId,
reservationDate,
expirationDate
)
registerEvent(event)
return Result.success(this)
}
override fun clearReservation(): Result<Book> {
return if (currentReservation is ActiveReservation) {
val event = ReservationCleared(getId())
registerEvent(event)
Result.success(this)
} else {
Result.failure(BookNotReserved(getId()))
}
}
override fun handleEvent(event: BookEvent) {
when (event) {
is BookRegistered -> handle(event)
is BookBorrowed -> handle(event)
is BookReturned -> handle(event)
is BookReserved -> handle(event)
is ReservationCleared -> handle(event)
}
}
private fun handle(event: BookRegistered) {
this.currentLoan = NoLoan
this.currentReservation = NoReservation
}
private fun handle(event: BookBorrowed) {
this.currentLoan = ActiveLoan(
event.loanId,
event.readerId,
event.loanDate,
event.loanEndDate,
0
)
}
private fun handle(event: BookReturned) {
this.currentLoan = NoLoan
}
private fun handle(event: BookReserved) {
this.currentReservation = ActiveReservation(
event.readerId,
event.reservedAt,
event.expiresAt
)
}
private fun handle(event: ReservationCleared) {
this.currentReservation = NoReservation
}
}