26

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 ?

27

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.

  • Okay, so if I ve business rules (admitting Specification Pattern rules), if it only concerns one entity, I should but the validation in that entity ? It seems weird to be to inject business rules like controlling a good user mail format inside of the user entity. Isn't it ? Concerning the global response, thank you. It got that there's no "default rule to apply", and it really dependes on our usecases. I have some work to do to well distinguish all of this job – mfrachet Sep 8 '16 at 10:45
  • 2
    To clarify, the rules that belong in the entity are only the rules that are the responsibility of that entity. I agree, controlling a good user email format doesn't feel like it belongs in the User entity. Personally, I like to put validation rules like that into a Value Object that represents an email address. User would have a property of type EmailAddress, and the EmailAddress constructor accepts a string, and throws an exception if the string doesn't match the required format. Then you can re-use the EmailAddress ValueObject on other entities that need to store an email address. – Chris Simon Sep 8 '16 at 11:07
  • Okay I see why to use Value Object now. But it means that the value object ows a property that is the business rule managing the format ? – mfrachet Sep 9 '16 at 15:46
  • 1
    Value Objects should be immutable. Generally this means you initialize and validate in the constructor, and for any properties use public get/private set pattern. But you can use language constructs to define equality, ToString process, etc. e.g. kacper.gunia.me/ddd-building-blocks-in-php-value-object or github.com/spring-projects/spring-gemfire-examples/blob/master/… – Chris Simon Sep 9 '16 at 19:24
  • Thank you @ChrisSimon, finally and answer to a real life DDD situation that involves code and not just theory. I've spent 5 days trawling SO and the web for a functional example of creation and saving of an aggregate, and this is the most clear explanation I have found. – e_i_pi Sep 25 '17 at 11:39
1

Neither of your patterns are good unless your services and objects encapsulate some coherent set of responsibility.

First of all say what your domain object is and talk about what it can do within the domain language. If it can be valid or invalid why not have this as a property of the domain object itself?

If for example the objects validness only makes sense in terms of another object then maybe you have a responsibility 'validation rule X for domain objects' which can.be encapsulated in a set of services.

Does validating an object necessitate storing it within your business rules? Probably not. The 'storing objects' responsibility normally goes in a separate repository object.

Now you have an operation you want to perform which covers a range of responsibilities, create an object, validate it and if valid, store it.

Is this operation intrinsic to the domain object? Then Make it part of that object ie ExamQuestion.Answer(string answer)

Does it fit with some other part of your domain? put it there Basket.Purchase(Order order)

Would you rather do ADM REST services? Ok then.

Controller.Post(json) 
{ 
    parse(json); 
    verify(parsedStruct); 
    save(parsedStruct); 
    return 400;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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