60

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 ?

mfrachet
  • 1,481
  • 3
  • 15
  • 21
  • Related post - [How does a service layer fit into my repository implementation?](https://stackoverflow.com/q/6138660/465053) – RBT Aug 28 '18 at 10:45
  • 1
    "to inject a repository and a domain model in an application service." What do you mean by injecting a "domain model" somewhere? AFAICT in terms of DDD domain model means the whole set of concepts from the domain and interactions between them them that are relevant for the application. It's an abstract thing, it's not some in-memory object. You cannot inject it. – Alexey Aug 17 '19 at 23:06

4 Answers4

64

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.

Chris Simon
  • 881
  • 7
  • 4
  • 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 08 '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 08 '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 09 '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. https://kacper.gunia.me/ddd-building-blocks-in-php-value-object/ or https://github.com/spring-projects/spring-gemfire-examples/blob/master/spring-gemfire-examples-common/src/main/java/org/springframework/data/gemfire/examples/domain/EmailAddress.java – Chris Simon Sep 09 '16 at 19:24
  • 1
    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
  • I would argue that `otherAggregateId` is not part of the UL (usually), so the definition of your domain service is already broken. – Chan-Ho Suh Jan 10 '19 at 14:28
  • 1
    What if the business validation concerns only one entity and needs database call, could I pass repository into entity method directly ? `Order.AddProduct(Product product, ISomeRepository repository)` ? – Muflix May 04 '19 at 10:30
  • I have a doubt when you tell persist() shouldn't belong to the domain, because it is not part of the business logic. But the, if an action affects only to one aggregate, where is the place to persist the data? Perhaps in the application service? – Álvaro García Aug 04 '22 at 09:25
20

There is problem with accepted answer:

Domain model is not allowed to depend on repository and domain service is part of domain model -> domain service should not depend on repository.

What you should do instead is assemble all your entities that are needed for business logic execution already in application service and then just provide your models with instantiated objects.

Based on your example it could look like this:

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repositoryA = repositoryA
    this.repositoryB = repositoryB
    this.repositoryC = repositoryC
  }

  // any parsing and/or pre-business validation already happened in controller or whoever is a caller
  executeUserStory(data){
    const entityA = this.repositoryA.get(data.criterionForEntityA)
    const entityB = this.repositoryB.get(data.criterionForEntityB)

    if(this.domainService.validateSomeBusinessRules(entityA, entityB)){
      this.repositoryC.persist(new EntityC(entityA.name, entityB.surname))
    }
    // ...
  }
}

So, rule of thumb: Domain model does not depend on outer layers

Application vs Domain service From this article:

  • Domain services are very granular where as application services are a facade purposed with providing an API.

  • Domain services contain domain logic that can’t naturally be placed in an entity or value object whereas application services orchestrate the execution of domain logic and don’t themselves implement any domain logic.

  • Domain service methods can have other domain elements as operands and return values whereas application services operate upon trivial operands such as identity values and primitive data structures.

  • Application services declare dependencies on infrastructural services required to execute domain logic.

