Thursday 26 September 2013

Applied Domain-Driven Design (DDD), Part 2 - Domain Events

In my last post we have addressed DDD thought process and constant refining/re-factoring. In this post we are going to talk about domain events. There are many articles on this out there (see references at the bottom), so i will be brief.

When something has happened in the domain, domain event can be raised. It can be from a trivial property change to an overall object state change. This is a fantastic way to describe an actual event in your domain, i.e. customer has checked out, customer was created, etc.

Let's extend our previous e-commerce example:

    public class Product
    {
        public Guid Id { get; protected set; }
        public string Name { get; protected set; }
        public int Quantity { get; protected set; }
        public DateTime Created { get; protected set; }
        public DateTime Modified { get; protected set; }
        public bool Active { get; protected set; }
    }

    public class Cart
    {
        private List products;

        public ReadOnlyCollection Products
        {
            get { return products.AsReadOnly(); }
        }

        public static Cart Create(List products)
        {
            Cart cart = new Cart();
            cart.products = products;
            return cart;
        }
    }

    public class Purchase
    {
        private List products = new List();

        public Guid Id { get; protected set; }
        public ReadOnlyCollection Products
        {
            get { return products.AsReadOnly(); }
        }
        public DateTime Created { get; protected set; }
        public Customer Customer { get; protected set; }

        public static Purchase Create(Customer customer, ReadOnlyCollection products)
        {
            Purchase purchase = new Purchase()
            {
                Id = Guid.NewGuid(),
                Created = DateTime.Now,
                products = products.ToList(),
                Customer = customer
            };
            return purchase;
        }
    }

    public class Customer
    {
        private List purchases = new List()

        public Guid Id { get; protected set; }
        public string FirstName { get; protected set; }
        public string LastName { get; protected set; }
        public string Email { get; protected set; }
        public ReadOnlyCollection Purchases { get { return this.purchases.AsReadOnly(); } }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = Purchase.Create(this, cart.Products);
            this.purchases.Add(purchase);
            DomainEvents.Raise(new CustomerCheckedOut() { Purchase = purchase });
            return purchase;
        }

        public static Customer Create(string firstName, string lastName, string email)
        {
            Customer customer = new Customer()
            {
                 FirstName = firstName,
                 LastName = lastName,
                 Email = email
            };
            return customer;
        }
    }

    public class CustomerCheckedOut : IDomainEvent
    {
        public Purchase Purchase { get; set; }
    }

    public class CustomerCheckedOutHandle : Handles CustomerCheckedout
    {
        public CustomerCheckedOutHandle()
        {
      
        }

        public void Handle(CustomerCheckedOut args)
        {
            //send notifications, emails, update third party systems, etc
        }
    }

For example in our case, when customer calls Customer.Checkout(...) we raise "CustomerCheckedOut" event. When event is handled it should not change the purchase object state, it should facilitate additional behavior only. For example sending out an email, updating financial monthly balance sheet, calling third party API, etc.

This is how you could use it:
    public class CustomerCheckedOut : IDomainEvent
    {
        public Purchase Purchase { get; set; }
    }

    public class CustomerCheckedOutHandle : Handles CustomerCheckedOut
    {
        readonly IEmailSender emailSender;

        public CustomerCheckedOutHandle(IEmailSender emailSender)
        {
            this.emailSender = emailSender;
        }

        public void Handle(CustomerCheckedOut args)
        {
            this.emailSender.Send(args.Purchase.Customer, EmailTemplate.PurchaseMade);
            //send notifications, update third party systems, etc
        }
    }

    public interface IEmailSender
    {
        void Send(Customer customer, EmailTemplate template);
    }

    public enum EmailTemplate
    {
        PurchaseMade,
        CustomerCreated
    }

It can be confusing, what do you put in to Customer.Checkout(...) method and what do you put in to the handler?

In my opinion Customer.Checkout(...) method should do what it needs to do with the access to the properties/fields that it has access to. So creating a purchase object and adding it to the collection, incrementing purchase count on the customer, updating last purchase date on the customer object, you get the idea.

