Skip to content

Commit 1a02ae9

Browse files
committed
return events from command handlers, fix tests
1 parent bdffaf7 commit 1a02ae9

10 files changed

Lines changed: 71 additions & 56 deletions

File tree

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
from decimal import Decimal
22
from seedwork.application.command_handlers import CommandResult
33
from seedwork.application.decorators import command_handler
4-
from src.modules.bidding.domain.entities import Bidding
4+
from src.modules.bidding.domain.entities import Listing
5+
from src.modules.bidding.domain.value_objects import Bid, Bidder, Money
56
from src.modules.bidding.domain.repositories import ListingRepository
67

78

89
class PlaceBidCommand:
9-
buyer_id: str
10+
listing_id: str
11+
bidder_id: str
1012
amount: Decimal
1113
currency: str = "USD"
1214

1315

1416
@command_handler
1517
def place_bid(command: PlaceBidCommand, repository: ListingRepository) -> CommandResult:
16-
listing = repository.get_by_id(listing_id=command.listing_id)
17-
if listing is None:
18-
return CommandResult.failed("Listing does not exist")
19-
20-
buyer = Buyer(id=command.buyer_id)
21-
money = Money(amount=command.amount)
22-
buyer.place_bid(listing, money)
18+
bidder = Bidder(id=command.bidder_id)
19+
bid = Bid(bidder=bidder, price=Money(command.amount))
2320

21+
listing: Listing = repository.get_by_id(id=command.listing_id)
22+
events = listing.place_bid(bid)
2423
repository.update(listing)
2524

26-
return CommandResult.ok()
25+
return CommandResult.ok(events=events)
Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
11
from decimal import Decimal
22
from seedwork.application.command_handlers import CommandResult
33
from seedwork.application.decorators import command_handler
4-
from src.modules.bidding.domain.entities import Bidding
4+
from src.modules.bidding.domain.entities import Listing
5+
from src.modules.bidding.domain.value_objects import Bidder
56
from src.modules.bidding.domain.repositories import ListingRepository
67

78

89
class RetractBidCommand:
9-
buyer_id: str
10+
listing_id: str
11+
bidder_id: str
1012
amount: Decimal
1113
currency: str = "USD"
1214

1315

1416
@command_handler
1517
def retract_bid(
16-
command: PlaceBidCommand, repository: ListingRepository
18+
command: RetractBidCommand, repository: ListingRepository
1719
) -> CommandResult:
18-
listing = repository.get_by_id(listing_id=command.listing_id)
19-
if listing is None:
20-
return CommandResult.failed("Listing does not exist")
20+
bidder = Bidder(id=command.bidder_id)
2121

22-
buyer = Buyer(id=command.buyer_id)
23-
money = Money(amount=command.amount)
24-
bid = Bid(buyer=buyer, money=money)
25-
listing.place_bid(bid)
22+
listing: Listing = repository.get_by_id(id=command.listing_id)
23+
events = listing.retract_bid_of(bidder)
2624
repository.update(listing)
2725

28-
return CommandResult.ok()
26+
return CommandResult.ok(events=events)
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from datetime import datetime
2-
from modules.bidding.domain.repositories import ListingRepository
32
from seedwork.application.queries import Query, Field
3+
from seedwork.application.query_handlers import QueryResult
44
from seedwork.application.decorators import query_handler
5+
from modules.bidding.domain.repositories import ListingRepository
56

67

78
class GetPastdueListingsQuery(Query):
@@ -11,6 +12,6 @@ class GetPastdueListingsQuery(Query):
1112
@query_handler
1213
def get_past_due_listings(
1314
query: GetPastdueListingsQuery, listing_repository: ListingRepository
14-
):
15+
) -> QueryResult:
1516
# TODO: not yet implemented
16-
return []
17+
return QueryResult([])

