Pragmatic
Pragmatic

Reputation: 3127

Fetching associated aggregates in Domain driven Design

I have recently started exploring Domain driven design and have a question. Suppose I have a Product, Category, Manufacturer domain models in my application. And Product looks like this:

public class Product 
{ 
int ProductId; 
string Title; 
string Description; 
double Price; 
int CategoryId; 
Category Category; 
Manufacturer Manufacturer; 
}

Generally on a detail view where a product is displayed, Category Name and Manufacturer name is shown (rather than their Ids). But Category and Manufacturer are different Aggregates. Question is how to fetch Manufacturer and Category Name along with Product Domain Model. ProductRepository will only return Product domain Model (along with categoryId and ManufacturerId).

  1. Either my Product Service raise another request to fetch the CategoryId and ManufacturerId
  2. Or I can fetch them while Product is fetch from Product repository.

But I don't need all the attributes, I just need their title. And I am facing similar issues with all the domain model.

Please help how should I solve this problem.

Upvotes: 2

Views: 176

Answers (3)

Ilya Palkin
Ilya Palkin

Reputation: 15727

Generally on a detail view where a product is displayed, Category Name and Manufacturer name is shown (rather than their Ids). ... Question is how to fetch Manufacturer and Category Name along with Product Domain Model. ProductRepository will only return Product domain Model (along with categoryId and ManufacturerId)

As I see, Category Name and Manufacturer Name are used for displaying so it is presentation concern. I'd suggest having a separate read model for Product which serves that screen only (see Command Query Responsibility Segregation (CQRS)):

public class ProductReadModel 
{ 
    int ProductId; 
    string Title; 
    string Description; 
    double Price; 
    string CategoryName; 
    string ManufacturerName; 
}

There are different ways to fill/build that model:

  • If our aggregates are persisted in relational data base using ORM (e.g. NHibernate) then you can directly query your data base using raw SQL or a light-weight ORM (e.g. Dapper). You do not need repository for it, you need just QueryHandler

  • If our aggregates are event sourced, then you simply listen to Domain Events (e.g. CategoryNameChanged/ManufacturerNameChanged ) and then project (denormalise) them into ProductReadModel and store it in any storage (even in memory).

  • You can also fire and project/denormalise Domain Events if your aggregates are persisted in relational data base.

Upvotes: 0

Thomas Ploch
Thomas Ploch

Reputation: 795

In addition to @tomliversidge's answer I recommend looking into the Composite UI pattern (you can find an example here).

There you would have a Service Gateway building a composite View Model consisting of information from the 3 services (Product, Manufacturer and Category).

Upvotes: 0

tomliversidge
tomliversidge

Reputation: 2369

There are various ways you can handle this:

Local Caching / View Models

Keep an in-memory cache locally in your service that maps between CategoryId and CategoryTitle (same for Manufacturer) - this can either be through:

  • listening to an event (i.e. CategoryCreated). This would be preferred if you are have an event-driven system. You could also listen to other events (i.e. CategoryTitleUpdated) if relevant.
  • by making a web request to the external services. You would query your local cache first, and then decide if to call the external service. You'd need to think about how stale you allow your cache to become.

Denormalising the data

You could duplicate the data by saving the CategoryTitle alongside the CategoryId. This way you have no call to an external service. The tradeoff is you need to consider how often the CategoryTitle is likely to change, and how you handle that change.

Reporting "domain"

You could have a completely separate service that listens for data from other services and maintains view models for UIs. This would keep your other services ignorant of other service's concerns. When using an event-driven system, you'd be listening for events form other services that allow you to build a view model for the UI

Upvotes: 2

Related Questions