Skip to content

Commit 3e00ef0

Browse files
Merge pull request #10 from codewithme-py/chore/ddd-isolation-refactoring
chore:Domain-Driven-Design
2 parents 2fa0337 + 31eab66 commit 3e00ef0

4 files changed

Lines changed: 120 additions & 21 deletions

File tree

app/services/inventory/internal.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from uuid import UUID
2+
3+
from sqlalchemy import select
4+
from sqlalchemy.ext.asyncio import AsyncSession
5+
6+
from app.core.exceptions import NotFoundError
7+
from app.services.inventory.models import Product, Reservation
8+
from app.services.orders.models import OrderStatus
9+
10+
11+
async def mark_reservation_as_completed(
12+
session: AsyncSession, reservation_id: UUID
13+
) -> None:
14+
res_result = await session.execute(
15+
select(Reservation).with_for_update().where(Reservation.id == reservation_id)
16+
)
17+
reservation = res_result.scalar_one_or_none()
18+
if not reservation:
19+
raise NotFoundError
20+
reservation.status = OrderStatus.COMPLETED
21+
22+
23+
async def mark_reservation_by_order_as_completed(
24+
session: AsyncSession, order_id: UUID
25+
) -> None:
26+
res_result = await session.execute(
27+
select(Reservation).with_for_update().where(Reservation.order_id == order_id)
28+
)
29+
reservation = res_result.scalar_one_or_none()
30+
if not reservation:
31+
raise NotFoundError
32+
reservation.status = OrderStatus.COMPLETED
33+
34+
35+
async def cancel_reservation_and_return_stock(
36+
session: AsyncSession, reservation_id: UUID
37+
) -> None:
38+
res_result = await session.execute(
39+
select(Reservation).with_for_update().where(Reservation.id == reservation_id)
40+
)
41+
reservation = res_result.scalar_one_or_none()
42+
if not reservation:
43+
raise NotFoundError
44+
prod_result = await session.execute(
45+
select(Product).with_for_update().where(Product.id == reservation.product_id)
46+
)
47+
product = prod_result.scalar_one_or_none()
48+
if product:
49+
product.qty_available += reservation.qty_reserved
50+
reservation.status = OrderStatus.CANCELLED
51+
52+
53+
async def cancel_reservation_by_order_and_return_stock(
54+
session: AsyncSession, order_id: UUID
55+
) -> None:
56+
res_result = await session.execute(
57+
select(Reservation).with_for_update().where(Reservation.order_id == order_id)
58+
)
59+
reservation = res_result.scalar_one_or_none()
60+
if not reservation:
61+
raise NotFoundError
62+
prod_result = await session.execute(
63+
select(Product).with_for_update().where(Product.id == reservation.product_id)
64+
)
65+
product = prod_result.scalar_one_or_none()
66+
if product:
67+
product.qty_available += reservation.qty_reserved
68+
reservation.status = OrderStatus.CANCELLED

app/services/inventory/tasks.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
from app.main import logger
77
from app.services.inventory.models import Product, Reservation
8-
from app.services.orders.models import Order, OrderStatus
8+
from app.services.orders.internal import cancel_order_by_system
9+
from app.services.orders.models import OrderStatus
910

1011

1112
async def release_expired_reservations(ctx: dict) -> None:
@@ -38,13 +39,6 @@ async def release_expired_reservations(ctx: dict) -> None:
3839
product.qty_available += reservation.qty_reserved
3940
reservation.status = OrderStatus.EXPIRED
4041
if reservation.order_id is not None:
41-
order_result = await session.execute(
42-
select(Order)
43-
.with_for_update()
44-
.where(Order.id == reservation.order_id)
45-
)
46-
order = order_result.scalar_one_or_none()
47-
if order:
48-
order.status = OrderStatus.CANCELLED
42+
await cancel_order_by_system(session, reservation.order_id)
4943
await session.commit()
5044
logger.info(f'Released reservation {res_id}')

app/services/orders/internal.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from uuid import UUID
2+
3+
from sqlalchemy import select
4+
from sqlalchemy.ext.asyncio import AsyncSession
5+
6+
from app.core.exceptions import NotFoundError
7+
from app.services.orders.models import Order, OrderStatus
8+
9+
10+
async def cancel_order_by_system(session: AsyncSession, order_id: UUID) -> None:
11+
order_result = await session.execute(
12+
select(Order).with_for_update().where(Order.id == order_id)
13+
)
14+
order = order_result.scalar_one_or_none()
15+
if not order:
16+
raise NotFoundError
17+
order.status = OrderStatus.CANCELLED

app/services/orders/service.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
from sqlalchemy.ext.asyncio import AsyncSession
66

77
from app.core.exceptions import ConflictError, NotFoundError
8+
from app.services.inventory.internal import (
9+
cancel_reservation_by_order_and_return_stock,
10+
mark_reservation_by_order_as_completed,
11+
)
812
from app.services.inventory.models import Product, Reservation
913
from app.services.orders.models import Order, OrderItem, OrderStatus
1014
from app.services.orders.schemas import OrderCreate
@@ -89,11 +93,22 @@ async def confirm_order_payment(
8993
order_id: UUID,
9094
user_id: UUID,
9195
) -> Order:
92-
order, reservation = await _get_locked_order_and_reservation(
93-
session, order_id, user_id
96+
order_result = await session.execute(
97+
select(Order)
98+
.with_for_update()
99+
.where(
100+
Order.id == order_id,
101+
Order.user_id == user_id,
102+
)
94103
)
104+
order = order_result.scalar_one_or_none()
105+
if not order:
106+
raise NotFoundError
107+
if order.status != OrderStatus.PENDING:
108+
raise ConflictError
109+
95110
order.status = OrderStatus.PAID
96-
reservation.status = OrderStatus.COMPLETED
111+
await mark_reservation_by_order_as_completed(session, order_id)
97112
await session.commit()
98113
return order
99114

@@ -103,16 +118,21 @@ async def cancel_order(
103118
order_id: UUID,
104119
user_id: UUID,
105120
) -> Order:
106-
order, reservation = await _get_locked_order_and_reservation(
107-
session, order_id, user_id
108-
)
109-
product_rollback = await session.execute(
110-
select(Product).with_for_update().where(Product.id == reservation.product_id)
121+
order_result = await session.execute(
122+
select(Order)
123+
.with_for_update()
124+
.where(
125+
Order.id == order_id,
126+
Order.user_id == user_id,
127+
)
111128
)
112-
product = product_rollback.scalar_one_or_none()
113-
if product:
114-
product.qty_available += reservation.qty_reserved
129+
order = order_result.scalar_one_or_none()
130+
if not order:
131+
raise NotFoundError
132+
if order.status != OrderStatus.PENDING:
133+
raise ConflictError
134+
115135
order.status = OrderStatus.CANCELLED
116-
reservation.status = OrderStatus.CANCELLED
136+
await cancel_reservation_by_order_and_return_stock(session, order_id)
117137
await session.commit()
118138
return order

0 commit comments

Comments
 (0)