Question

I've been practicing DDD and refactoring an app to understand it's principles and applications better. However I can't fully grasp some of the ideas and how to implement them in my business domain.

Let me start with stating a business requirement: A user can place and order and this order would need to be verified by a sales manager who is responsible for the orders of that specific product. User can be a registered user or an anonymous user. In both cases order comes from a request form that contains all the required information for order to be placed. Users can purchase their orders with different supported payment methods.

Now this requirement only belongs to the "placing an order" context. Other parts of the app includes how to process this order after it's verified, and then when the processing is complete, eventually deliver the order.

For this requirement I'm trying to think of my domain entities. First I think it'd be logical to have Customer aggregate, because from the original requirement it's clear that a Customer can place an order so my code could be something like the following:

class Customer {
   Key Id
   void PlaceOrder(OrderRequest request) {...}
}

However, business requirement states that this user can be an anonymous user in that case I have no "Customer" instance that I can map the incoming request to. So I change my model and come up with the following aggregate:

class OrderContext {
   Key OrderId;
   void PlaceOrder(OrderRequest request, Nullable<Customer> customer) {...}
}

But then DDD examples and books state that domain entities should correspond to their use cases. In this context, it's not very clean when you read this code and out loud state "an Order can place itself" - errm what?

How do you model your domain for a business requirement like the above? Who is actually responsible for creating this order? On top of that, an order can be placed from the sales manager's if, for example we want to gift to a customer for compensation. In this case, a "SalesManager" entity would also have "placeOrder" method in it. I'm totally confused and lost about how to translate this kind of business logic into my domain models.

How would be the relation between orders and customers and sales managers then? I could have "orders" collection in all of those entities and then it'd be a nightmare to keep all of them in a consistent state considering this is a web app and aggregates are loaded - consumed and then destroyed per request basis.

Was it helpful?

Solution

For this requirement I'm trying to think of my domain entities. First I think it'd be logical to have Customer aggregate, because from the original requirement it's clear that a Customer can place an order so my code could be something like the following:

class Customer {
  Key Id
  void PlaceOrder(OrderRequest request) {...}
}

However, business requirement states that this user can be an anonymous user in that case I have no "Customer" instance that I can map the incoming request to.

You should realize that a Customer and a Registered User are two (related but) distinct entities. The Registered User is a person who has gone through the trouble of creating an account. A Customer is a person who places an order.

To model your entities, I would do it like this

class Customer {
  Key Id
  Nullable<Account> Account

  static Customer CreateForAccount(Account registeredUser) { ... }
  static Customer CreateAnonymous() { ... }

  void PlaceOrder(OrderRequest request) { ... }
}

The Customer class has a link to the Account that was used when placing the order. That link is Nullable to cater for anonymous customers who don't have an account. For good measure, I included two Factory Methods to create the two kinds of customers.

This Customer class would probably grow to have additional properties, such as the customer's name, billing address and shipping address, which are all needed when fulfilling an order and might be different for this order from the defaults that a registered user might have on file (for example, when they are purchasing on behalf of a friend). An anonymous customer would, off course, have nothing on file.

Licensed under: CC-BY-SA with attribution
scroll top