Domain-Driven Design – DDD Repositories in Application or Domain Service

designdomain-driven-design

I m studying DDD these days, and I m having some questions concerning how to manage repositories with DDD.

Actually, I have met two possibilies :

First one

The first way of manage services I've read is to inject a repository and a domain model in an application service.

This way, in one of the application service methods, we call a domain service method (checking business rules) and if the condition is good, the repository is called on a special method to persist / retrieve the entity from the database.

A simple way of doing this could be :

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repository = repository
  }

  postAction(data){
    if(this.domainService.validateRules(data)){
      this.repository.persist(new Entity(data.name, data.surname))
    }
    // ...
  }

}

Second one

The second possibility is to inject the repository inside of the domainService instead, and to only use the repository through the domain service :

class ApplicationService{

  constructor(domainService){
    this.domainService = domainService
  }

  postAction(data){
    if(this.domainService.persist(data)){
      console.log('all is good')
    }
    // ...
  }

}

class DomainService{

  constructor(repository){
    this.repository = repository
  }

  persist(data){
    if(this.validateRules(data)){
      this.repository.save(new Entity(data.name))
    }
  }

  validateRules(data){
    // returns a rule matching
  }

}

From now, I m not able to distinguish which one is the best (if there's one best) or what they imply both in their context.

Can you provide me example where one could be better than the other and why ?

Best Answer

The short answer is - you can use repositories from an application service, or a domain service - but it is important to consider why, and how, you are doing so.

Purpose of a Domain Service

Domain Services should encapsulate domain concepts/logic - as such, the the domain service method:

domainService.persist(data)

does not belong on a domain service, as persist is not a part of the ubiquitious language and the operation of persistence is not part of the domain business logic.

Generally, domain services are useful when you have business rules/logic that require coordinating or working with more than one aggregate. If the logic is only involving one aggregate, it should be in a method on that aggregate's entities.

Repositories in Application Services

So in that sense, in your example, I prefer your first option - but even there there is room for improvement, as your domain service is accepting raw data from the api - why should the domain service know about the structure of data?. In addition, the data appears to only be related to a single aggregate, so there is limited value in using a domain service for that - generally I'd put the validation inside the entity constructor. e.g.

postAction(data){

  Entity entity = new Entity(data.name, data.surname);

  this.repository.persist(entity);

  // ...
}

and throw an exception if it's invalid. Depending on your application framework, it may be simple to have a consistent mechanism for catching the exception and mapping it to the appropriate response for the api type - e.g. for a REST api, return a 400 status code.

Repositories in Domain Services

Notwithstanding the above, sometimes it is useful to inject and use a repository in a domain service, but only if your repositories are implemented such that they accept and return aggregate roots only, and also where you are abstracting logic that involves multiple aggregates. e.g.

postAction(data){

  this.domainService.doSomeBusinessProcess(data.name, data.surname, data.otherAggregateId);

  // ...
}

the implementation of the domain service would look like:

doSomeBusinessProcess(name, surname, otherAggregateId) {

  OtherEntity otherEntity = this.otherEntityRepository.get(otherAggregateId);

  Entity entity = this.entityFactory.create(name, surname);

  int calculationResult = this.someCalculationMethod(entity, otherEntity);

  entity.applyCalculationResultWithBusinessMeaningfulName(calculationResult);

  this.entityRepository.add(entity);

}

Conclusion

The key here is that the domain service encapsulates a process that is part of the ubiquitous language. In order to fulfill its role, it needs to use repositories - and it's perfectly fine to do so.

But adding a domain service that wraps a repository with a method called persist adds little value.

On that basis, if your application service is expressing a use case that calls for only working with a single aggregate, there is no problem using the repository directly from the application service.