4

After reading lot of posts, I realised if an aggregate root exists for a concept/context, we need to have a single repository for that whole concept/context.

If thats the case, I see there won't be any repositories for the internal entities. If so, how these internal entities are saved to database?

I have a many internal entities under the aggregate root. So, wondering If I need to have all the saving of the internal entities under the aggregate root repository, it's going to be bloated. Please suggest what can be done in this case.

Also, my internal entities will go to each table of their own at the persistence level. Please correct me if I'm not allowed to store internal entities this way.

Example
Consider I have a Restaurant as a aggregate root. It can group an entity named Review. A review exists for a restaurant and can’t exist without it.

Here if Review is an internal entity and there can be numerous reviews for a restaurant, Reviews will be saved in a separate table. But as there will be only one Restaurant Repository for Restaurant aggregate root, how/where to handle saving reviews.

Ayyappa
  • 1,876
  • 1
  • 21
  • 41

3 Answers3

9

I agree with some of the points made in other answers about that you probably would not want to model it like that, but I think your question itself has not been answered yet. And for an example it is perfectly fine.

So here goes:

You don't create repositories (as in DDD repositories) for entities, only for the aggregate root. So in your example the application layer could be something like:

Restaurant restaurant = restaurantRepository.findById(23)
restaurant.addReview(review)
restaurantRepository.save(restaurant)

Notice also that actions are done on the aggregate root. So in general, commands in your application layer would normally be: load aggregate, execute action on it, save it

The aggregate is saved as a whole and the entities with it. What happens behind the repository is dependant on your infrastructure. And of course since the repository belongs in the domain, we don't care about that infrastructure there.

So then the implementation in the infrastructure layer (these are just some examples of how it could be):

  1. In a documentDB you would have an implementation of the repository which just saves the document to the DB as a whole. (Document stores and DDD match really well here since aggregate = document)
  2. If you were to serialize your aggregate to disk in a simple text file, you might have a repository implementation with a serializer per entity, value object to do the serialization.
  3. If you have an RDBMS you would indeed want the entities to be stored in their own table. Here you could use:

    • An ORM framework and just save the root entity and let the framework automatically save / delete / update the child entities
    • Use a DAO pattern and create a DAO per table / entity
    • ...

Also have a look at this question

Also, in some cases it can happen that you would split up an aggregate for technical reasons only. If you were to have an aggregate with tons of entities and it gets to heavy to handle it well, then you might want to split it in two aggregates purely for technical reasons. But that is something that you try to avoid and only do when really necessary.

niekname
  • 2,528
  • 1
  • 13
  • 27
  • 1
    This is what I'm expecting! Thanks for understanding the stated problem. So here, I can have addReview to Restaurant repository and internally I have a DAO to write review to a separate table/collection(NOSQL) right? – Ayyappa Mar 27 '20 at 17:53
  • 1
    addReview is not a repository method, it is an action on the restaurant aggregate. After the action the whole aggregate restaurant is saved again via the save method on the repository. Behind that interface you can indeed have a specific DAO to save the review object itself – niekname Mar 27 '20 at 17:56
  • But if we save the entity and aggregate root as a whole... what happens with entity's id? For what do we need an id in the review entity, if it is saved in same table as aggregate root? – Linksx Apr 13 '22 at 17:12
  • 1
    Entities are unique based on their ID and nothing else. The ID on the entity in the domain model is not the same as a persistence ID. You could for example have a CellPhone entity which has an IMEI which is used in the domain model as its ID. When persisted you can chose to add an extra persistence ID (GUID, or incrementing int, ...) purely for persistence reasons. – niekname Apr 14 '22 at 09:15
2

I'd like to complement the answer by CPerson.

Such discussions without going to the context are often meaningless. Each case is context-specific and it rarely boils down to an issue with persistence.

In the mentioned case, I would never model Review as an entity in the Restaurant aggregate. It is not a persistence issue, it is a modelling issue.

There are a few DDD books out there and I believe that reading a few blog posts isn't enough to understand both strategic and tactical patterns of DDD. One of those patterns is indeed the Aggregate pattern. In short, an Aggregate is the consistency boundary. Aggregate is always context-specific (just as anything else).

If you are modelling a restaurant management system or a food delivery system, reviews would probably be in a separate context. There's no such context as the "restaurant context". That's the whole point of the Bounded Context pattern. In your example, it is, probably, the restaurant review context. What happens with reviews has nothing to do with food, opening hours and table bookings.

If you are modelling something like TripAdviser, you only have reviews, basically. In such a case, reviews are more or less agnostic to what is being reviewed. Then, your model is completely different.