What does handler have access to? All of the infrastructure layer interfaces. This makes it a great place to send out emails and notifications, synchronize with third party services, create audit records, etc.


Summary:
  • Domain event handlers should not change the state of the object that caused domain event to be triggered.
  • Domain events should be raised inside the domain.
  • Domain events handlers should be looked at as a side effect / chain reaction facilitator.

Useful links: 

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment. 

Applied Domain-Driven Design (DDD), Part 1 - Basics

When I started learning domain-driven design there was a lot of theory to take in, Eric Evans did a great job explaining it from theoretical point of view. As a software engineer I wanted to see some code and just to follow some examples, I found very little resource out there that showed applied domain-driven design in C#.

Over the coming weeks I will be posting series of articles on the subject, it will be my attempt to make domain-driven design simpler and easier follow. Articles that are published:


Domain Driven Design Architecture
Domain Driven Design Architecture (it's simpler then it looks) 


Before we get started let's see why DDD is so great:
  • Development becomes domain oriented not UI/Database oriented 
  • Domain layer captures all of the business logic, making your service layer very thin i.e. just a gateway in to your domain via DTO's
  • Domain oriented development allows you to implement true service-oriented architecture i.e. making your services reusable as they are not UI/Presentation layer specific 
  • Unit tests are easy to write as code scales horizontally and not vertically, making your methods thin and easily testable
  • DDD is a set of Patterns and Principles, this gives developers a framework to work with, allowing everyone in the development team to head in the same direction

Through this series of articles I will be focusing on a simple and made up e-commerce domain.

So, let's see some code:
    
    public class Product
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public int Quantity { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public bool Active { get; set; }
    }

    public class Customer
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public List Purchases { get; set; }
  }

    public class Purchase
    {
        public Guid Id { get; set; }
        public List Products { get; set; }
        public DateTime Created { get; set; }
        public Customer Customer { get; set; }
    }

Code above represents anemic classes. Some developers would stop here, and use these classes to pass data to service and then bind this data to the UI. Let's carry on and mature our models.

When a customer shops online they choose items first, they browse around, and then eventually they will make a purchase. So we need something that will hold the products, lets call it a Cart, this object will have no identity and it will be transient.

Cart is my value object:
    
    public class Cart
    {
        public List Products { get; set; }
    }

Cart simply contains a list of products. Customer can go ahead and checkout these products when they are ready.

We can use what was said above as a business case "Customer can go ahead and checkout these products when they are ready".

Code would look like:
            Cart cart = new Cart() 
            { 
                Products = new Product[]
                { 
                    new Product(), 
                    new Product() 
                } 
            };

            Customer customer = new Customer()
            {
                FirstName = "Josh",
                LastName = "Smith"
            };

            Purchase purchase = customer.Checkout(cart);

What's going on here? Customer checks-out the product and gets a purchase back. Normally in business context you get a receipt back, this provides basic information about the purchase, discounts, and acts as a guarantee that i can use to refer back to my purchase.

I could rename Purchase to Receipt, but wait, what's does purchase mean in the business context?

"to acquire by the payment of money or its equivalent; buy." - Dictionary.com

(Returning purchase object would make sense if customer actually made a purchase i.e. we pre-authenticated a customer and then simply passed payment authentication code to the checkout, but to keep this simple we are not going to do this)

Customer can now checkout, when they checkout they will get back a Purchase object (this will allow further data manipulation).

Code above needs to be re-factored, if we return back a purchase are we going to then add it to the collection of the customer i.e. Customer.Purchases.Add(...)? This seems strange, if we have a method Customer.Checkout(...) we should aim to capture relevant data right there and then. Customer should only expose business methods, if we have to expose something else in order to capture data then we are not doing it right.

Lets refine further:
  
    public class Customer
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public List Purchases { get; set; }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = new Purchase()
            {
                 Customer = this,
                 Products = cart.Products,
                 Created = DateTime.Now
            };

            this.Purchases.Add(purchase);
            return purchase;
        }
    }

