Reconciling DDD, viewmodels, and performance
Asked Answered
I

2

7

I am starting to learn about DDD and am concerned about the performance implications of retrieving entity objects from persistence and then restructuring them in a viewmodel for the UI.

Let's say that I have two aggregate roots:

Person      Orders
------      -------
personId    orderId
name        personId

Each aggregate root has its own repository in charge of basic CRUD operations for the entire aggregate.

Let's say that the UI requires the following columns:

viewmodel
---------
personName
numberOfOrders

I can think of two ways that I can populate this viewmodel:

  1. Eagerly load all person entities, eagerly load all orders based on personId, restructure loaded entities into the viewmodel.
  2. create a JOIN/COUNT(orderId) stored procedure and have the database return data in the same structure as the viewmodel.

Obviously, option 1 can be quite the expensive operation, as there can be multiple persons and multiple orders resulting in MULTIPLE database calls. Option 2 will require only one database call.

If Option 2 is the preferred (performant) option, where do I store this "viewmodel" and the so called "database call?" Is there a separate "data service layer" on top of the repositories that I can implement? Or is that an anti-pattern in regards to how DDD is generally implemented?

Basically, how do I reconcile complex DDD aggregates with custom UI Viewmodels keeping performance in mind?

UPDATES

Specifications / Query Object

In talks with a friend, he had suggested that a possible solution is some sort of specification/query object pattern. The only problem is that we will have to implement this on the repository-level, requiring me to combine the Persons and Orders into one large aggregate. This is something I generally avoid for transaction consistency reasons.

Intravenous answered 21/8, 2012 at 6:54 Comment(0)
A
6

You can introduce a dedicated value object and a repository that would return statistics for a given person:

// value object
class PersonStatistics {
    String PersonName
    Int NumberOfOrders
    Money AverageOrderAmount
}

// repository
interface PersonStatisticsProvider {
    PersonStatistics Get();
}

This is similar to a read-model pattern.

Afeard answered 21/8, 2012 at 17:32 Comment(2)
Do we implement the provider and value object on an aggregate-level or on the domain-level? Since this value object can be classified under either the Persons or Orders aggregate.Intravenous
Value object has not direct association (reference) with any of the Aggregates. It is just a 'standalone' class. And you can have more than one repository per aggregate.Afeard
S
0

From a performance perspective, I'd choose option 2 but would probably keep the query that returns a person separated from the query that returns its number of orders.

You can make the order count available through something like OrderRepository.GetOrderCountByPerson(personId) even if it does deviate a bit from your canonical definition of a repository.

Normally you derive ViewModels from your domain entities. It would seem odd to have a Service that queries the database directly so that it returns a data structure matching a ViewModel exactly.

Sporty answered 21/8, 2012 at 9:5 Comment(2)
This would still result in multiple database trips which I'm trying to minimize. I agree with you that having an application-specific service that queries the database directly is a bit odd.Intravenous
I figured that you would already have PersonName in memory from a previously loaded Person aggregate root, so that didn't mean multiple database trips to me, just one for NumberOfOrders.Sporty

© 2022 - 2024 — McMap. All rights reserved.