Last active
June 3, 2023 06:55
-
-
Save chuasonglin1995/95a8ecc510dce6f6e0f7c2e36a761ce5 to your computer and use it in GitHub Desktop.
Domain Event Publishers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DomainEvent } from 'path-to-DomainEvent'; | |
interface DomainEventPublisher { | |
publish(aggregateType: string, aggregateId: any, domainEvents: DomainEvent[]): void; | |
} | |
abstract class AbstractAggregateDomainEventPublisher<A, E extends DomainEvent> { | |
private idSupplier: (aggregate: A) => any; | |
private eventPublisher: DomainEventPublisher; | |
private aggregateType: new () => A; | |
protected constructor( | |
eventPublisher: DomainEventPublisher, | |
aggregateType: new () => A, | |
idSupplier: (aggregate: A) => any | |
) { | |
this.eventPublisher = eventPublisher; | |
this.aggregateType = aggregateType; | |
this.idSupplier = idSupplier; | |
} | |
public publish(aggregate: A, events: E[]): void { | |
this.eventPublisher.publish(this.aggregateType, this.idSupplier(aggregate), events as DomainEvent[]); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface IDomainEvent<T> { | |
queue: string; | |
idempotencyId: string; | |
event: string; | |
data: T; | |
} | |
interface IBankingTransactionCompletedData { | |
userId: string; | |
amount: number; | |
currency: string; | |
} | |
interface IBankingTransactionFailedData { | |
userId: string; | |
failReason: string; | |
} | |
interface IBankingTransactionCompletedEvent extends IDomainEvent<IBankingTransactionCompletedData> {} | |
abstract class AbstractAggregateDomainEventPublisher<E extends IDomainEvent<any>> { | |
private eventPublisher: EventPublisher; | |
constructor() {} | |
public publish(events: E[]): void { | |
this.eventPublisher.publish(events as IDomainEvent<any>[]); | |
} | |
} | |
################################################################################### | |
enum BankingDomainEvents { | |
BankingTransactionCompleted = 'BankingTransactionCompleted', | |
BankingTransactionFailed = 'BankingTransactionFailed', | |
// Add more domain events here... | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Transactional } from 'typeorm'; | |
@Transactional() | |
class OrderService { | |
private orderRepository: OrderRepository; | |
private createOrderSagaManager: SagaManager<CreateOrderSagaState, CreateOrderSagaState>; | |
private reviseOrderSagaManager: SagaManager<ReviseOrderSagaState, ReviseOrderSagaData>; | |
private orderAggregateEventPublisher: OrderDomainEventPublisher; | |
constructor( | |
orderRepository: OrderRepository, | |
createOrderSagaManager: SagaManager<CreateOrderSagaState, CreateOrderSagaState>, | |
reviseOrderSagaManager: SagaManager<ReviseOrderSagaState, ReviseOrderSagaData>, | |
orderAggregateEventPublisher: OrderDomainEventPublisher | |
) { | |
this.orderRepository = orderRepository; | |
this.createOrderSagaManager = createOrderSagaManager; | |
this.reviseOrderSagaManager = reviseOrderSagaManager; | |
this.orderAggregateEventPublisher = orderAggregateEventPublisher; | |
} | |
public createOrder(orderDetails: OrderDetails): Order { | |
const restaurant: Restaurant = this.restaurantRepository.findById(restaurantId) | |
.orElseThrow(() => new RestaurantNotFoundException(restaurantId)); | |
const orderLineItems: OrderLineItem[] = makeOrderLineItems(lineItems, restaurant); | |
const orderAndEvents: ResultWithDomainEvents<Order, OrderDomainEvent> = Order.createOrder( | |
consumerId, | |
restaurant, | |
orderLineItems | |
); | |
const order: Order = orderAndEvents.result; | |
this.orderRepository.save(order); | |
this.orderAggregateEventPublisher.publish(order, orderAndEvents.events); | |
const orderDetails: OrderDetails = new OrderDetails( | |
consumerId, | |
restaurantId, | |
orderLineItems, | |
order.getOrderTotal() | |
); | |
const data: CreateOrderSagaState = new CreateOrderSagaState(order.getId(), orderDetails); | |
this.createOrderSagaManager.create(data, Order, order.getId()); | |
return order; | |
} | |
public reviseOrder(orderId: number, orderRevision: OrderRevision): Order { | |
const order: Order = this.orderRepository.findById(orderId) | |
.orElseThrow(() => new OrderNotFoundException(orderId)); | |
const sagaData: ReviseOrderSagaData = new ReviseOrderSagaData( | |
order.getConsumerId(), | |
orderId, | |
null, | |
orderRevision | |
); | |
this.reviseOrderSagaManager.create(sagaData); | |
return order; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { AbstractAggregateDomainEventPublisher } from 'path-to-AbstractAggregateDomainEventPublisher'; | |
import { TicketDomainEvent } from 'path-to-TicketDomainEvent'; | |
import { DomainEventPublisher } from 'path-to-DomainEventPublisher'; | |
import { Ticket } from 'path-to-Ticket'; | |
class TicketDomainEventPublisher extends AbstractAggregateDomainEventPublisher<Ticket, TicketDomainEvent> { | |
constructor(eventPublisher: DomainEventPublisher) { | |
super(eventPublisher, Ticket, (ticket: Ticket) => ticket.getId()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment