Ricibald
Ricibald

Reputation: 9939

DDD: aggregate root specialization

I have an aggregate root Order with corresponding OrderService and OrderRepository.

I have an ExtendedOrder with corresponding ExtendedOrderService and ExtendedOrderRepository.

For example:

class Order {
   int GetOrderId();
}

class ExtendedOrder : Order {
   string GetExtendedInfo();
}

I would like to have OrderService to return both type Order and ExtendedOrder as a list of Order. But to obtain ExtendedOrder it should ask to the corresponding ExtendedOrderService when the Order is of type ExtendedOrder.

Is it possible to obtain this behavior? Is it legal to have an aggregate root extend another aggregate root?

Upvotes: 5

Views: 3012

Answers (4)

Jonathan Moffatt
Jonathan Moffatt

Reputation: 13457

Given that ExtendedOrder inherits from Order then you would not normally have an ExtendedOrderRepository and ExtendedOrderService - the OrderRepository and OrderService would be sufficient.

An ExtendedOrder is still an Order, so it should live within the boundaries of the Order aggregate root and use the OrderRepository and OrderService.

The OrderRepository will return Order objects, which may or may not be Extended Orders. ORMs such as NHibernate support this behaviour out of the box.

Working in this way lends itself to more elegant code and less repetition. Code might look like:

public class Order
{
    public virtual void Process()
    {
        // do processing stuff to an order
        // ...
    }
}

public class ExtendedOrder : Order
{
    public override void Process()
    {
        // do the standard order processing
        base.Process();
        // do extra processing specific to an extended order
        // ...
    }
}


public class OrderService
{
    public void ProcessRecentOrders()
    {
        IEnumerable<Order> orders = orderRepository.GetRecentOrders();
        // orders may include Order and ExtendedOrder objects in the collection...
        foreach (Order in orders)
        {
            // ...but polymorphism ensures that we don't need to know whether order is an ExtendedOrder or an Order
            order.Process();
        }
    }
}

and if need be you can still explicitly distinguish between Order and ExtendedOrder:

public void SendOrder(Order order)
{
    // do normal order sending stuff
    orderSender.TransmitOrder(order);
    if (order is ExtendedOrder)
    {
        // do additional stuff required by an ExtendedOrder
    }
}

This approach lets you continue to create other Order types (e.g. "SpecialOrder") without a proliferation of repositories and services.

Upvotes: 2

guillaume31
guillaume31

Reputation: 14064

If the Extended part of ExtendedOrder is really a domain concept and part of the ubiquitous language (as opposed to a purely UI commodity) I'd favor composition over inheritance and make it a separate entity, ExtendedOrderDetails for instance.

It would be part of the Order aggregate and thus accessible through the Order provided by OrderService.

Upvotes: 2

kstaruch
kstaruch

Reputation: 1319

The main question is, what do you need it for. I suspect it's related with some kind of UI or reporting functionality. If that's the case my advise would be to not use Domain Model concepts for these queries.

In other words separate Domain Model concepts, which are great for keeping consistency and achieving control over complex business logic, from reporting-related queries (presenting results on UI is a form of report after all). Failing to do that separation at some point always leads to adjusting Domain Model in order to fulfill the requirements of some kind of UI or report, and this shouldn't happen.

In your case I'd simply prepare a query that ignores the aggregate and selects all the data that I need. Of course, when some business action is to be performed on the results it should use Domain Model. After all aggregates are responsible for keeping the consistency, and the only thing that will have a chance to mess with consistency is updating the data - reading the data will not cause changes, thus using aggregates for reads is kinda pointless in my opinion.

To sum up - reporting-related functionality in most scenarios shouldn't cause adjustment in Domain Model business concepts.

Upvotes: 5

autonomatt
autonomatt

Reputation: 4433

You don't need an ExtendedOrderService. Just use one OrderService to coordinate results from OrderRepository and ExtendedOrderRepository.

See the answer to this question for more on the role of Application Services. An Application Service can utilize multiple repositories.

Upvotes: 6

Related Questions