1

I'm writing an app with DDD in mind and trying to avoid having an anemic domain model by delegating doman logic and behaviour to entities. There's an issue I'm running into with constructing entities that are aggregate roots and need to create sub-entities that require handling by a factory class. Here's an example:

I have the following entities: Page, Url and Layout. What makes a Page in my model is a Page with a Url and a Layout - without those two, a Page object would not be valid. In a simple model, the Page constructor method would simply create those two objects as private attributes. However, the Url has a specific requirement; it has to be created from the Page's title - or a slug - and it has to be unique (ensured by appending "-1", "-2", or something similar). This requires communication with a repository.

My initial idea was to pass/inject a UrlFactory object to the Page constructor and have the Page create the Url it needs, but I keep reading about how injecting services into entities is a bad idea.

So, my question is; is there a way - or an established pattern - to allow entities to construct their complex sub-entities without having an anemic domain model, or is injecting a factory in case such as this a valid solution?

Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
Martin Vrkljan
  • 879
  • 10
  • 20
  • 1
    Not everything can or should go in entities. The single responsibility principle still applies. Here's what you could do in your application service: `url = urlGeneratorService.fromPageTitle(title); page = new Page(title, url, layout)`. Then your `PageUrl` value object could implement an `isDerivedFromTitle` method and within `Page`'s constructor you could do... `if (!url.isDerivedFromTitle(title)) throw ...`. The `urlGeneratorService` could construct the `PageUrl` like `new PageUrl(pageTitle, rawUrl)`. – plalx Mar 16 '15 at 16:41
  • What are the arguments against injecting Services into Entities? Can you provide a URL? – ctietze Mar 26 '15 at 13:02

2 Answers2

2

If you consider URL construction as a technical concern, you could have an UrlFactory in the Infrastructure layer

in C# :

public class UrlFactory 
{
  public string CreateUrl(string title)
  {
    var url = // ... construct URL from the title here

    return _pageRepository.NextAvailableUrlLike(url);
  }
}

and then call it from your Application layer service.

If you see it as a Domain concern, Url construction logic could be in a Domain Service instead. The line calling the repository would be moved to the Application layer Service :

public void CreatePage(string title)
{
  var potentialUrl = _urlService.CreateUrl(title);
  var realUrl = _pageRepository.NextAvailableUrlLike(url)
  new Page(title, realUrl, ...); // do whatever you want with the new Page instance
}
guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • 1
    Why would you move calling a Repository to the Application Layer? Since the Repository interface is part of the Domain, it should be okay to call it from within the Domain's bounds. – ctietze Mar 26 '15 at 13:03
  • I wanted to keep the domain service thin and cohesive but you're right, you can also include the URL "unique-ization" part in it. – guillaume31 Mar 27 '15 at 09:00
0

Do not inject factory class into aggregrate, use factory method instead. Then create method "validate(Validator)" in aggregate (Aggregate will only know it can be valided, but it will not implement logic how to do it).

Validator class which will be passed as parameter to your validate method, will need to have one method ->validateObject(this). You pass instance of aggregate into validateObject so it will have access to your properties.

Validator class can have injected repository. When you run validateObject method it will search database for uniquness.

Dariss
  • 1,258
  • 1
  • 12
  • 27