Skip to content

Commit 3d75865

Browse files
committed
Split into Events & Commands
1 parent d1b1922 commit 3d75865

File tree

7 files changed

+135
-107
lines changed

7 files changed

+135
-107
lines changed

src/allocation/domain/commands.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from dataclasses import dataclass
2+
from datetime import date
3+
from typing import Optional
4+
5+
6+
class Command:
7+
pass
8+
9+
10+
@dataclass
11+
class CreateBatch(Command):
12+
reference: str
13+
sku: str
14+
qty: int
15+
eta: Optional[date] = None
16+
17+
18+
@dataclass
19+
class ChangeBatchQuantity(Command):
20+
reference: str
21+
qty: int
22+
23+
24+
@dataclass
25+
class Allocate(Command):
26+
orderid: str
27+
sku: str
28+
qty: int
29+
30+
31+
@dataclass
32+
class Deallocate(Command):
33+
orderid: str
34+
sku: str
35+
qty: int

src/allocation/domain/events.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,3 @@ class Event:
1010
@dataclass
1111
class OutOfStock(Event):
1212
sku: str
13-
14-
15-
@dataclass
16-
class BatchCreated(Event):
17-
reference: str
18-
sku: str
19-
qty: int
20-
eta: Optional[date] = None
21-
22-
23-
@dataclass
24-
class BatchQuantityChanged(Event):
25-
reference: str
26-
qty: int
27-
28-
29-
@dataclass
30-
class AllocationRequired(Event):
31-
orderid: str
32-
sku: str
33-
qty: int
34-
35-
36-
@dataclass
37-
class DeallocationRequired(Event):
38-
orderid: str
39-
sku: str
40-
qty: int

src/allocation/domain/model.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
from datetime import date
33
from typing import List, NewType, Optional, Set
44

5-
from allocation.domain.events import AllocationRequired, Event, OutOfStock
5+
from allocation.domain.commands import Allocate
6+
from allocation.domain.events import Event, OutOfStock
67

78
Reference = NewType("Reference", str)
89
Sku = NewType("Sku", str)
@@ -113,6 +114,4 @@ def change_batch_quantity(self, reference: Reference, qty: Quantity):
113114
batch._purchased_quantity = qty
114115
while batch.allocated_quaitity > qty:
115116
line = batch.deallocate_one()
116-
self.events.append(
117-
AllocationRequired(line.orderid, line.sku, line.qty)
118-
)
117+
self.events.append(Allocate(line.orderid, line.sku, line.qty))

src/allocation/entrypoints/flask_app.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from datetime import datetime
22

33
from allocation.adapters import orm
4-
from allocation.domain import events
5-
from allocation.service_layer import handlers, unit_of_work, messagebus
4+
from allocation.domain import commands, events
5+
from allocation.service_layer import handlers, messagebus, unit_of_work
66
from flask import Flask, jsonify, request
77

88
orm.start_mappers()
@@ -17,14 +17,14 @@ def add_batch_endpoint():
1717
if eta is not None:
1818
eta = datetime.fromisoformat(eta).date()
1919