Ok, so now when customer checks-out a cart, purchase will be added to the purchase collection and also returned  so it can be used further in our logic. This is great, but another software engineer can go in and compromise our domain. They can just add Orders directly to the customer without checking out i.e. Customer.Orders.Add(...).

Lets refine further:
    public class Customer
    {
        private List purchases = new List();

        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public ReadOnlyCollection Purchases { get { return this.purchases.AsReadOnly(); } }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = new Purchase()
            {
                 Customer = this,
                 Products = cart.Products,
                 Created = DateTime.Now
            };
            this.purchases.Add(purchase);
            return purchase;
        }
    }

Now orders can't be compromised, and code forces software engineers to checkout a cart. What about other properties? They are not protected. We know customer state can't just be changed, we have to go through a process. You need to ask your self, when personal information is changed on the customer, do we need to send an email out? Do we need to call some 3rd party API to sync up our records? Right now you might not have a requirement from your business analysts to do anything like this, so lets not worry about it, lets just protect these fields so they can't be changed.
  
    public class Customer
    {
        private List purchases = new List();

        public Guid Id { get; protected set; }
        public string FirstName { get; protected set; }
        public string LastName { get; protected set; }
        public string Email { get; protected set; }
        public ReadOnlyCollection Purchases { get { return this.purchases.AsReadOnly(); } }
        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = new Purchase()
            {
                 Customer = this,
                 Products = cart.Products,
                 Created = DateTime.Now
            };
            this.purchases.Add(purchase);
            return purchase;
        }
    }

That's great, now other software engineers in the team can't change personal information without adding a new method such as Customer.ChangeDetails(...).

Taking in to account what was said above, thinking process, constant re-factoring and making the model match the actual business domain, this is what I've got so far:


    public class Product
    {
        public Guid Id { get; protected set; }
        public string Name { get; protected set; }
        public int Quantity { get; protected set; }
        public DateTime Created { get; protected set; }
        public DateTime Modified { get; protected set; }
        public bool Active { get; protected set; }
    }

    public class Cart
    {
        private List products;

        public ReadOnlyCollection Products
        {
            get { return products.AsReadOnly(); }
        }

        public static Cart Create(List products)
        {
            Cart cart = new Cart();
            cart.products = products;
            return cart;
        }
    }

    public class Purchase
    {
        private List products = new List();

        public Guid Id { get; protected set; }
        public ReadOnlyCollection Products
        {
            get { return products.AsReadOnly(); }
        }
        public DateTime Created { get; protected set; }
        public Customer Customer { get; protected set; }

        public static Purchase Create(Customer customer, ReadOnlyCollection products)
        {
            Purchase purchase = new Purchase()
            {
                Id = Guid.NewGuid(),
                Created = DateTime.Now,
                products = products.ToList(),
                Customer = customer
            };
            return purchase;
        }
    }

    public class Customer
    {
        private List purchases = new List()

        public Guid Id { get; protected set; }
        public string FirstName { get; protected set; }
        public string LastName { get; protected set; }
        public string Email { get; protected set; }
        public ReadOnlyCollection Purchases { get { return this.purchases.AsReadOnly(); } }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = Purchase.Create(this, cart.Products);
            this.purchases.Add(purchase);
            return purchase;
        }

        public static Customer Create(string firstName, string lastName, string email)
        {
            Customer customer = new Customer()
            {
                 FirstName = firstName,
                 LastName = lastName,
                 Email = email
            };
            return customer;
        }
    }

Example usage:
   
            Cart cart = Cart.Create(new List() { new Product(), new Product() });
            Customer customer = Customer.Create("josh", "smith", "josh.smith@microsoft.com");
            Purchase purchase = customer.Checkout(cart);


Summary: 
  • DDD is all about capturing business logic in the domain i.e. entities, aggregate roots, value objects and domain service.
  • DDD is all about thought process and challenging where should what go and what is most logical.
  • DDD is all about constant re-factoring and maturing your model as you get further requirements. More requirements your receive the better and stronger your domain will be. Therefore requirements are gold and something that software engineers should always strive to understand.

Useful links:


*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment.