8

I have inherited an ASP.NET website application and the previous developer has used what I believe are some DDD concepts. I am new to DDD and I have to admit I am struggling with the practical side of it having come from an N-Tier Transaction Script background.

One of the problems with the site is that it is slow. There is no paging on huge grids and also the business logic layer pulls down way more data than is needed for 90% of operations 70% of which are reads to bind data to grids.

The general design is that the business logic layer has a Service class per aggregate root, with various "GetByX()", 'FindBestMatching()" and "Update()" type methods on it. These Service classes are the points from which trees of domain objects are created such as "Organisation" and "User."

Back in my comfortable transaction script days, my service layer was similar: there would be some logic possibly, a call into a repository (or since EF 6 direct use of EF as it can be mocked for testing) and then the call would return. The objects that were returned would be DTO type objects containing return data relevant, including possibly some POCO entity objects. These DTOs would travel to clients via webservices, be bound to ASP.NET or Silverlight grids...all was good.

My operations on my service layer could be very specific, allowing for queries like:

"GetUsersWithSurnameIncludeChildren(string surname, int skip, int take)"

allowing for nice narrow DB queries that supported paging and eager loading of related data. It was fast and loaded only the data that was actually needed.

Now with this site I have inherited there is an actual OO model of the business domain. These classes are POCOs, not generated from EF, but EF is used database first as a repository (there is a lot of mapping between the domain objects and the EF objects). An example domain object is below (without any logic).

public class Organisation
{
    public int OrganisationID { get; set; }
    public string Name { get; set; }

    public IList<Organisation> AssociatedOrganisations { get; set; }
    public IList<User> Staff { get; set; }
    public IList<User> Managers { get; set; }

    // Business logic method implemented below here
}

Many of the classes are essentially copies of the underlying entity framework objects, others have a significant amount of logic encapsulated within them.

The way the site works currently is that every time the OrganisationService service class creates a new Organisation domain object (and it may create a collection in one hit) it also populates ALL of the fields and collections in that object...and all of the fields and collections in those objects! A huge deep tree is created in memory and a stack of joins and unions (generated by EF) is needed to get all of the data. This is totally inappropriate for a website that mainly displays loads of flat grids.

My questions are:

1) Is an application that is 70% pulling data out of a DB for display and 30% business logic actually a bad candidate for DDD?

2) If DDD is appropriate how are scenarios like this generally designed? Should I implement a separate "Query Service" that is designed just to pull out loads of flat data for binding to pages and grids (these could just be the EF POCOS themselves) and reserve the Domain objects just for parts of the application where logic must actually be performed?

3) Should I only load data into my Domain Object reference types and collections when it is actually accessed (like EF lazy loading...)? Previous posts I have read have suggested lazy loading in a Domain Object is a code smell: DDD Lazy Loading Code Smell

I am about to buy a DDD book tonight :)

4 Answers 4

5

After you read Evans' book, you will see that DDD is a collection of concepts. The book was written over a decade ago. My opinion of it today is that it has good parts but is easily misunderstood and sometimes used inappropriately. In reality, some parts you may not need or may not be right for your application or chosen tools.

As far as EF, I don't think that the DDD concept of combining data and behavior in your entities melds well with it. EF entity models should be devoid of behavior if you want to avoid a lot of unexpected errors and performance bottlenecks. As other answers here point out, using a CQ(R)S pattern is a good way of implementing behavior externally from EF entities. Bottom line is, try to avoid implementing a "rich domain model" using methods on EF entities. You can still crunch the business knowledge into "the model", but your "model" should contain different classes for data (entities) and behavior (operations).

I also don't like the idea of repositories, but that's primarily my opinion and I'm sure others will disagree when I say they really aren't necessary when you are using an ORM like EF. Maybe a decade ago, before we had modern generics, they were more useful. These days, you can wrap a single generic interface around an EF DbContext and have a single generic repository (along with unit of work).

On the other hand, things like the ubiquitous language, aggregate roots, bounded contexts, query criteria, etc. I consider to be pearls. Keeping the application layer thin is a timelessly good practice too. It sounds like the other developer might not have applied the appropriate DDD concepts, and as someone else here already said, created more problems than they solved.

1
  • The problem is that repository interfaces are considered a part of the domain in traditional DDD modeling. The methods on the repo are meant to created using ubiquitous language language, and explain their intent.
    – UK-AL
    Commented Feb 20, 2017 at 11:04
1

1) I wouldn't say that. DDD is good for complex systems (which may mean, anything more sophisticated than CRUD). Domain logic can be very complex regardless of its share in the system compared to query logic.

2) I see two ways of adressing this. One is limit aggregate size. Small aggregates are less prone to bloated, slow data loading. The other is separate your reads from your writes (CQRS). This is basically the "Query Service" you're mentioning, but it's better to have it return separate read model objects that are not your entities.

3) Calling entity lazy loading a code smell is somewhat... opinionated. It could actually be a solution. But you may still have performance problems with it, such as adding one entity to a huge collection forcing you to load this entire collection. A better aggregate design can address this.

1

I've built several DDD models, persistence was handled by NHibernate. We went with a repository pattern in our early projects and built all of our queries into our repositories.

This is a great idea in theory but what we learnt in practice is that you end up writing a lot of boiler place when implementing the repositories and also they were practically useless anyway. Beyond the standard save, delete, and FindByID method all the actual queries that get used are almost always unique to one particular use case, especially when you take into account which properties you want to eager load.

So we still use a repository pattern but just have save, delete, findByID and findByQuery methods. The query is encapsulated in an object and passed into the repository, the query object is built in the application layer.

0

If code is what you're struggling with, find the reason it's there. If there's no valid reason to be found, get rid of the code and write code that has a reason to be there. Your question is showing a list of self-inflicted problems caused by the usage of a pattern which might have solved a couple of problems but introduced a lot more, most of them technical ones.

Let me refer to my answer on SO about whether or not DDD is a waste of time: https://stackoverflow.com/questions/810606/is-ddd-a-waste-of-time/810661#810661

This might get you a bit of perspective what DDD is and what it isn't. The TL;DR is that it's not a technical solution but could (not would!) end up in a technical solution of some sort, but it doesn't have to.

The extensive graph fetching might hint at a SELECT N+1 problem through either lazy loading or loops in place which fetch code of related elements. This might be done to be able to produce a projection on the data in-memory. Looking at the class snippet though, I don't see a deep DDD influence there, as the collections otherwise wouldn't have been there.

It might be wise to see whether you actually need to fetch entity instances in a graph at all, as it might be way faster to formulate the projection of the data needed as a linq query over the entities in the model and let the DB do the set production. The resultset can then be fetched into new POCO objects, which are simply flat lists and these are then used in the UI. This is of course read/only data but for a lot of cases this is enough.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.