20-
event = events.BatchCreated(
20+
message = commands.CreateBatch(
2121
request.json["reference"],
2222
request.json["sku"],
2323
request.json["qty"],
2424
eta=eta,
2525
)
2626

27-
messagebus.handle(event, uow)
27+
messagebus.handle(message, uow)
2828

2929
return "OK", 201
3030

@@ -34,10 +34,10 @@ def allocate_endpoint():
3434
uow = unit_of_work.SqlAlchemyUnitOfWork()
3535

3636
try:
37-
event = events.AllocationRequired(
37+
message = commands.Allocate(
3838
request.json["orderid"], request.json["sku"], request.json["qty"]
3939
)
40-
results = messagebus.handle(event, uow)
40+
results = messagebus.handle(message, uow)
4141
batchref = results.pop(0)
4242
except handlers.InvalidSku as e:
4343
return {"message": str(e)}, 400
@@ -52,10 +52,10 @@ def allocate_endpoint():
5252
def deallocate_endpoint():
5353
uow = unit_of_work.SqlAlchemyUnitOfWork()
5454
try:
55-
event = events.DeallocationRequired(
55+
message = commands.Deallocate(
5656
request.json["orderid"], request.json["sku"], request.json["qty"]
5757
)
58-
results = messagebus.handle(event, uow)
58+
results = messagebus.handle(message, uow)
5959
except handlers.InvalidSku as e:
6060
return {"message": str(e)}, 400
6161

src/allocation/service_layer/handlers.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@
22
from typing import Optional
33

44
from allocation.adapters import email
5-
from allocation.domain import events, model
5+
from allocation.domain import commands, events, model
66
from allocation.service_layer import unit_of_work
77

88

99
class InvalidSku(Exception):
1010
pass
1111

1212

13-
def add_batch(event: events.BatchCreated, uow: unit_of_work.AbstractUnitOfWork):
14-
sku = event.sku
15-
batch = model.Batch(event.reference, event.sku, event.qty, event.eta)
13+
def add_batch(
14+
message: commands.CreateBatch, uow: unit_of_work.AbstractUnitOfWork
15+
):
16+
sku = message.sku
17+
batch = model.Batch(
18+
message.reference, message.sku, message.qty, message.eta
19+
)
1620
with uow:
1721
product = uow.products.get(sku=sku)
1822
if product is None:
@@ -22,20 +26,24 @@ def add_batch(event: events.BatchCreated, uow: unit_of_work.AbstractUnitOfWork):
2226
uow.commit()
2327

2428

25-
def change_batch_quantity(event: events.BatchQuantityChanged, uow):
29+
def change_batch_quantity(message: commands.ChangeBatchQuantity, uow):
2630
with uow:
27-
product = uow.products.get_by_batch_reference(reference=event.reference)
28-
product.change_batch_quantity(reference=event.reference, qty=event.qty)
31+
product = uow.products.get_by_batch_reference(
32+
reference=message.reference
33+
)
34+
product.change_batch_quantity(
35+
reference=message.reference, qty=message.qty
36+
)
2937
uow.commit()
3038

3139

3240
def allocate(
33-
event: events.AllocationRequired, uow: unit_of_work.AbstractUnitOfWork
41+
message: commands.Allocate, uow: unit_of_work.AbstractUnitOfWork
3442
) -> str:
35-
line = model.OrderLine(event.orderid, event.sku, event.qty)
43+
line = model.OrderLine(message.orderid, message.sku, message.qty)
3644

3745
with uow:
38-
product = uow.products.get(sku=event.sku)
46+
product = uow.products.get(sku=message.sku)
3947

4048
if product is None:
4149
raise InvalidSku(f"Invalid sku {line.sku}")
@@ -47,11 +55,11 @@ def allocate(
4755

4856

4957
def deallocate(
50-
event: events.DeallocationRequired, uow: unit_of_work.AbstractUnitOfWork
58+
message: commands.Deallocate, uow: unit_of_work.AbstractUnitOfWork
5159
) -> None:
52-
line = model.OrderLine(event.orderid, event.sku, event.qty)
60+
line = model.OrderLine(message.orderid, message.sku, message.qty)
5361
with uow:
54-
product = uow.products.get(sku=event.sku)
62+
product = uow.products.get(sku=message.sku)
5563

5664
if product is None:
5765
raise InvalidSku(f"Invalid sku {line.sku}")
@@ -61,6 +69,6 @@ def deallocate(
6169

6270

6371
def send_out_of_stock_notification(
64-
event: events.OutOfStock, uow: unit_of_work.AbstractUnitOfWork
72+
message: events.OutOfStock, uow: unit_of_work.AbstractUnitOfWork
6573
):
66-
email.send_mail("stock-admin@made.com", f"Out of stock: {event.sku}")
74+
email.send_mail("stock-admin@made.com", f"Out of stock: {message.sku}")
Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
1-
from allocation.domain import events
2-
from allocation.service_layer import handlers
3-
from allocation.service_layer import unit_of_work
1+
from typing import Union
42

3+
from allocation.domain import commands, events
4+
from allocation.service_layer import handlers, unit_of_work
55

6-
def handle(event: events.Event, uow: unit_of_work.AbstractUnitOfWork):
6+
Message = Union[commands.Command, events.Event]
7+
8+
9+
def handle(message: Message, uow: unit_of_work.AbstractUnitOfWork):
710
results = []
8-
queue = [event]
11+
queue = [message]
912
while queue:
10-
event = queue.pop(0)
11-
for handler in HANDLERS[type(event)]:
12-
results.append(handler(event, uow))
13+
message = queue.pop(0)
14+
if isinstance(message, events.Event):
15+
for handler in EVENT_HANDLERS[type(message)]:
16+
results.append(handler(message, uow))
17+
queue.extend(uow.collect_new_events())
18+
elif isinstance(message, commands.Command):
19+
handler = COMMAND_HANDLERS[type(message)]
20+
results.append(handler(message, uow))
1321
queue.extend(uow.collect_new_events())
22+
else:
23+
raise TypeError(f"Unknown message type {type(message)}")
1424

1525
return results
1626

1727

18-
HANDLERS = {
19-
events.BatchCreated: [handlers.add_batch],
20-
events.BatchQuantityChanged: [handlers.change_batch_quantity],
21-
events.AllocationRequired: [handlers.allocate],
22-
events.DeallocationRequired: [handlers.deallocate],
28+
EVENT_HANDLERS = {
2329
events.OutOfStock: [handlers.send_out_of_stock_notification],
2430
}
31+
32+
33+
COMMAND_HANDLERS = {
34+
commands.CreateBatch: handlers.add_batch,
35+
commands.ChangeBatchQuantity: handlers.change_batch_quantity,
36+
commands.Allocate: handlers.allocate,
37+
commands.Deallocate: handlers.deallocate,
38+
}

0 commit comments

Comments
 (0)