newbie question of orders/line items

1,169 views
Skip to first unread message

Colin Yates

unread,
Oct 14, 2011, 4:50:28 AM10/14/11
to dddcqrs
Hi all,

This must have come up a gazillion times, but I just wanted clarity. Let's say we have an order with a number of line items. The user stories involve "create new order", "add item to order" and crucially "edit item in order". Before DDD I would have exposed the following DAO methods (on respective DAOs):

Order getOrder(Long id)
LineItem getLineItem(Long id)

and Order would have Order.getLineItems()

However, DDD introduces aggregate roots and the thing the customer cares about and views as atomic is the Order; the LineItem is clearly part of the composite Order. This implies that the aggregate root is the Order which means loading a LineItem directly is a no-no. The Repository would offer:

Order getOrder(Long id)

Fine, data persistence aside, I still need the ability to allow a client to edit a particular LineItem. You can imagine the Order screen:

a href="edit.html?id=lineItem.id"

The question is what is this ID? I thought IDs inside aggregate roots were invisible to the outside world, and I thought this level of manipulation of an AR's contents was a no-no. The quote "Nothing outside the Aggregate boundary can hold a reference to anything inside, except to the root Entity.  The root Entity can hand references to the internal Entities to other objects, but they can only use them transiently (within a single method or block)." from the very useful "http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/05/15/domain-driven-design-supple-design-patterns-series.aspx" makes the use case of "edit a lineitem" really quite hard to implement :)

I can't believe the LineItem is an AR as that essentially means the pattern is "anything you want to handle independently is an AR" which seems bogus.

I know I need to read DDD by Eric Evans again :) but what are your thoughts?

Col

Nuno Lopes

unread,
Oct 14, 2011, 5:08:50 AM10/14/11
to ddd...@googlegroups.com
Hi,

If this was possible to hold a reference to a Entity inside an Order:

LineItem li = aOrder.GetItem(itemid);
li.Qty = 30;

You would be changing the Order. If the Order had invariants over the number of items or price it would be more complex to enforce them.

Instead:

aOrder.GeItem(itemid, out description, out baseprice, out ...)

To change a line item:

aOrder.Change(itemid, description, baseprice, ......);

In other words, accessors return values (not entities) mutators receive values or aggregate roots (special entities).

Does that make sense?

Nuno

Colin Yates

unread,
Oct 14, 2011, 5:15:17 AM10/14/11
to dddcqrs
Hi - thanks for your reply. What if LineItem is really quite complex though, if it was a graph for example. Is it "valid" to do:

AR root = loadRoot()
Child child = root.getChild(childId) // child is "detached" or cloned
// update the child
root.updateChild(child)

Is that valid?

Col

Excerpts from Nuno Lopes's message of 2011-10-14 10:08:50 +0100:

Jimmy Bogard

unread,
Oct 14, 2011, 6:04:56 AM10/14/11
to ddd...@googlegroups.com
In most ordering systems I've seen, users can do only a few things with line items:

- Add line item to order
- Modify quantity of individual line item
- Remove line item from order

What operations do you have that the user can do that are more complex than this?

Colin Yates

unread,
Oct 14, 2011, 6:20:22 AM10/14/11
to dddcqrs
Well my use case isn't really anything to do with orders and line items, I chose that because it is a similar structure. My actual use case is to edit Projects which are actually Directed Acyclic Graphs - each node in the graph is itself a node.

However, even keeping it simple in the Order domain I can easily imagine the need for polymorphic line items - my order has three items, each item can be customised but the customisation is different depending on the line item.

To be explicit, I have an object which the domain considers atomic however it is a composite and there is a need to edit its constituent parts. These parts are non-trivial and aren't structurally identical therefore exposing the structure via a method on the root isn't possible. To make things even more complicated; those parts are actually designed by an external DSL which the client updates so the codebase doesn't even know the structure of the node ;).

For example, imagine the an instance of the DSL is:

--- start
pathway 1 starts with
node "a" and node "b"
when "a" and "b" finish then start node "c"
node "a" contains fields "field1" and "field2"
node "b" contains fields "field1" and "field3"
--- end

