TU Tran

Technologies should serve for business purpose.

NAVIGATION - SEARCH

[TinyERP]Refactoring the architecture/ pattern (Part 3)

Overview

In part 1 and part 2,We know how the order will be created using CQRS pattern.

In real ERP system, there are many other modules need to be updated when new order was created.

For example:

  • Inventory module needs to be updated.
  • Accounting module also needs to be updated.
  • Customer management module needs to be notified in this case.

In general, diagram below describe how does order module notify other modules:

 

From diagram above, Modules communicate each other through message-bus channel, let see how does it work in case of create new order request:

  1. Customer module will subscribe on OnOrderCreated to Message Bus.
  2. Inventory module also subscribes on OnOrderCreated to Message Bus.
  3. Client submits "create new order" request to Api.
  4. Api will redirect request to appropriated handler in Order Module.
  5. After creating new order, Order Module will broadcast appropriated event (it was OnOrderCreated in this case) to all subscribers in system.
  6. OnOrderCreated event was handled to Customer module
  7. OnOrderCreated event was handled to Inventory module

From diagram, we see that we can have more than 1 subscriber for an event. Please aware that each module was deployed as isolated application on IIS

How to get the code

Checkout the code at https://github.com/techcoaching/TinyERP.git

How to implement in TinyERP

1/ Customer module will subscribe on OnOrderCreated to Message Bus.

 look at CRM/App.Customer/CustomerHandler.cs:

namespace App.Customer.Api
{

    [RoutePrefix("api/customers")]
    public class CustomerHandler : RemoteEventSubcriberHandler
    {
        [Route("onOrderCreated")]
        [HttpPost]
        [ResponseWrapper()]
        public bool OnOrderCreated(OnOrderCreated ev)
        {
			/*Business logic for this event*/
        }
        [Route("onCustomerDetailChanged")]
        [HttpPost]
        [ResponseWrapper()]
        public bool OnCustomerDetailChanged(OnCustomerDetailChanged ev)
        {
			/*Business logic for this event*/
        }

        [Route("onOrderLineItemAdded")]
        [HttpPost]
        [ResponseWrapper()]
        public bool OnOrderLineItemAdded(OnOrderLineItemAdded ev)
        {
            /*Business logic for this event*/
        }

    }
}

In this code, we have:

CustomerHandler inherits from RemoteEventSubcriberHandler class, this indicates that this class will listen for remote event and handle it

we have this method declaration "public bool OnOrderCreated(OnOrderCreated ev)". This mean that  this method will handle remote event with type of OnOrderCreated class. So, this event occurred somewhere in the system (in any modules), this handler method will be triggered with appropriated information.

2/ Inventory module also subscribes on OnOrderCreated to Message Bus

We do the same as customer module above

3/ Client submits "create new order" request to Api.

 Let use REST client and simulate request from client for creating new order as below:

  •  We use POST to create new Order
  • Content-Type: specify the format of content will be sent to server in JSON format.
  • Url: This is where the handler will handle this create new order request.
  • Body: Json content

4/ Api will redirect request to appropriated handler in Order Module

Look at "Order/App.Order/Api/OrderHandler.cs":

namespace App.Order.Api
{
    [RoutePrefix("api/orders")]
    public class OrderHandler : CommandHandlerController<OrderAggregate>
    {
        [Route("")]
        [HttpPost()]
        [ResponseWrapper()]
        public void CreateOrder(CreateOrderRequest request)
        {
            this.Execute(request);
        }
    }
}

This handle method will be called when user send create new order request to the order module.

This handler does no thing, just call execute method. this will passing this to appropriate command handler in OrderCommandHandler class:

namespace App.Order.Command
{
    internal class OrderCommandHandler : BaseCommandHandler, IOrderCommandHandler
    {
        public void Handle(CreateOrderRequest command)
        {

            OrderAggregate order = AggregateFactory.Create<OrderAggregate>();
            order.AddCustomerDetail(command.CustomerDetail);
            order.AddOrderLineItems(command.OrderLines);
            using (IUnitOfWork uow = this.CreateUnitOfWork<OrderAggregate>())
            {
                IOrderRepository repository = IoC.Container.Resolve<IOrderRepository>(uow);
                repository.Add(order);
                uow.Commit();
                order.AddEvent(new App.Order.Event.OnOrderCreated(order.Id));
            }
            order.PublishEvents();
        }
    }
}

5/ After creating new order, Order Module will broadcast appropriated event (it was OnOrderCreated in this case) to all subscribers in system.

At the end of method above, we have:

order.PublishEvents();

This will phublish all events generated during creating new order to all subscribers. So which event will be created, we can find more detail in implementation of those methods:

order.AddCustomerDetail(command.CustomerDetail);
order.AddOrderLineItems(command.OrderLines);

 

In AddCustomerDetail:

public void AddCustomerDetail(App.Order.Command.CustomerDetail customerDetail)
{
	this.CustomerDetail = new OrderCustomerDetail(customerDetail.Name);
	this.AddEvent(new App.Order.Event.OnCustomerDetailChanged(this.Id, customerDetail.Name));
}

We generate new OnCustomerDetailChanged event.

In AddOrderLineItems:

public void AddOrderLineItems(IList<App.Order.Command.OrderLine> orderLines)
{
	foreach (App.Order.Command.OrderLine item in orderLines)
	{
		this.AddOrderLineItem(item.ProductId, item.ProductName, item.Quantity, item.Price);
	}
}
public void AddOrderLineItem(Guid productId, string productName, int quantity, decimal price)
{
	OrderLine orderLine = new OrderLine(productId, productName, quantity, price);
	this.OrderLines.Add(orderLine);
	this.AddEvent(new App.Order.Event.OnOrderLineItemAdded(this.Id, productId, productName, quantity, price));
}

We generate new OnOrderLineItemAdded for every order line item was added into order.

At the end of Handle(CreateOrderRequest command) method, we publish those events. So all subcribers will be triggered with appropriated information.

 6/ OnOrderCreated event was handled to Customer module

At the step on, we register event handler for OnOrderCreated in "CRM/App.Customer/Api/CustomerHandler" class.

For OnOrderCreated event, System will call OnOrderCreated(OnOrderCreated ev) method:

[Route("onOrderCreated")]
[HttpPost]
[ResponseWrapper()]
public bool OnOrderCreated(OnOrderCreated ev)
{
	ILogger logger = IoC.Container.Resolve<ILogger>();
	logger.Info("new order created, detail:{0}", JsonHelper.ToJson(ev));
	return true;
}

At the moment, We just catch the event and write to log file. Open log file of CRM project, we see:

7/ OnOrderCreated event was handled to Inventory module

This was the same as Customer module

By this way, 1 event can be notified across the entire system.

For more information about other articles in this series

Thank you for reading,

Note: Please like and share to your friends if you think this is useful article, I really appreciate

 

 

 

 

Add comment