Disclaimer: I'm not a DDD expert, but I'm going to do my best here to answer your questions.
Let's use an Online Book Retailer as an example. A book seller is offering to sell books on his website, but he doesn't print the books himself. Instead, he has a long list of "Book Suppliers" who print and ship the books to his warehouse, were he packages them and ships them to the customers all around the world.
Could you imagine the teams working in that company?
- The listing team: The team that selects which books they should list on the Website, depending on the stock of the suppliers.
- The fulfillment team: responsible for collecting the orders from the Website and managing the orders' life cycle.
- The commercial team: these are the guys who handle adding or removing suppliers from the system.
- The marketing team: responsible for the campaigns and offers.
- The warehouse team: responsible for collecting books from the suppliers and shipping them.
Each team will use its own terms to describe what it is doing. Teams will use the domain language or the "Ubiquitous Language", as Eric Evans calls it. Normally, each team will have a different mental model of what an entity is. For example:
Listing team: BOOK(book_id, ISBN, title, price, weight, length, width )
Fulfillment team: BOOK(book_id, totalPrice, sold_quantity)
Shipping team: ITEM(book_id, delivery_date) --> they refer to the book entity as "item"
A sub-domain is one particular part of the domain, in which some users use a certain Ubiquitous Language. When the language changes, this is an indication the you are crossing into another sub-domain.
What if two teams use the same terms? Both the fulfillment and listing teams call book "book". You will have to ask them, "what is a book for you? What are its key attributes? How is it used?"
Answers to these question will result in two different or similar domain models. The greater the difference between the two models, the more indication that these are two different Bounded Contexts/sub-domains. The opposite is also true. The more similar the two models are, the more likely that they should be part of the same sub-domain.
This may result in very interesting findings in your model. You may discover that inside the fulfillment sub-domain, the orders pass through different states (new -> requested -> shipped). Maybe each of these states requires complex management and different attributes, so you may divide this sub-domain into several other sub-domains.
This brings out a question of what the size of the sub-domain(s) should be. The answer is "let the domain model decide that". Whenever you see a change in the UL and model, then draw a boundary for that sub-domain.
Remember that DDD is all about the business, the people and the communication(s) between them. Let that drive your model.
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.
Best Answer
I would argue your requirement has nothing to do with DDD. DDD is about solving complex business rules in complex business domain. There are no business rules in requirement you are trying to implement.
In context of DDD, your requirement will be part of Application Services layer and not part of any of the bounded contexts in the domain layer. As such, no rules or practices of DDD should apply to it. So thinking about it as different bounded context doesn't make sense.