Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita001ce7

Browse files
committed
refactor(lending): checkout out book
Refactor domain part of checking out a book.Remove policies as there is only one ruleand it is an invariant.Replace BookRepository with the FindBookOnHold.
1 parent3ad2f0d commita001ce7

File tree

6 files changed

+42
-133
lines changed

6 files changed

+42
-133
lines changed

‎libs/lending/application/src/lib/check-out/check-out-book.handler.spec.ts‎

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,67 +6,62 @@ import { createSpyObj } from 'jest-createspyobj';
66
import{BookFixtures}from'../../../../domain/tests/book.fixtures';
77
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
88
import{PatronFixtures}from'../../../../domain/tests/patron.fixtures';
9-
import{BookRepository}from'../ports/book.repository';
9+
import{FindBookOnHold}from'../cancel-hold/find-book-on-hold';
1010
import{PatronRepository}from'../ports/patron.repository';
1111
import{CheckOutBookCommand}from'./check-out-book.command';
1212
import{CheckOutBookHandler}from'./check-out-book.handler';
1313

1414
describe('CheckOutBookHandler',()=>{
15-
constbookRepository=createSpyObj(BookRepository,['findById','save']);
16-
constpatronRepository=createSpyObj(PatronRepository,[
17-
'findById',
18-
'publish',
19-
]);
15+
constbookOnHold=BookFixtures.bookOnHold();
16+
17+
constwillFindBook:FindBookOnHold={
18+
findBookOnHold:()=>Promise.resolve(some(bookOnHold)),
19+
};
20+
constwillNotFindBook:FindBookOnHold={
21+
findBookOnHold:()=>Promise.resolve(none),
22+
};
23+
constpatronRepository:jest.Mocked<PatronRepository>=createSpyObj(
24+
PatronRepository,
25+
['findById','publish']
26+
);
2027

2128
it('should successfully check out book if patron and book exist',async()=>{
2229
// given
23-
constcheckingOut=newCheckOutBookHandler(
24-
bookRepository,
25-
patronRepository
26-
);
27-
// and
28-
constbook=persistedOnHoldBook(bookRepository);
30+
constcheckingOut=newCheckOutBookHandler(willFindBook,patronRepository);
2931
// and
30-
constpatron=persistedRegularPatronWithHold(patronRepository,book);
32+
constpatronId=persistedRegularPatronWithHold(
33+
patronRepository,
34+
bookOnHold
35+
);
3136
// when
3237
constresult=awaitcheckingOut.execute(
33-
newCheckOutBookCommand(patron,book.bookId)
38+
newCheckOutBookCommand(patronId,bookOnHold.bookId)
3439
);
3540
// then
3641
expect(result).toBe(Result.Success);
3742
});
3843

3944
it('should reject checking out book if one of the domain rules is broken (but should not fail!)',async()=>{
4045
// given
41-
constcheckingOut=newCheckOutBookHandler(
42-
bookRepository,
43-
patronRepository
44-
);
46+
constcheckingOut=newCheckOutBookHandler(willFindBook,patronRepository);
4547
// and
46-
constpatron=persistedRegularPatronWithoutHold(patronRepository);
47-
//and
48-
constbook=persistedOnHoldBook(bookRepository);
48+
constpatronId=persistedRegularPatronWithoutHold(patronRepository);
4949
// when
5050
constresult=awaitcheckingOut.execute(
51-
newCheckOutBookCommand(patron,book.bookId)
51+
newCheckOutBookCommand(patronId,bookOnHold.bookId)
5252
);
5353
// then
5454
expect(result).toBe(Result.Rejection);
5555
});
5656

5757
it('should fail if patron does not exists',async()=>{
5858
// given
59-
constcheckingOut=newCheckOutBookHandler(
60-
bookRepository,
61-
patronRepository
62-
);
59+
constcheckingOut=newCheckOutBookHandler(willFindBook,patronRepository);
6360
// and
6461
constpatron=unknownPatron(patronRepository);
65-
// and
66-
constbook=persistedOnHoldBook(bookRepository);
6762
// when
6863
constresult=checkingOut.execute(
69-
newCheckOutBookCommand(patron,book.bookId)
64+
newCheckOutBookCommand(patron,bookOnHold.bookId)
7065
);
7166
// then
7267
awaitexpect(result).rejects.toThrow();
@@ -75,35 +70,20 @@ describe('CheckOutBookHandler', () => {
7570
it('should fail if book does not exists',async()=>{
7671
// given
7772
constcheckingOut=newCheckOutBookHandler(
78-
bookRepository,
73+
willNotFindBook,
7974
patronRepository
8075
);
8176
// and
82-
constnotExistingBook=unknownBook(bookRepository);
83-
// and
8477
constpatron=persistedRegularPatron(patronRepository);
8578
// when
8679
constresult=checkingOut.execute(
87-
newCheckOutBookCommand(patron,notExistingBook)
80+
newCheckOutBookCommand(patron,BookId.generate())
8881
);
8982
// then
9083
awaitexpect(result).rejects.toThrow();
9184
});
9285
});
9386

94-
functionpersistedOnHoldBook(
95-
repository:jest.Mocked<BookRepository>
96-
):BookOnHold{
97-
constbook=BookFixtures.bookOnHold();
98-
repository.findById.mockResolvedValueOnce(some(book));
99-
returnbook;
100-
}
101-
102-
functionunknownBook(repository:jest.Mocked<BookRepository>):BookId{
103-
repository.findById.mockResolvedValueOnce(none);
104-
returnBookId.generate();
105-
}
106-
10787
functionunknownPatron(repository:jest.Mocked<PatronRepository>):PatronId{
10888
repository.findById.mockResolvedValueOnce(none);
10989
returnPatronId.generate();

‎libs/lending/application/src/lib/check-out/check-out-book.handler.ts‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import{BookRepository,PatronRepository}from'@library/lending/application';
21
import{
3-
Book,
42
BookCheckedOut,
53
BookCheckOutFailed,
64
BookId,
5+
BookOnHold,
76
Patron,
87
PatronId,
98
}from'@library/lending/domain';
@@ -13,19 +12,21 @@ import { CommandHandler } from '@nestjs/cqrs';
1312
import{match}from'fp-ts/Either';
1413
import{pipe}from'fp-ts/function';
1514
import{getOrElseW}from'fp-ts/Option';
15+
import{FindBookOnHold}from'../cancel-hold/find-book-on-hold';
16+
import{PatronRepository}from'../ports/patron.repository';
1617
import{CheckOutBookCommand}from'./check-out-book.command';
1718

1819
@CommandHandler(CheckOutBookCommand)
1920
exportclassCheckOutBookHandler
2021
implementsIInferredCommandHandler<CheckOutBookCommand>
2122
{
2223
constructor(
23-
privatereadonlybookRepository:BookRepository,
24+
privatereadonlyfindBookOnHold:FindBookOnHold,
2425
privatereadonlypatronRepository:PatronRepository
2526
){}
2627

2728
asyncexecute(command:CheckOutBookCommand):Promise<Result>{
28-
constbook=awaitthis.findBook(command.bookId);
29+
constbook=awaitthis.findBook(command.bookId,command.patronId);
2930
constpatron=awaitthis.findPatron(command.patronId);
3031
constresult=patron.checkoutBook(book);
3132
returnpipe(
@@ -37,8 +38,8 @@ export class CheckOutBookHandler
3738
);
3839
}
3940

40-
privatefindBook(id:BookId):Promise<Book>{
41-
returnthis.bookRepository.findById(id).then((result)=>
41+
privatefindBook(id:BookId,patronId:PatronId):Promise<BookOnHold>{
42+
returnthis.findBookOnHold.findBookOnHold(id,patronId).then((result)=>
4243
pipe(
4344
result,
4445
getOrElseW(()=>{

‎libs/lending/domain/src/lib/factories/patron.factory.ts‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import{Patron}from'../patron';
2-
import{allCheckingOutPolicies}from'../policies/checking-out.policy';
32
import{allCurrentPolicies}from'../policies/placing-on-hold-policy';
43
import{BookId}from'../value-objects/book-id';
54
import{Hold}from'../value-objects/hold';
@@ -16,7 +15,6 @@ export class PatronFactory {
1615
patronHolds:Set<[BookId,LibraryBranchId]>
1716
):Patron{
1817
returnnewPatron(
19-
allCheckingOutPolicies,
2018
newPatronHolds(
2119
newSet(
2220
[...patronHolds].map(

‎libs/lending/domain/src/lib/patron.ts‎

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import{Either,isLeft,left,right}from'fp-ts/lib/Either';
22
import{getLeft,isNone,none,Option}from'fp-ts/lib/Option';
33
import{AvailableBook}from'./book/available-book';
4-
import{Book}from'./book/book';
54
import{BookOnHold}from'./book/book-on-hold';
65
import{BookCheckOutFailed}from'./events/book-check-out-failed';
76
import{BookCheckedOut}from'./events/book-checked-out';
@@ -11,7 +10,6 @@ import { BookHoldFailed } from './events/book-hold-failed';
1110
import{BookPlacedOnHold}from'./events/book-placed-on-hold';
1211
import{BookPlacedOnHoldEvents}from'./events/book-placed-on-hold-events';
1312
import{MaximumNumberOhHoldsReached}from'./events/maximum-number-on-holds-reached';
14-
import{CheckingOutPolicy}from'./policies/checking-out.policy';
1513
import{
1614
PlacingOnHoldPolicy,
1715
Rejection,
@@ -22,7 +20,6 @@ import { PatronInformation } from './value-objects/patron-information';
2220

2321
exportclassPatron{
2422
constructor(
25-
privatereadonlycheckingOutPolicies:Set<CheckingOutPolicy>,
2623
privatereadonlypatronHolds:PatronHolds,
2724
privatereadonlyplacingOnHoldPolicies:Set<PlacingOnHoldPolicy>,
2825
privatereadonlypatronInformation:PatronInformation
@@ -43,28 +40,17 @@ export class Patron {
4340
returnleft(newBookHoldCancelingFailed(this.patronInformation.patronId));
4441
}
4542

46-
checkoutBook(
47-
book:Book
48-
):Either<BookCheckOutFailed,BookCheckedOut>{
49-
constrejection=this.patronCanCheckout(book);
50-
51-
if(!isNone(rejection)){
52-
returnleft(
53-
BookCheckOutFailed.bookCheckOutFailedBecause(
54-
rejection.value,
55-
this.patronInformation.patronId
56-
)
57-
);
43+
checkoutBook(book:BookOnHold):Either<BookCheckOutFailed,BookCheckedOut>{
44+
if(this.patronHolds.includes(book)){
45+
returnright(newBookCheckedOut(this.patronInformation.patronId));
5846
}
5947

60-
returnright(newBookCheckedOut(this.patronInformation.patronId));
61-
}
62-
63-
privatepatronCanCheckout(book:Book):Option<Rejection>{
64-
constrejection=[...this.checkingOutPolicies]
65-
.map((policy)=>policy(book,this))
66-
.find(isLeft);
67-
returnrejection ?getLeft(rejection) :none;
48+
returnleft(
49+
BookCheckOutFailed.bookCheckOutFailedBecause(
50+
Rejection.withReason('book is not on hold by patron'),
51+
this.patronInformation.patronId
52+
)
53+
);
6854
}
6955

7056
isRegular():boolean{

‎libs/lending/domain/src/lib/policies/checking-out.policy.ts‎

Lines changed: 0 additions & 51 deletions
This file was deleted.

‎libs/lending/domain/tests/patron.fixtures.ts‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Version } from '@library/shared/domain';
22
import{AvailableBook}from'../src/lib/book/available-book';
33
import{BookOnHold}from'../src/lib/book/book-on-hold';
44
import{Patron}from'../src/lib/patron';
5-
import{allCheckingOutPolicies}from'../src/lib/policies/checking-out.policy';
65
import{
76
allCurrentPolicies,
87
onlyResearcherPatronsCanPlaceOpenEndedHolds,
@@ -21,7 +20,6 @@ export class PatronFixtures {
2120
patronId?:PatronId
2221
):Patron{
2322
returnnewPatron(
24-
allCheckingOutPolicies,
2523
newPatronHolds(
2624
newSet([newHold(bookOnHold.bookId,bookOnHold.libraryBranchId)])
2725
),
@@ -38,7 +36,6 @@ export class PatronFixtures {
3836
patronId=PatronId.generate();
3937
}
4038
returnnewPatron(
41-
allCheckingOutPolicies,
4239
newPatronHolds(newSet()),
4340
newSet([onlyResearcherPatronsCanPlaceOpenEndedHolds]),
4441
newPatronInformation(patronId,PatronType.Regular)
@@ -53,7 +50,6 @@ export class PatronFixtures {
5350
}
5451
staticGivenResearcherPatron():Patron{
5552
returnnewPatron(
56-
allCheckingOutPolicies,
5753
newPatronHolds(newSet()),
5854
newSet([onlyResearcherPatronsCanPlaceOpenEndedHolds]),
5955
newPatronInformation(PatronId.generate(),PatronType.Researcher)
@@ -62,7 +58,6 @@ export class PatronFixtures {
6258

6359
staticregularPatronWithHolds(numberOfHold:number):Patron{
6460
returnnewPatron(
65-
allCheckingOutPolicies,
6661
newPatronHolds(
6762
newSet(
6863
Array(numberOfHold)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp