Skip to content

Commit

Permalink
feat(backend): Replace interceptors with calls to ports
Browse files Browse the repository at this point in the history
  • Loading branch information
BartlomiejRasztabiga committed Oct 7, 2023
1 parent e70ac83 commit b67263c
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 219 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import me.rasztabiga.thesis.delivery.domain.command.command.DeliverDeliveryComma
import me.rasztabiga.thesis.delivery.domain.command.command.PickupDeliveryCommand
import me.rasztabiga.thesis.delivery.domain.command.command.RejectDeliveryOfferCommand
import me.rasztabiga.thesis.delivery.domain.command.port.CalculateDeliveryFeePort
import me.rasztabiga.thesis.delivery.domain.command.port.CourierOnlineVerifierPort
import me.rasztabiga.thesis.delivery.domain.command.port.OrderPreparedVerifierPort
import me.rasztabiga.thesis.shared.domain.command.command.CreateOrderDeliveryOfferCommand
import me.rasztabiga.thesis.shared.domain.command.event.OrderDeliveryAcceptedEvent
Expand Down Expand Up @@ -52,8 +53,11 @@ class OrderDelivery {
}

@CommandHandler
fun handle(command: RejectDeliveryOfferCommand) {
fun handle(command: RejectDeliveryOfferCommand, courierOnlineVerifierPort: CourierOnlineVerifierPort) {
require(this.status == DeliveryStatus.OFFER) { "Delivery can be rejected only if it's in OFFER status." }
require(courierOnlineVerifierPort.isCourierOnline(this.courierId!!)) {
"Delivery can be rejected only if the courier is online."
}

apply(
OrderDeliveryRejectedEvent(
Expand All @@ -64,8 +68,11 @@ class OrderDelivery {
}

@CommandHandler
fun handle(command: AcceptDeliveryOfferCommand) {
fun handle(command: AcceptDeliveryOfferCommand, courierOnlineVerifierPort: CourierOnlineVerifierPort) {
require(this.status == DeliveryStatus.OFFER) { "Delivery can be accepted only if it's in OFFER status." }
require(courierOnlineVerifierPort.isCourierOnline(this.courierId!!)) {
"Delivery can be accepted only if the courier is online."
}

apply(
OrderDeliveryAcceptedEvent(
Expand All @@ -77,14 +84,21 @@ class OrderDelivery {
}

@CommandHandler
fun handle(command: PickupDeliveryCommand, orderPreparedVerifierPort: OrderPreparedVerifierPort) {
fun handle(
command: PickupDeliveryCommand,
orderPreparedVerifierPort: OrderPreparedVerifierPort,
courierOnlineVerifierPort: CourierOnlineVerifierPort
) {
require(this.status == DeliveryStatus.ACCEPTED) { "Delivery can be picked up only if it's in ACCEPTED status." }
require(this.courierId == command.courierId) {
"Delivery can be picked up only by the courier who accepted it."
}
require(orderPreparedVerifierPort.isOrderPrepared(this.orderId)) {
"Delivery can be picked up only if the order is prepared."
}
require(courierOnlineVerifierPort.isCourierOnline(this.courierId!!)) {
"Delivery can be picked up only if the courier is online."
}

apply(
OrderDeliveryPickedUpEvent(
Expand All @@ -96,13 +110,16 @@ class OrderDelivery {
}

@CommandHandler
fun handle(command: DeliverDeliveryCommand) {
fun handle(command: DeliverDeliveryCommand, courierOnlineVerifierPort: CourierOnlineVerifierPort) {
require(this.status == DeliveryStatus.PICKED_UP) {
"Delivery can be delivered only if it's in PICKED_UP status."
}
require(this.courierId == command.courierId) {
"Delivery can be delivered only by the courier who picked it up."
}
require(courierOnlineVerifierPort.isCourierOnline(this.courierId!!)) {
"Delivery can be delivered only if the courier is online."
}

apply(
OrderDeliveryDeliveredEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package me.rasztabiga.thesis.delivery.domain.command.command
import org.axonframework.modelling.command.TargetAggregateIdentifier
import java.util.UUID

// TODO verify that restaurant order is prepared
data class PickupDeliveryCommand(
@TargetAggregateIdentifier val id: UUID,
val courierId: String
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package me.rasztabiga.thesis.delivery.domain.command.port

interface CourierOnlineVerifierPort {

fun isCourierOnline(courierId: String): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package me.rasztabiga.thesis.delivery.infrastructure.axon

import me.rasztabiga.thesis.delivery.domain.command.port.CourierOnlineVerifierPort
import me.rasztabiga.thesis.shared.adapter.`in`.rest.api.CourierResponse
import me.rasztabiga.thesis.shared.domain.query.query.FindCourierByIdQuery
import org.axonframework.messaging.responsetypes.ResponseTypes
import org.axonframework.queryhandling.QueryGateway
import org.springframework.stereotype.Service

@Service
class AxonCourierOnlineVerifierAdapter(
private val queryGateway: QueryGateway
) : CourierOnlineVerifierPort {

override fun isCourierOnline(courierId: String): Boolean {
val courier = queryGateway.query(
FindCourierByIdQuery(courierId),
ResponseTypes.instanceOf(CourierResponse::class.java)
).join()
return courier.availability == CourierResponse.Availability.ONLINE
}
}
3 changes: 2 additions & 1 deletion frontend/app/models/order.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ export interface OrderResponse {
restaurantId: string;
restaurantLocation: Location;
deliveryLocation: Location;
deliveryFee: number;
userId: string;
status: OrderStatus;
items: OrderItemResponse[];
total: number;
itemsTotal: number;
paymentId: string;
paymentSessionUrl: string;
}
Expand Down
22 changes: 9 additions & 13 deletions frontend/app/routes/ordering.orders.$orderId.payment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,25 +88,21 @@ export default function OrderPaymentPage() {
</div>
);
})}
<div>
<p className="flex-grow">
{" "}
1x Delivery fee, {data.order.deliveryFee} PLN
</p>
</div>
</div>
<hr className="my-4" />
<div>
<p className="flex-grow">Total: {data.order.total} PLN</p>
</div>
<hr className="my-4" />
<div>
<p className="flex-grow">Choose payment method</p>
<select
className="select select-bordered w-full max-w-xs"
name="address"
>
<option value="1">Stripe (fake)</option>
</select>
<p className="flex-grow font-bold">Total: {data.order.itemsTotal + data.order.deliveryFee} PLN</p>
</div>
<hr className="my-4" />
<div>
<p className="flex-grow">Selected delivery address</p>
<span>TODO</span>
<p className="flex-grow font-bold">Selected delivery address</p>
<span>{data.order.deliveryLocation.streetAddress}</span>
</div>
<hr className="my-4" />
{error && (
Expand Down
1 change: 1 addition & 0 deletions frontend/app/routes/ordering.restaurants.$restaurantId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export default function RestaurantPage() {
);
})}
<p className="text-lg text-center">Total: {orderSum} PLN</p>
<p className="text-sm text-center">+ delivery fee</p>
<Form method="post">
<select
className="select select-bordered w-full max-w-xs"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package me.rasztabiga.thesis.order.adapter.out.axon

import me.rasztabiga.thesis.order.domain.command.port.OrderVerificationPort
import me.rasztabiga.thesis.shared.adapter.`in`.rest.api.RestaurantResponse
import me.rasztabiga.thesis.shared.adapter.`in`.rest.api.UserResponse
import me.rasztabiga.thesis.shared.domain.query.query.FindRestaurantByIdQuery
import me.rasztabiga.thesis.shared.domain.query.query.FindUserByIdQuery
import org.axonframework.messaging.responsetypes.ResponseTypes
import org.axonframework.queryhandling.QueryGateway
import org.springframework.stereotype.Service
import java.util.*

@Service
class AxonOrderVerificationAdapter(
private val queryGateway: QueryGateway
) : OrderVerificationPort {
override fun restaurantExists(restaurantId: UUID): Boolean {
val restaurant = queryGateway.query(
FindRestaurantByIdQuery(
restaurantId,
),
ResponseTypes.optionalInstanceOf(RestaurantResponse::class.java)
).get()

return restaurant.isPresent
}

override fun isRestaurantOpen(restaurantId: UUID): Boolean {
val restaurant = queryGateway.query(
FindRestaurantByIdQuery(
restaurantId,
),
ResponseTypes.instanceOf(RestaurantResponse::class.java)
).get()

return restaurant.availability == RestaurantResponse.Availability.OPEN
}

override fun userExists(userId: String): Boolean {
val user = queryGateway.query(
FindUserByIdQuery(
userId,
),
ResponseTypes.optionalInstanceOf(UserResponse::class.java)
).get()

return user.isPresent
}

override fun productExists(productId: UUID, restaurantId: UUID): Boolean {
val restaurant = queryGateway.query(
FindRestaurantByIdQuery(
restaurantId,
),
ResponseTypes.instanceOf(RestaurantResponse::class.java)
).get()

return restaurant.menu.any { it.id == productId }
}

override fun deliveryAddressExists(deliveryAddressId: UUID, userId: String): Boolean {
val user = queryGateway.query(
FindUserByIdQuery(
userId,
),
ResponseTypes.instanceOf(UserResponse::class.java)
).get()

return user.deliveryAddresses.any { it.id == deliveryAddressId }
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import me.rasztabiga.thesis.order.domain.command.command.CancelOrderCommand
import me.rasztabiga.thesis.order.domain.command.command.DeleteOrderItemCommand
import me.rasztabiga.thesis.order.domain.command.command.FinalizeOrderCommand
import me.rasztabiga.thesis.order.domain.command.command.StartOrderCommand
import me.rasztabiga.thesis.order.domain.command.port.OrderVerificationPort
import me.rasztabiga.thesis.shared.domain.command.command.MarkOrderAsPaidCommand
import me.rasztabiga.thesis.shared.domain.command.command.RejectOrderCommand
import me.rasztabiga.thesis.shared.domain.command.event.OrderCanceledEvent
import me.rasztabiga.thesis.shared.domain.command.event.OrderFinalizedEvent
import me.rasztabiga.thesis.shared.domain.command.event.OrderItemAddedEvent
import me.rasztabiga.thesis.shared.domain.command.event.OrderItemDeletedEvent
import me.rasztabiga.thesis.shared.domain.command.event.OrderPaidEvent
import me.rasztabiga.thesis.shared.domain.command.event.OrderRejectedEvent
import me.rasztabiga.thesis.shared.domain.command.event.OrderStartedEvent
import me.rasztabiga.thesis.shared.domain.command.event.OrderPaidEvent
import org.axonframework.commandhandling.CommandHandler
import org.axonframework.eventsourcing.EventSourcingHandler
import org.axonframework.modelling.command.AggregateIdentifier
Expand All @@ -39,7 +40,17 @@ internal class Order {
private constructor()

@CommandHandler
constructor(command: StartOrderCommand) {
constructor(command: StartOrderCommand, orderVerificationPort: OrderVerificationPort) {
require(orderVerificationPort.restaurantExists(command.restaurantId)) {
"Restaurant with id ${command.restaurantId} does not exist"
}
require(orderVerificationPort.isRestaurantOpen(command.restaurantId)) {
"Restaurant with id ${command.restaurantId} is closed"
}
require(orderVerificationPort.userExists(command.userId)) {
"User with id ${command.userId} does not exist"
}

apply(
OrderStartedEvent(
orderId = command.orderId,
Expand Down Expand Up @@ -97,9 +108,18 @@ internal class Order {
}

@CommandHandler
fun handle(command: FinalizeOrderCommand) {
fun handle(command: FinalizeOrderCommand, orderVerificationPort: OrderVerificationPort) {
require(this.userId == command.userId) { "Order can be finalized only by the user who created it." }
require(this.status == OrderStatus.CREATED) { "Order can be finalized only if it's in CREATED status." }
require(orderVerificationPort.deliveryAddressExists(command.deliveryAddressId, this.userId)) {
"Delivery address with id ${command.deliveryAddressId} does not exist for user with id ${this.userId}"
}

this.items.forEach {
require(orderVerificationPort.productExists(it.productId, this.restaurantId)) {
"Product with id ${it.productId} does not exist in restaurant with id ${this.restaurantId}"
}
}

apply(
OrderFinalizedEvent(
Expand Down
Loading

0 comments on commit b67263c

Please sign in to comment.