The "pathway" is the unit that the domain talks about however there is a need to edit individual nodes - editing nodea requires editing the default fields plus "field1" and "field2", editing node c requires editing the default fields. You can see how I couldn't possibly capture the structure of those nodes in public API on the Pathway object.

I can see an argument for saying the Node is an AR and Pathway is also a "super" AR but I don't know if that makes any sense in DDD....

Am I completely off base here?

Col

Excerpts from Jimmy Bogard's message of 2011-10-14 11:04:56 +0100:

Michael Brown

unread,
Oct 14, 2011, 2:53:15 PM10/14/11
to ddd...@googlegroups.com
It sounds to me you are modeling a Tree which is just a collection of Nodes and their children. The node is your AR and you might have a concept of a "Root" node. Having polymorphic nodes is allowable without violating the AR constraints. For instance let's say your system is modeling a construction project. You might model different types of activities as specific types of nodes. Each activity might have constituent activities for example "lay the foundation" might include "level/excavate the land" and "pour the cement". Pouring cement might be a special category of ProjectActivity with its own specific attributes. The Project is little more than the root node in this approach.

Colin Yates

unread,
Oct 14, 2011, 4:12:34 PM10/14/11
to dddcqrs
To be clear, are you saying that the root node is an AR which contains links to other (AR) nodes? This is where I was heading originally but I wasn't sure whether an AR can be "owned" by another AR. This is different from merely a relationship between ARs (i.e. the relationship between a Customer AR and the Order AR). For example, the project might have invariants like "if there is a 'pour the cement' (AR) node then it must be logically before the 'screed the cement' node" and "there can only be one 'sell the constructed structure'" and so on.


Excerpts from Michael Brown's message of 2011-10-14 19:53:15 +0100:

Peter Ritchie

unread,
Oct 14, 2011, 4:39:56 PM10/14/11
to ddd...@googlegroups.com
Even then, that functionality may not even be in an "Order"--it could be in a shopping cart and the Order wouldn't be created until payment was received or the order was committed in some way.

Cheers -- Peter

Daniel Yokomizo

unread,
Oct 14, 2011, 6:58:36 PM10/14/11
to ddd...@googlegroups.com

Let's try to unpack it in a few problems:

1. Aggregates are about maintaining invariants, not about structure, so if there's no order level invariants depending on line item state  you could have both order and line items as roots of different aggregates if both externally addressable.

2. If you have complex graphs inside an entity and need to make arbitrary changes on it which invariants are you maintaining? You may not even have (in this part of the system) enough behavior to justify using DDD. You could just do a simple transaction script over a graph library and get done with it.

3. If these changes require complex validation (i.e. aren't entirely arbitrary) and dictate more then structure (e.g. different changes have different effects in other system parts) then you may have a decent domain modeling problem. In such situations I prefer to model the changes as a DSL of value objects and have an apply command using an interpreter internally to  process the DSL. Most of my tests and objects end up driven by the interpreter and the values  (i.e. dsl fragments).

andho

unread,
Oct 15, 2011, 5:13:41 AM10/15/11
to ddd...@googlegroups.com
That would not be valid as the child is updated outside of the aggregate root so invariants are not maintained.

Why not instead of:

AR root = loadRoot()
Child child = root.getChild(childId) // child is "detached" or cloned
// update the child
root.updateChild(child)

do this

AR root = loadRoot()
Child child = root.getChild(childId) // child is "detached" or cloned
// update the child
root.updateChild(childId, changeparam1, changeparam2....);

// Aggregate class
class Order {
  function updateChild(childId, param1, param2) {
    child = this.child(childId);
    // update child
  }
}

Is there any reason that the child could not be updated by the Aggregate Root instead of the Command Handler. If updating the child needs to communicate with other ARs then the update method should be in a Service.

Or make the child immutable:

AR root = loadRoot()
Child child = root.getChild(childId) // child is "detached" or cloned
newChild = Factory.newChildFromExistingChild(child);
root.updateChild(childId, newChild); // keeping it real

class Order {
  function updateChild(childId, newChild) {
    this.removeChild(childId);
    this.addChild(childId, newChild);
  }
}

Reply all
Reply to author
Forward
0 new messages