The number of reviews is ever-growing, so putting all reviews as entities to some aggregate won't make much of a sense. Again, Aggregate is the consistency boundary. Would you say that a review cannot be posted if another review is one star? I don't think so. What is the invariant that you're trying to protect in the Restaurant aggregate, concerning reviews? Would you need to limit the reviews count of change the state of a Restaurant based on those reviews? I don't think it's the case. So, a review could be an aggregate on its own, since all reviews are completely independent of each other.

A Restaurant in the review aggregate could be a simple value object that holds the restaurant id. On the construction of this value object, you'd ensure that the given restarant indeed exists and open for reviews. You would indeed be required to clear reviews when the restaurant disappears. But it is also context-specific. The restaurant might close but you keep the reviews anyway.

Alexey Zimarev
  • 17,944
  • 2
  • 55
  • 83
  • I'm not yet clear but I need to go through your answer again. Please consider that in this example Reviews are entities which have no external dependency other than with restaurant. It might be a bad example but please consider this constraint. As per your answer, does it mean an aggregate never hold other entities but only value objects? If not, who is responsible for storing those entities? Question I have is do we need to overload the Aggregate Root Repository with the code for saving these internal entities too? – Ayyappa Feb 28 '20 at 09:52
  • If you want another example, please consider the standard one. Order and Line Items. Here consider that Line Items can be in huge number. Line Item will have a unique Id and is a not a value object, instead its an entity on its own. Now where should be the code for saving this Line Item should be? Do I need to overload Order Repository with the Line Item saving code? – Ayyappa Feb 28 '20 at 10:01
  • _it mean an aggregate never hold other entities but only value objects_: no, I meant that in that particular case it can be the opposite. A review Review is an aggregate and you have a lot of them. Each review can gold a reference to a restaurant as a value object. The Restaurant itself is another aggregate. – Alexey Zimarev Feb 28 '20 at 11:01
  • _If you want another example, please consider the standard one. Order and Line Items. Here consider that Line Items can be in huge number._: again, it is a theoretical example and the first rule of DDD is to work on a model for a specific business domain. Each business is different and therefore models are different. The rule for aggregates is the same. Aggregate is a transactional consistency boundary. Without external references it is always value. That's it. Persistence plays no role here (except transactions of course). – Alexey Zimarev Feb 28 '20 at 11:04
  • Ok, for ease, can you please give me an example where Aggregate can have entities? Probably I'm missing something. As per your text it looks like "If an entity is going to be a big list, it always ends up as a separate aggregate root on its own.". But I'm thinking what if that particular entity doesn't make any sense individually but valuable only under another entity group(Aggregate). – Ayyappa Feb 28 '20 at 11:19
  • Great! So, can you please share one of the ideas on how the OrderLine entity can be saved internally at persistence level? Is it through OrderRepository? – Ayyappa Feb 28 '20 at 12:24
  • Entities are accessed only through the aggregate root. An aggregate is retrieved and persisted entirely, with all elements inside it. As I said, it is a consistency boundary so each operation on an aggregate is one transaction. I wrote it several times, I don't know what else to add. – Alexey Zimarev Feb 28 '20 at 16:50
  • "An aggregate is retrieved and persisted entirely, with all elements inside it" - I think this makes it clear. Thanks for explaining again. I need to see how long list of entities within an Aggregate root can be handled. – Ayyappa Feb 29 '20 at 09:22
  • @Ayyappa one way to look at aggregate persistence that helps understanding the concept of consistency boundary is to imagine (or do it in practice) that you store them as json documents. There is only one document per aggregate. You read it and save it all at once and you can't read only an entity from within a document. Your repository only allows saving the aggregate, not the root and entities separately. I think NoSql makes it simpler to implement, but you can achieve the same behavior with ORMs and relational databases. – Francesc Castells Feb 29 '20 at 22:32
1

If thats the case, I see there won't be any repositories for the internal entities. If so, how these internal entities are saved to database?

An Aggregate represents a consistency boundary. If an Entity is owned by an Aggregate and is required for ensuring consistency then it should be persisted with the Aggregate Root.

So, wondering If I need to have all the saving of the internal entities under the aggregate root repository, it's going to be bloated.

Yes, it would be. You can consider using an ORM if this is a real issue. The ORM will maintain in memory the graph rooted at your Aggregate Root and will ensure that changes are persisted as necessary.

Also, my internal entities will go to each table of their own at the persistence level. Please correct me if I'm not allowed to store internal entities this way.

Try to think of your domain separately from your persistence strategy. You have a domain model that you map into a relational schema. The relational schema should not drive the design of the domain.

CPerson
  • 1,222
  • 1
  • 6
  • 16
  • True that we shouldn't mix with persistence strategy. But there will be only one repository to handle the persistence of all entities within an aggregate root. How its is practically handled? Any pseudo code for the example(Restaurant/Reviews) stated will be helpful. – Ayyappa Feb 27 '20 at 19:32