sMs
  • 301
  • 2
  • 4
  • 12
    Not quite correct - the original Blue Book does not dictate that domain services can't depend on a repository - and in fact the Vaugh Vernon Red Book (Implementing Domain Driven Design) includes an example in Chapter 7 of a Domain Service depending on a repository (see `BusinessPriorityCalculator`). The key point though is that the repository _interface_ is in the domain layer - so the domain service can depend on the interface. The repository implementation is in the infrastructure layer, and the domain service should not depend on the implementation. – Chris Simon Jun 16 '20 at 00:37
  • 1
    @ChrisSimon I will take a look at Red book, tnx for reference. Does repository interface belong to **application** or **domain** layer? My opinion is that they are artifacts of **application layer**. I find hard to justify using of repository in domain layer, even for some exotic corner cases, such necessity is for me just a symptom of badly modeled aggregates and possible invariant endangerment. If one have well-defined aggregate boundaries, one will have everything needed by just fetching those aggregates and providing them to the domain layer (to the services) for further processing. – sMs Jun 17 '20 at 04:12
  • @ChrisSimon Some time latter and I still have a problem with repo consumption in Domain model layer. My logic here is pretty simple: "I want to be able to unit test Domain model without mocking any dependency" And if I allow Domain service to use it than I have to mock impl of Repo. Just revisited question and I see that nothing changed for me, even if DDD authorities are stating something else :D – sMs Feb 22 '21 at 09:53
  • 2
    Fair enought! None of this stuff are universal laws, like the laws of physics - they're just patterns and practices that have helped folks in particular contexts. If it doesn't work for you, don't do it! The challenge with a lot of these conversations is people saying "XYZ means you can't do ABC" but because XYZ is a made up term to encompass a fuzzy idea that someone once found helpful "can't" is a bit meaningless. What we should say is "According to J Smith's definition of XYZ, there are risks to doing ABC, which are 1, 2, 3." But that takes a bit more thought! – Chris Simon Feb 22 '21 at 22:43
  • 1
    @ChrisSimon man you scored it to the center! That what you have described is the most common problem that I am facing, people just take things for granted and have hard times to utilize the brain and go behind some guideline, heuristic or conceptual framework. I will save your analysis of the problem for all my future conversations about such subjects and quot you :D – sMs Mar 02 '21 at 06:41
  • @ChrisSimon The key point is that the repository interface belongs to the domain layer, implemented by the infra layer (Dependency inversion). That's ok. Question: Is it a good practice to inject the repository in the Aggregate root? – CelinHC Jun 01 '21 at 18:35
  • I think if you're tempted to inject a repository into an aggregate root your scenario might be more naturally expressed in a domain service as it suggests that there is an interaction btwn two aggregates (the AR of the repo you're injecting, and the AR you're injecting into.). But as with all things there are exceptions, and rather than focussing on 'best practice', I'd focus on 'how naturally does this code express the domain knowledge'? Separately, practically speaking, since aggregates are usually instantiated by persistence layers (e.g. an ORM) injecting isn't always convenient. – Chris Simon Jun 03 '21 at 03:08
  • @ChrisSimon Each approach here comes with its own set of trade-offs. If you inject your repository into the domain service then you sacrifice domain model purity for domain model completeness and performance. But if you split your domain logic between the application service and your domain model then domain model completeness is sacrificed while maintaining domain model purity. If you didn't split the domain logic all your reads would be upfront and performance would be the cost in a complex domain. Every decision has trade-offs and knowing those helps us make an informed decision. – holmberd Oct 13 '21 at 15:27
2

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;
}
Ewan
  • 70,664
  • 5
  • 76
  • 161
0

Thinking about Dependency Inversion Principle, and also the Interface Segregation Principle : the notion of Interface is more a client code concern ! Meaning the client code has a specific need of an external service, that then it gets via an Interface that is specific to its needs, not more, not less. Which means that, this is the client code (team) that keeps master (or "owner") of this Interface definition.

Here, if we have an Application1, it may not needs the same exact Interface definition that an Application2, in order to access the RepositoryXxx implementation ! So the Interface IRepositoryXxx definition could potentially not be the same for each of these applications, that act as client code to this Interface. So each Application could have its "own" Interface definition for its proper needs of access to the RepositoryXxx.

And as the Domain Layer should always be Application agnostic, keeping being exploitable by any Applications(use cases needs). It would NOT be logic to keep any of those Interfaces IRepositoryXxx into the Domain layer because these Interfaces could be specific for each given Application ! And there we are : i wouldn't put the IRepository Interfaces into the Domain Layer, because they are not part of its concerns, it's a use case concern, so a specific Application concern and need. I would put these Interfaces into the Application Layer, OR a specific layer between Domain And Application layer, for the sake of keeping opened and reusable.

Keeping therefore, more S.O.L.I.D. .

Now, if the Domain Layer really needs to access a Repository, i think it should have its own IRepository Interface, but not sharing it or exposing it to the externals layers.

So each one its needs, each one its Interfaces, following SOLID : DIP and ISP.