src/modules/bidding/domain/test_listing.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
def test_listing_initial_price():
11-
seller = Seller(uuid=UUID.v4())
11+
seller = Seller(id=UUID.v4())
1212
listing = Listing(
1313
id=Listing.next_id(),
1414
seller=seller,
@@ -20,8 +20,8 @@ def test_listing_initial_price():
2020

2121
def test_place_one_bid():
2222
now = datetime.utcnow()
23-
seller = Seller(uuid=UUID.v4())
24-
bidder = Bidder(uuid=UUID.v4())
23+
seller = Seller(id=UUID.v4())
24+
bidder = Bidder(id=UUID.v4())
2525
bid = Bid(price=Money(20), bidder=bidder, placed_at=now)
2626
listing = Listing(
2727
id=Listing.next_id(),
@@ -35,9 +35,9 @@ def test_place_one_bid():
3535

3636
def test_place_two_bids():
3737
now = datetime.utcnow()
38-
seller = Seller(uuid=UUID.v4())
39-
bidder1 = Bidder(uuid=UUID.v4())
40-
bidder2 = Bidder(uuid=UUID.v4())
38+
seller = Seller(id=UUID.v4())
39+
bidder1 = Bidder(id=UUID.v4())
40+
bidder2 = Bidder(id=UUID.v4())
4141
listing = Listing(
4242
id=Listing.next_id(),
4343
seller=seller,
@@ -51,8 +51,8 @@ def test_place_two_bids():
5151

5252
def test_place_two_bids_by_same_bidder():
5353
now = datetime.utcnow()
54-
seller = Seller(uuid=UUID.v4())
55-
bidder = Bidder(uuid=UUID.v4())
54+
seller = Seller(id=UUID.v4())
55+
bidder = Bidder(id=UUID.v4())
5656
listing = Listing(
5757
id=Listing.next_id(),
5858
seller=seller,
@@ -67,8 +67,8 @@ def test_place_two_bids_by_same_bidder():
6767

6868

6969
def test_cannot_place_bid_if_listing_ended():
70-
seller = Seller(uuid=UUID.v4())
71-
bidder = Bidder(uuid=UUID.v4())
70+
seller = Seller(id=UUID.v4())
71+
bidder = Bidder(id=UUID.v4())
7272
listing = Listing(
7373
id=Listing.next_id(),
7474
seller=seller,
@@ -88,8 +88,8 @@ def test_cannot_place_bid_if_listing_ended():
8888

8989

9090
def test_retract_bid():
91-
seller = Seller(uuid=UUID.v4())
92-
bidder = Bidder(uuid=UUID.v4())
91+
seller = Seller(id=UUID.v4())
92+
bidder = Bidder(id=UUID.v4())
9393
listing = Listing(
9494
id=Listing.next_id(),
9595
seller=seller,
@@ -108,7 +108,7 @@ def test_retract_bid():
108108

109109
def test_cancel_listing():
110110
now = datetime.utcnow()
111-
seller = Seller(uuid=UUID.v4())
111+
seller = Seller(id=UUID.v4())
112112
listing = Listing(
113113
id=Listing.next_id(),
114114
seller=seller,
@@ -123,8 +123,8 @@ def test_cancel_listing():
123123

124124
def test_can_cancel_listing_with_bids():
125125
now = datetime.utcnow()
126-
seller = Seller(uuid=UUID.v4())
127-
bidder = Bidder(uuid=UUID.v4())
126+
seller = Seller(id=UUID.v4())
127+
bidder = Bidder(id=UUID.v4())
128128
listing = Listing(
129129
id=Listing.next_id(),
130130
seller=seller,
@@ -145,8 +145,8 @@ def test_can_cancel_listing_with_bids():
145145

146146
def test_cannot_cancel_listing_with_bids():
147147
now = datetime.utcnow()
148-
seller = Seller(uuid=UUID.v4())
149-
bidder = Bidder(uuid=UUID.v4())
148+
seller = Seller(id=UUID.v4())
149+
bidder = Bidder(id=UUID.v4())
150150
listing = Listing(
151151
id=Listing.next_id(),
152152
seller=seller,

src/modules/bidding/domain/value_objects.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
@dataclass
77
class Bidder(ValueObject):
8-
uuid: UUID
8+
id: UUID
99

1010

1111
@dataclass
1212
class Seller(ValueObject):
13-
uuid: UUID
13+
id: UUID
1414

1515

1616
@dataclass

src/modules/catalog/domain/entities.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from dataclasses import dataclass
12
from datetime import date
23
from typing import Any
34
from seedwork.domain.entities import Entity

src/modules/catalog/domain/rules.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class ListingPriceMustBeGreaterThanZero(BusinessRule):
2020
price: Money
2121

2222
def is_broken(self) -> bool:
23-
return self.price <= 0
23+
return self.price.amount <= 0
2424

2525

2626
class SellerMustBeEligibleForAddingNextListing(BusinessRule):

src/seedwork/application/command_handlers.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import sys
2+
from typing import Any, List
3+
from seedwork.domain.type_hints import DomainEvent
24

35

46
class CommandResult:
5-
def __init__(self, result) -> None:
7+
def __init__(self, result: Any, events: List[DomainEvent]) -> None:
68
self.__result = result
9+
self.__events = events
710
self.__errors = []
811

912
# commands
@@ -13,33 +16,45 @@ def add_error(self, message, exception=None, exception_info=None):
1316

1417
# queries
1518
@property
16-
def result(self):
19+
def result(self) -> Any:
1720
"""Shortcut to get_result()"""
1821
return self.get_result()
1922

20-
def get_result(self):
23+
def get_result(self) -> Any:
2124
"""Gets result"""
2225
assert (
2326
not self.has_errors()
2427
), f"Cannot access result. QueryResult has errors.\n Errors: {self.__errors}"
2528
return self.__result
2629

30+
@property
31+
def events(self) -> List[DomainEvent]:
32+
"""Shortcut to get_events()"""
33+
return self.get_events()
34+
35+
def get_events(self) -> List[DomainEvent]:
36+
"""Gets result"""
37+
assert (
38+
not self.has_errors()
39+
), f"Cannot access events. QueryResult has errors.\n Errors: {self.__errors}"
40+
return self.__events
41+
2742
def get_errors(self):
2843
return self.__errors
2944

3045
def has_errors(self):
3146
return len(self.__errors) > 0
3247

33-
def is_ok(self):
48+
def is_ok(self) -> bool:
3449
return not self.has_errors()
3550

3651
@classmethod
37-
def ok(cls, result):
52+
def ok(cls, result=None, events=[]) -> "CommandResult":
3853
"""Creates a successful result"""
39-
return cls(result=result)
54+
return cls(result=result, events=events)
4055

4156
@classmethod
42-
def failed(cls, message="Failure", exception=None):
57+
def failed(cls, message="Failure", exception=None) -> "CommandResult":
4358
"""Creates a failed result"""
4459
result = cls(result=None)
4560
exception_info = sys.exc_info()

src/seedwork/application/queries.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from pydantic import BaseModel
1+
from pydantic import BaseModel, Field
22

33

44
class Query(BaseModel):

src/seedwork/infrastructure/test_repository.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
from dataclasses import dataclass
12
from seedwork.infrastructure.repository import InMemoryRepository
23
from seedwork.domain.entities import Entity
34

4-
5+
@dataclass
56
class Person(Entity):
67
first_name: str
78
last_name: str
89

910

1011
def test_InMemoryRepository_persist_one():
1112
# arrange
12-
person = Person(first_name="John", last_name="Doe")
13+
person = Person(id=Person.next_id(), first_name="John", last_name="Doe")
1314
repository = InMemoryRepository()
1415

1516
# act
@@ -21,8 +22,8 @@ def test_InMemoryRepository_persist_one():
2122

2223
def test_InMemoryRepository_persist_two():
2324
# arrange
24-
person1 = Person(first_name="John", last_name="Doe")
25-
person2 = Person(first_name="Mary", last_name="Doe")
25+
person1 = Person(id=Person.next_id(), first_name="John", last_name="Doe")
26+
person2 = Person(id=Person.next_id(), first_name="Mary", last_name="Doe")
2627
repository = InMemoryRepository()
2728

2829
# act

0 commit comments

Comments
 (0)