public async Task <bool> TryHandleAsync(UserCheckoutAcceptedIntegrationEvent @event) { var result = false; // Send Integration event to clean basket once basket is converted to Order and before starting with the order creation process var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(@event.UserId); _orderingIntegrationEventService.PublishThroughEventBus(orderStartedIntegrationEvent); if (@event.RequestId != Guid.Empty) { var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street, @event.State, @event.Country, @event.ZipCode, @event.CardNumber, @event.CardHolderName, @event.CardExpiration, @event.CardSecurityNumber, @event.CardTypeId); var requestCreateOrder = new IdentifiedCommand <CreateOrderCommand, bool>(createOrderCommand, @event.RequestId); try { result = await _mediator.Send(requestCreateOrder); } catch (Exception ex) { _logger.LogCritical(ex.ToString()); } } _logger.LogInformation(result ? $"UserCheckoutAccepted integration event has been received and a create new order process is started with requestId: {@event.RequestId}" : $"UserCheckoutAccepted integration event has been received but a new order process has failed with requestId: {@event.RequestId}"); return(true); }
public async Task <bool> ProcessPayment(OrderCustomer order, ExecuteOrderCommand message) { var orderStarted = new OrderStartedIntegrationEvent { OrderId = order.Id, CustomerId = order.CustomerId, TotalValue = order.TotalValue, PaymentMethod = 1, // fixed. Change if you have more types CardHolder = message.CardHolder, CardNumber = message.CardNumber, CardExpiration = message.CardExpiration, CardCvv = message.CardCvv }; var result = await _bus .RequestAsync <OrderStartedIntegrationEvent, ResponseMessage>(orderStarted); if (result.ValidationResult.IsValid) { return(true); } foreach (var error in result.ValidationResult.Errors) { AddErrors(error.ErrorMessage); } return(false); }
private void HandleMessage(OrderStartedIntegrationEvent newOrderModel) { using (var scope = _serviceScopeFactory.CreateScope()) { try { _productService = scope.ServiceProvider.GetRequiredService <IProductServiceAPI>(); bool result = false; foreach (var product in newOrderModel.Order_Detail) { var temp = _productService.UpdateQuantity(product.id, product.quantity); result = !result ? temp || result : temp && result; } if (result) { var eventData = new OrderFulfilledEvent { OrderId = newOrderModel.OrderId, isFulFill = true }; var json = JsonConvert.SerializeObject(eventData); var body = Encoding.UTF8.GetBytes(json); _channel.BasicPublish("OrderFulfilledEvent", "OrderFulfilledEvent", null, body); } } catch (Exception ex) { } } }
public async Task <bool> Handle(CreateOrderCommand message, CancellationToken cancellationToken) { // Add Integration event to clean the basket var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(message.UserId); await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStartedIntegrationEvent); // Add/Update the Buyer AggregateRoot // DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root // methods and constructor so validations, invariants and business logic // make sure that consistency is preserved across the whole aggregate var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode); var order = new Order(message.UserId, message.UserName, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration); foreach (var item in message.OrderItems) { order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); } _logger.LogInformation("----- Creating Order - Order: {@Order}", order); _orderRepository.Add(order); return(await _orderRepository.UnitOfWork .SaveEntitiesAsync()); }
/// <summary> /// Integration event handler which starts the create order process /// </summary> /// <param name="eventMsg"> /// Integration event message which is sent by the /// basket.api once it has successfully process the /// order items. /// </param> /// <returns></returns> public async Task Handle(UserCheckoutAcceptedIntegrationEvent eventMsg) { var result = false; // Send Integration event to clean basket once basket is converted to Order and before starting with the order creation process var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(eventMsg.UserId); await _orderingIntegrationEventService.PublishThroughEventBusAsync(0, "Order Started", orderStartedIntegrationEvent); if (eventMsg.RequestId != Guid.Empty) { var createOrderCommand = new CreateOrderCommand(eventMsg.Basket.Items, eventMsg.UserId, eventMsg.UserName, eventMsg.City, eventMsg.Street, eventMsg.State, eventMsg.Country, eventMsg.ZipCode, eventMsg.CardNumber, eventMsg.CardHolderName, eventMsg.CardExpiration, eventMsg.CardSecurityNumber, eventMsg.CardTypeId); var requestCreateOrder = new IdentifiedCommand <CreateOrderCommand, bool>(createOrderCommand, eventMsg.RequestId); result = await _mediator.Send(requestCreateOrder); } _logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler)) .LogTrace(result ? $"UserCheckoutAccepted integration event has been received and a create new order process is started with requestId: {eventMsg.RequestId}" : $"UserCheckoutAccepted integration event has been received but a new order process has failed with requestId: {eventMsg.RequestId}"); }
public async Task Consume(ConsumeContext <UserCheckoutAcceptedIntegrationEvent> context) { IExecutionResult result = ExecutionResult.Failed(); var orderId = OrderId.New; // Send Integration event to clean basket once basket is converted to Order and before starting with the order creation process var orderItems = from orderItem in context.Message.Basket.Items select new OrderStockItem(int.Parse(orderItem.ProductId), orderItem.Quantity); if (context.Message.RequestId != Guid.Empty) { var createOrderCommand = new CreateOrderCommand(orderId, new SourceId(context.Message.RequestId.ToString()), context.Message.Basket.Items, context.Message.UserId, context.Message.UserName, context.Message.City, context.Message.Street, context.Message.State, context.Message.Country, context.Message.ZipCode, context.Message.CardNumber, context.Message.CardHolderName, context.Message.CardExpiration, context.Message.CardSecurityNumber, context.Message.CardTypeId); result = await _commandBus.PublishAsync(createOrderCommand, CancellationToken.None).ConfigureAwait(false); if (result.IsSuccess) { var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(context.Message.UserId, orderId.Value, orderItems.ToList(), context.Message.RequestId); await _endpoint.Publish(orderStartedIntegrationEvent); } } }
public async Task Handle(PaymentMethodVerifiedDomainEvent paymentMethodVerifiedDomainEvent) { var order = await _orderRepository.GetAsync(paymentMethodVerifiedDomainEvent.OrderId); order.SetBuyerId(paymentMethodVerifiedDomainEvent.Buyer.Id); order.SetPaymentMethodId(paymentMethodVerifiedDomainEvent.PaymentMethod.Id); // Publish the new ordering integration event var items = new List <OrderItemInfo>(); order.OrderItems.ToList().ForEach((item) => { items.Add(new OrderItemInfo() { ProductId = item.ProductId, Assets = item.Units }); }); var integrationEvent = new OrderStartedIntegrationEvent( Guid.NewGuid(), paymentMethodVerifiedDomainEvent.Buyer.IdentityGuid, DateTime.UtcNow, items); await _orderingIntegrationEventService.PublishThroughEventBusAsync(integrationEvent); await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(); _logger.CreateLogger(nameof(UpdateOrderOnPaymentMethodVerifiedEventHandler)) .LogTrace($"Order with Id: {paymentMethodVerifiedDomainEvent.OrderId} has been successfully updated with a payment method id: { paymentMethodVerifiedDomainEvent.PaymentMethod.Id }"); }
public ActionResult <IEnumerable <string> > Get() { var eventMessage = new OrderStartedIntegrationEvent("Hello Test using RabbbitMQ Event Bus"); string response = _eventBus.PublishRpc(eventMessage); //_eventBus.Publish(eventMessage); return(new string[] { response }); }
public async Task Handle(OrderStartedIntegrationEvent @event) { using (LogContext.PushProperty("IntegrationEventContext", $"{Program.AppName}")) { _logger.LogInformation("----- Handling integration event: {AppName} - ({@IntegrationEvent})", Program.AppName, @event); await _repository.DeleteBasketAsync(@event.UserId.ToString()); } }
private async Task RemoveBasket(OrderStartedIntegrationEvent message) { using var scope = _serviceProvider.CreateScope(); var repository = scope.ServiceProvider.GetRequiredService <IBasketRepository>(); var basket = await repository.GetByCustomerId(message.CustomerId); if (basket != null) { await repository.Delete(message.CustomerId); } }
public async Task Handle(OrderStartedIntegrationEvent message, IMessageHandlerContext context) { Data.UserId = message.UserId; // We'll do the following actions at the same time, but asynchronous // - Grace Period // - ~~Verify buyer and payment method~~ // - Verify if there is stock available var @event = new OrderStatusChangedToAwaitingValidationIntegrationEvent(message.OrderId, message.OrderedItems); await context.Publish(@event); await RequestTimeout <GracePeriodExpired>(context, TimeSpan.FromMinutes(settings.GracePeriodTime)); }
public async Task Handle_DeleteBasket( [Frozen] Mock <IBasketRepository> mockBasketRepository, OrderStartedIntegrationEventHandler sut, OrderStartedIntegrationEvent orderStartedEvent ) { //Arrange //Act await sut.Handle(orderStartedEvent); //Assert mockBasketRepository.Verify(_ => _.DeleteBasketAsync( It.IsAny <string>() )); }
public async Task <CreateOrderResponse> CreateOrder(CreateOrderRequest request) { var orderItems = request.CartItems.Select(item => new OrderItem( item.OfferId, item.OfferName, item.Quantity, item.PricePerItem, item.ImageUri)).ToList(); var order = new Order(request.UserId, request.SellerId, orderItems); _orderRepository.Add(order); await _orderRepository.UnitOfWork.SaveChangesAndDispatchDomainEventsAsync(); var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent { OrderId = order.Id, OrderItems = order.OrderItems.Select(orderItem => new OrderItemDto { OfferId = orderItem.OfferId, Quantity = orderItem.Quantity }).ToList() }; await _eventBus.PublishAsync(orderStartedIntegrationEvent); _logger.LogInformation($"Integration event {nameof(OrderStartedIntegrationEvent)} published", "EventId".ToKvp(orderStartedIntegrationEvent.Id), "OfferIds".ToKvp(string.Join(",", orderStartedIntegrationEvent.OrderItems.Select(x => x.OfferId)))); var notificationIntegrationEvent = new NotificationIntegrationEvent { UserId = order.SellerId, Code = NotificationCodes.OrderStarted, Message = "Order started", Metadata = new Dictionary <string, string> { { "OrderId", order.Id.ToString() }, { "BuyerId", order.BuyerId.ToString() }, { "OfferIds", string.Join(",", request.CartItems.Select(x => x.OfferId)) } } }; await _eventBus.PublishAsync(notificationIntegrationEvent); _logger.LogInformation($"Published {nameof(NotificationIntegrationEvent)}", "EventId".ToKvp(orderStartedIntegrationEvent.Id), "PublishedEventId".ToKvp(notificationIntegrationEvent.Id), "NotifiedUserId".ToKvp(notificationIntegrationEvent.UserId)); return(new CreateOrderResponse { OrderId = order.Id }); }
/// <summary> /// Integration event handler which starts the create order process /// </summary> /// <param name="message"> /// Integration event message which is sent by the /// basket.api once it has successfully process the /// order items. /// </param> /// <param name="context"></param> /// <returns></returns> public async Task Handle(UserCheckoutAcceptedIntegrationEvent message, IMessageHandlerContext context) { // Store immediately in database // Normally we'd do a command, store the data into the database and fire an event. // This event would then start the saga. Instead, we first store the data from the other servce. // Then we fire a new event to start the saga. var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode); var order = new Order(message.UserId, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration); var allItems = new List <OrderStockItem>(); foreach (var item in message.Basket.Items) { var productId = int.TryParse(item.ProductId, out int id) ? id : -1; var discount = 0; order.AddOrderItem(productId, item.ProductName, item.UnitPrice, discount, item.PictureUrl, item.Quantity); allItems.Add(new OrderStockItem(productId, item.Quantity)); } _orderRepository.Add(order); await _orderRepository.UnitOfWork.SaveEntitiesAsync(); // We'll use this to uniquely identify the order for orchestrating the grace period. var orderId = order.Id; // Instead of a domain event, we'll use an asynchronous message var cmd = new VerifyBuyerAndPaymentCommand() { OrderId = orderId, UserId = message.UserId, CardTypeId = message.CardTypeId, CardNumber = message.CardNumber, CardSecurityNumber = message.CardSecurityNumber, CardHolderName = message.CardHolderName, CardExpiration = message.CardExpiration, }; await context.SendLocal(cmd); // Send Integration event to clean basket once basket is converted to Order and before starting with the order creation process var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(message.UserId, orderId, allItems); await context.Publish(orderStartedIntegrationEvent); _logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler)) .LogTrace("UserCheckoutAccepted integration event has been received and a create new order process is started with requestId: {message.RequestId}"); }
private async Task <ResponseMessage> AuthorizePayment(OrderStartedIntegrationEvent message) { using var scope = _serviceProvider.CreateScope(); var paymentService = scope.ServiceProvider.GetRequiredService <IPaymentService>(); var payment = new PaymentInfo { OrderId = message.OrderId, PaymentMehtod = (PaymentMehtod)message.PaymentMethod, TotalValue = message.TotalValue, CreditCard = new CreditCard( message.CardHolder, message.CardNumber, message.CardExpiration, message.CardCvv) }; var response = await paymentService.AuthorizePayment(payment); return(response); }
public async Task <bool> Handle(CreateSalesOrderCommand request, CancellationToken cancellationToken) { logger.LogInformation("----- Creating sales order"); logger.LogInformation("----- Creating OrderStarted domain event"); // Add Integration event to clean the basket var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(request.UserId); await salesOrderIntegrationEventService.AddAndSaveEventAsync(orderStartedIntegrationEvent); var customer = await GetCustomer(request.CustomerNumber); var creditCard = await GetCreditCard( request.CardType, request.CardNumber, request.CardExpiration ); var billToAddress = mapper.Map <Address>(request.BillToAddress); var shipToAddress = mapper.Map <Address>(request.ShipToAddress); var salesOrder = new SalesOrder(request.UserId, request.UserName, customer, request.ShipMethod, billToAddress, shipToAddress, creditCard, request.CardSecurityNumber, request.CardHolderName); foreach (var item in request.OrderItems) { var specialOfferProduct = await specialOfferProductRepository .GetBySpecAsync( new GetSpecialOfferProductSpecification(item.ProductNumber), cancellationToken ); Guard.Against.Null(specialOfferProduct, nameof(specialOfferProduct)); salesOrder.AddOrderLine(item.ProductNumber, item.ProductName, item.UnitPrice, item.Discount, specialOfferProduct, item.Quantity); } logger.LogInformation("----- Saving sales order to database - Sales Order: {@SalesOrder}", salesOrder); await salesOrderRepository.AddAsync(salesOrder, cancellationToken); logger.LogInformation("Sales order was created succesfully"); return(true); }
// Domain Logic comment: // When the Buyer and Buyer's payment method have been created or verified that they existed, // then we can update the original Order with the BuyerId and PaymentId (foreign keys) public async Task Handle(BuyerAndPaymentMethodVerifiedDomainEvent buyerPaymentMethodVerifiedEvent) { var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId); orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id); orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(buyerPaymentMethodVerifiedEvent.Buyer.IdentityGuid); // Using a local transaction to achieve atomicity between original Ordering database operation and // the IntegrationEventLog. Only saving event if order has been successfully persisted to db await _orderingIntegrationEventService .SaveEventAndOrderingContextChangesAsync(orderStartedIntegrationEvent); // Publish ordering integration event and mark it as published await _orderingIntegrationEventService .PublishThroughEventBusAsync(orderStartedIntegrationEvent); _logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler)) .LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }"); }
/// <summary> /// Integration event handler which starts the create order process /// </summary> /// <param name="message"> /// Integration event message which is sent by the /// basket.api once it has successfully process the /// order items. /// </param> /// <param name="context"></param> /// <returns></returns> public async Task Handle(UserCheckoutAcceptedIntegrationEvent message, IMessageHandlerContext context) { var result = false; // Send Integration event to clean basket once basket is converted to Order and before starting with the order creation process var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(message.UserId); await context.Publish(orderStartedIntegrationEvent); if (message.RequestId != Guid.Empty) { var createOrderCommand = new CreateOrderCommand(message.Basket.Items, message.UserId, message.City, message.Street, message.State, message.Country, message.ZipCode, message.CardNumber, message.CardHolderName, message.CardExpiration, message.CardSecurityNumber, message.CardTypeId); var requestCreateOrder = new IdentifiedCommand <CreateOrderCommand, bool>(createOrderCommand, message.RequestId); result = await _mediator.Send(requestCreateOrder); } _logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler)) .LogTrace(result ? $"UserCheckoutAccepted integration event has been received and a create new order process is started with requestId: {message.RequestId}" : $"UserCheckoutAccepted integration event has been received but a new order process has failed with requestId: {message.RequestId}"); }
public async Task <bool> Handle(CreateOrderCommand command, CancellationToken cancellationToken) { // Add Integration event to clean the basket var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(command.UserId); _eventBus.Publish(orderStartedIntegrationEvent); var address = new Address(command.Street, command.City, command.State, command.Country, command.ZipCode); var order = new Order(command.UserId, command.UserName, address, (int)command.CardType, command.CardNumber, command.CardSecurityNumber, command.CardHolderName, command.CardExpiration, command.Currency, command.CurrencyRate); foreach (var item in command.OrderItems) { order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.ISBN13, item.Units); } _orderingContext.Orders.Add(order); await _orderingContext.SaveEntitiesAsync(); return(true); }
public async Task CheckoutAsync(CheckoutBasketServiceModel serviceModel) { var order = new Order { OrderStateId = OrderStatesConstants.NewId, OrderStatusId = OrderStatusesConstants.NewId, ClientId = serviceModel.ClientId, ClientName = serviceModel.ClientName, SellerId = serviceModel.SellerId, BillingAddressId = serviceModel.BillingAddressId, BillingCity = serviceModel.BillingCity, BillingCompany = serviceModel.BillingCompany, BillingCountryCode = serviceModel.BillingCountryCode, BillingFirstName = serviceModel.BillingFirstName, BillingLastName = serviceModel.BillingLastName, BillingPhone = serviceModel.BillingPhone, BillingPhonePrefix = serviceModel.BillingPhonePrefix, BillingPostCode = serviceModel.BillingPostCode, BillingRegion = serviceModel.BillingRegion, BillingStreet = serviceModel.BillingStreet, ShippingAddressId = serviceModel.ShippingAddressId, ShippingCity = serviceModel.ShippingCity, ShippingCompany = serviceModel.ShippingCompany, ShippingCountryCode = serviceModel.ShippingCountryCode, ShippingFirstName = serviceModel.ShippingFirstName, ShippingLastName = serviceModel.ShippingLastName, ShippingPhone = serviceModel.ShippingPhone, ShippingPhonePrefix = serviceModel.ShippingPhonePrefix, ShippingPostCode = serviceModel.ShippingPostCode, ShippingRegion = serviceModel.ShippingRegion, ShippingStreet = serviceModel.ShippingStreet, ExternalReference = serviceModel.ExternalReference, ExpectedDeliveryDate = serviceModel.ExpectedDeliveryDate, MoreInfo = serviceModel.MoreInfo, IpAddress = serviceModel.IpAddress }; this.context.Orders.Add(order.FillCommonProperties()); foreach (var basketItem in serviceModel.Items) { var orderItem = new OrderItem { OrderId = order.Id, ProductId = basketItem.ProductId.Value, ProductSku = basketItem.ProductSku, ProductName = basketItem.ProductName, PictureUrl = basketItem.PictureUrl, Quantity = basketItem.Quantity, ExternalReference = basketItem.ExternalReference, ExpectedDeliveryFrom = basketItem.ExpectedDeliveryFrom, ExpectedDeliveryTo = basketItem.ExpectedDeliveryTo, MoreInfo = basketItem.MoreInfo }; this.context.OrderItems.Add(orderItem.FillCommonProperties()); } await this.context.SaveChangesAsync(); var message = new OrderStartedIntegrationEvent { BasketId = serviceModel.BasketId }; this.eventBus.Publish(message); }
public async Task OrderStarted(OrderStartedIntegrationEvent @event) { var handler = _serviceProvider.GetRequiredService <OrderStartedIntegrationEventHandler>(); await handler.Handle(@event); }