3

Background

Udi Dahan suggests a fetching strategy as a useful pattern to use for data access. I agree.

The concept is to make roles explicit. For example I have an Aggregate Root - Customer. I want customer in several parts of my application - a list of customers to select from, a view of the customer's details, and I want a button to deactivate a customer.

It seems Udi would suggest an interface for each of these roles. So I have ICustomerInList with very basic details, ICustomerDetail which includes the latest 10 products purchased, and IDeactivateCustomer which has a method to deactivate the customer. Each interface exposes just enough of my Customer Aggregate Root to get the job done in each situation. My Customer Aggregate Root implements all these interfaces.

Now I want to implement a fetching strategy for each of these roles. Each strategy can load a different amount of data into my Aggregate Root because it will be behind an interface exposing only the bits of information needed.

The general method to implement this part is to ask a Service Locator or some other style of dependency injection. This code will take the interface you are wanting, for example ICustomerInList, and find a fetching strategy to load it (IStrategyForFetching<ICustomerInList>). This strategy is implemented by a class that knows to only load a Customer with the bits of information needed for the ICustomerInList interface.

So far so good.

Question

What you pass to the Service Locator, or the IStrategyForFetching<ICustomerInList>. All of the examples I see are only selecting one object by a known id. This case is easy, the calling code passes this id through and will get back the specific interface.

What if I want to search? Or I want page 2 of the list of customers? Now I want to pass in more terms that the Fetching Strategy needs.

Possible solutions

Some of the examples I've seen use a predicate - an expression that returns true or false if a particular Aggregate Root should be part of the result set. This works fine for conditions but what about getting back the first n customers and no more? Or getting page 2 of the search results? Or how the results are sorted?

My first reaction is to start adding generic parameters to my IStrategyForFetching<ICustomerInList> It now becomes IStrategyForFetching<TAggregateRoot, TStrategyForSelecting, TStrategyForOrdering>. This quickly becomes complex and ugly. It's further complicated by different repositories. Some repositories only supply data when using a particular strategy for selecting, some only certain types of ordering. I would like to have the flexibility to implement general repositories that can take sorting functions along with specialised repositories that only return Aggregate Roots sorted in a particular fashion.

It sounds like I should apply the same pattern used at the start - How do I make roles explicit? Should I implement a strategy for fetching X (Aggregate Root) using the payload Y (search / ordering parameters)?

Edit (2012-03-05)

This is all still valid if I'm not returning the Aggregate Root each time. If each interface is implemented by a different DTO I can still use IStrategyForFetching. This is why this pattern is powerful - what does the fetching and what is returned doesn't have to map in any way to the aggregate root.

I've ended up using IStrategyForFetching<TEntity, TSpecification>. TEntity is the thing I want to get, TSpecification is how I want to get it.

Thomas Coats
  • 632
  • 5
  • 10

2 Answers2

3

Have you come across CQRS? Udi is a big proponent of it, and its purpose is to solve this exact issue.

The concept in its most basic form is to separate the domain model from querying. This means that the domain model only comes into play when you want to execute a command / commit a transaction. You don't use data from your aggregates & entities to display information on the screen. Instead, you create a separate data access service (or bunch of them) that contain methods that provide the exact data required for each screen. These methods can accept criteria objects as parameters and therefore do searching with whatever criteria you desire.

A quick sequence of how this works:

  • A screen shows a list of customers that have made orders in the last week.
  • The UI calls the CustomerQueryService passing a date as criteria.
  • The CustomerQueryService executes a query that returns only the fields required for this screen, including the aggregate id of each customer.
  • The user chooses a customer in the list, and chooses perform the 'Make Important Customer' action /command.
  • The UI sends a MakeImportantCommand to the Command Service (or Application Service in DDD terms) containing the ID of the customer.
  • The command service fetches the Customer aggregate from the repository using the ID passed in the command, calls the necessary methods and updates the database.

Building your app using the CQRS architecture opens you up to lot of possibilities regarding performance and scalability. You can take this simple example further by creating separate query databases that contain denormalised tables for every view, eventual consistency & event sourcing. There is a lot of videos/examples/blogs about CQRS that I think would really interest you.

I know your question was regarding 'fetching strategy' but I notice that he wrote this article in 2007, and it's likely that he considers CQRS its sucessor.

To summarise my answer:

  1. Don't try and project cut down DTO's from your domain aggregates. Instead, just create separate query services that give you a tailored query for your needs.
  2. Read up on CQRS (if you haven't already).
David Masters
  • 8,069
  • 2
  • 44
  • 75
  • Thank you for this perspective! I'm using this technique alongside CQRS which I have read a lot about. `IStrategyForFetching` doesn't have to return the aggregate, it could return a projection just for customers in lists. What I'm suggesting is instead of talking to a `CustomerQueryService` directly we can ask for the role ICustomerInList and part of the system knows how to get that for us. It's similar to the `IValidate` marker interface pattern. – Thomas Coats Mar 02 '12 at 18:29
1

To add to the response by David Masters, I think all the fetching strategy interfaces are adding needless complexity. Having the Customer AR implement the various interfaces which are modeled after a UI is a needless constraint on the AR class and you will spend far to much effort trying to enforce it. Moreover, it is a brittle solution. What if a view requires data that while related to Customer, does not belong on the customer class? Does one then coerce the customer class and the corresponding ORM mappings to contain that data? Why not just have a separate set of classes for query purposes and be done with it? This allows you to deal with fetching strategies at the place where they belong - in the repository. Furthermore, what value does the fetching strategy interface abstraction really add? It may be an appropriate model of what is happening in the application, it doesn't help in implementing it.

Community
  • 1
  • 1
eulerfx
  • 36,769
  • 7
  • 61
  • 83
  • Again, the Aggregate Root doesn't have to implement every interface - DTOs can be used. These interfaces can be thought of as view models. Udi suggests that the service layer changes at a different rate to the domain layer. As a result it's good to have a layer of indirection between each layer. I can ask 'the system' for `ICustomerInList` and through CQRS I'm reading a DTO straight from the database. My question is about 'the system'. `IStrategyForFetching` vs `IFetchCustomersInLists`. – Thomas Coats Mar 04 '12 at 21:21