2

I have seen lot of discussions regarding this topic but i couldn't get a convincing answer. The general advice is not to have repository inside a domain object. What about an aggregate root? Isnt it right to give the root the responsibility to manipulate the composed objects? For example, i have a microservice which takes care of invoices. Invoice is an aggregate root which has the different products. There is no requirement for this service to give details about individual products. I have 2 tables, one to store invoice details and other to store products of those invoices. I have two repositories corresponding to the tables. I have injected product repository inside the invoice domain object. Is it wrong to do so?

falcon
  • 1,332
  • 20
  • 39
  • Why the down vote? – falcon Jun 03 '16 at 13:37
  • Possible duplicate of [DDD - the rule that Entities can't access Repositories directly](http://stackoverflow.com/questions/5694241/ddd-the-rule-that-entities-cant-access-repositories-directly) – guillaume31 Jun 07 '16 at 08:27
  • @ArulKumaran I am not sure why someone downvoted but I believe this is because you are looking for a "convincing answers" to discouraging you from using an anti-pattern in tactics, when you don't seem to understand the basics of DDD. I think if you just open the "red book", you will find all answers there, there is nothing new here. Don't take as an offence. – Alexey Zimarev Jun 09 '16 at 13:49

2 Answers2

6

I see some mistakes according to DDD principles in your question. Let me try to clarify some concepts to give you hand.

First, you mentioned you have an Aggregate Root which is Invoice, and then two different repositories. Having an Aggregate Root means that any change on the Entities that the Aggregate consists of should be performed via the Aggregate Root. Why? That's because you need to satisfy some business rule (invariant) that applies on the relation of those Entities. For instance, given the next business rule:

Winning auction bids must always be placed before the auction ends. If a winning bid is placed after an auction ends, the domain is in an invalid state because an invariant has been broken and the model has failed to correctly apply domain rules.

Here there is an aggregate consisting of Auction and Bids where the Auction is the Aggregate Root.

If you have a BidsRepository, you could easily do:

var newBid = new Bid(money);
BidsRepository->save(newBid);

And you were saving a Bid without passing the defined business rule. However, having the repository just for the Aggregate Root you are enforcing your design because you need to do something like:

var newBid = new Bid(money);
auction.placeBid(newBid);
auctionRepository.save(auction);

Therefore, you can check your invariant within the method placeBid and nobody can skip it if they want to place a new Bid. Afterwards you can save the info into as many tables as you want, that is an implementation detail.

Second, you said if it's wrong injecting the repository into a Domain class. Here a quick explanation:

The repository should depend on the object it returns, not the other way around. The reason for this is that your "domain object" (more on that later) can exist (and should be testable) without being loaded or saved (that is, having a dependency on a repository).

Basically your design says that in order to have an invoice, you need to provide a MySQL/Mongo/XXX instance connection which is an infrastructure detail. Your domain should not know anything about how it is persisted. Your domain knows about the behavior like in the scenario of the Auction and Bids.

These concepts just help you to create code easier to maintain as well as help you to apply best practices such as SRP (Single Responsibility Principle).

mgonzalezbaile
  • 1,056
  • 8
  • 10
5

Yes, I think it is wrong.

Domain should match real business model and should not care how data is persisted. Even if data internally are stored in multiple tables, this should not affect domain objects in any way.

When you are loading aggregate root, you should load related entities as well in one go. For example, this can easily be achieved with Include keyword in Entity Framework if you are on .NET. By loading all the data you ensure that you have full representation of business entity at any given time and you don't have to query database anymore.

Any changes in related entities should be persisted together with aggregate root in one atomic operation (usually using transactions).

Kaspars Ozols
  • 6,967
  • 1
  • 20
  • 33