Domain Events with DotNetCommander

In my first article for DotnetCommander, I briefly described the basic usage. In this article, I will show you how to use the Domain Events.

Domain events are used for communicating changes to other parts of your same domain so that they can react to it.

For example; If you are changing the stock level of a product (e.g. from available to out of stock), you may want to raise a domain event to trigger re-ordering it from your supplier. And that's exactly what a domain event is. communicating changes within your bounded context

I will use this same scenario to demonstrate the domain events in the context of DotNetCommander.

I intentionally keep the business logic simple, since I want to keep the focus on domain events.

We are going to start off with a request object. We can use this object to initiate a request to place an order. It is a simple POCO with a single property Name that represents the name of the product.

public class OrderRequest  
{
    public OrderRequest(string name)
    {
        Name = name;
    }

    public string Name { get; }
}

I have defined two domain objects for this example. Product and Order. Order contains the product that's being ordered.

public class Order : AggregateRoot  
{
    public Order(long id, Product product)
    {
        Id = id;
        Product = product;
    }

    public Product Product { get; }    
}
public class Product : AggregateRoot  
{
    public Product(long id, string name, string description, decimal price)
    {
        Id = id;
        Name = name;
        Description = description;
        Price = price;
    }

    public string Name { get; }
    public string Description { get; }
    public decimal Price { get; }
}

And I am also going to define two commands. OrderCommand and ReSupplyProductCommand.

OrderCommand is where we handle creating the order and trigger an event (domain event) if the product is out of stock so that we can re-order it from our supplier.

public class OrderCommand : Command<OrderRequest, Order>  
{
    public static OrderCommand Instance() => new OrderCommand(new MockRepo());

    public OrderCommand(MockRepo repo)
    {
        Repo = repo;
    }

    public MockRepo Repo { get; }

    public override Order Handle()
    {
        var product = Repo.GetProductInformation(Request.Name);
        int quantity = Repo.GetProductInventory(product.Id);

        if (quantity < 1)
        {
            this.RaiseEvent<ReSupplyProductEvent>(new ReSupplyProductEvent(product.Id, true, 10));

            throw new Exception($"{product.Name} is on backorder. Please try again later.");
        }
        else
        {
            var order = Repo.CreateOrder(Repo.AssignOrderId(), product);
        }

        return order;
    }
}

ReSupplyProductCommand handles the event triggered from the OrderCommand. Notice that I need to call HandleEvent<T> in the Init method to listen for the ReSupplyProductEvent. And I call the GetEvent<T> in the Handle method to retrieve the event itself.

This command only gets executed, when the ReSupplyProductEvent event is triggered.

public class ReSupplyProductCommand : Command<OrderRequest, Order>  
{
    public static ReSupplyProductCommand Instance() => new ReSupplyProductCommand(new MockRepo());

    public ReSupplyProductCommand(MockRepo repo)
    {
        Repo = repo;
    }

    // We tell this command to monitor for ReSupplyProductEvent event.
    public override void Init() => HandleEvent<ReSupplyProductEvent>();

    public MockRepo Repo { get; }

    public override Order Handle()
    {
        // Get event details
        var supplyEvent = this.GetEvent<ReSupplyProductEvent>();

        if (supplyEvent.Order)
        {
            Repo.ReSupplyProduct(Model.Id, true, 10);
        }

        return Model;
    }
}

Here is what the event object looks like. Simple POCO that implements a marker interface IEvent

public class ReSupplyProductEvent : IEvent  
{
    public ReSupplyProductEvent(long productId, bool order, int quantity)
    {
        Order = order;
        Quantity = quantity;
        ProductId = productId;
    }

    public long ProductId { get; }
    public bool Order { get; }
    public int Quantity { get; }
}

Mock Repo object I used for demonstration.

public class MockRepo  
{
    public long AssignOrderId() => 1;

    public Order CreateOrder(long orderId, Product product) => new Order(orderId, product);

    public int GetProductInventory(long productId) => 1;

    public Product GetProductInformation(string name) => new Product(1, "Test", "Description", 12.99m);    

    public void ReSupplyProduct(long productId, bool order, int quantity)  {}
}

Let's run the Commander...

void Main()  
{
    var orderRequest = new OrderRequest("Test");

    Commander<OrderRequest, Order> commander = new Commander<OrderRequest, Order>(orderRequest);

    var order = commander.Execute(OrderCommand.Instance(),
        ReSupplyProductCommand.Instance());
}

That's all there is to it. Hope you enjoyed it. You can always reach out to me if you have any questions.

Arif Yayalar

Software Engineer

Seattle, WA

Subscribe to {:arif, :yayalar}

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!