1

I am modeling a course app, trying to play with DDD and Clean Architecture. So I have Course, which has one or more modules, and each of them has one or more lessons

I created a ModuleLessons aggregate root which is a list of lessons that belongs to a module.

I have the use case where user can access the whole list of lessons within a module, so he access an url like myapp/lessons/{module-id} and this it will endup calling something like moduleLessonsRepository.getById({module-id}) and will render to user a list of lessons which compose that module

As I understand, repository should only deal with the whole aggregate root, not child entities directly. In other words, if Lesson is not an AR, I must not have a LessonRepository.getById()

But I have another use case where user can access something like myapp/lesson/{lesson-id}

But how could I implement if I cant have a repository which returns a lesson by it's id?

I could load the ModuleLessons aggregate and then find lesson within it, but I don't have it's id to query.

I could put module id and lesson id (or maybe just a 'lesson position within it's module) on the url and use that to find the ModuleLessons AR, but I'm puting extra data on the url just to fulfill architectural constraints, is that right?

Finaly, the lesson position within it's module does mater, but this piece of data dont belong to the lesson nor to the module, that's why I created the list itself as the AR, maybe it wasn't the right decision?

magnomp
  • 353
  • 2
  • 8
  • Hi. Have you searched this question before? It has been answered multiple times in different contexts. For instance here: https://stackoverflow.com/questions/2104225/domain-driven-design-how-to-access-child-of-aggregate-root – Daniel Dec 14 '21 at 14:49
  • Please have a look at the following answer, specifically to the ***Did I miss to discover another aggregate root?*** part of it: https://stackoverflow.com/a/67250062/7730554 – Andreas Hütter Dec 15 '21 at 22:49
  • In fact I did consider make Lesson itself an AR (if is that you're suggesting), but I see I will end up like everything is an AR, certainly I missed something – magnomp Dec 16 '21 at 13:23

2 Answers2

0

Your model sounds very structural, e.g. a course consists of modules, modules consist of one or more lessons being taught as part of it, etc. It's not really solving a problem (or at least you've not described one). Could be booking a course, could be attending the lessons of a course, etc ... The other observation is that you seem to be describing what are essentially queries. You will find that most models have a conflict of interest when it comes to reading and writing, one of the main reasons CQRS came about in the first place (not suggesting you adopt that, merely pointing out the obvious). Writing happens to align with use cases and rules that must be upheld at all times (or else). Reading, on the other hand, seems to happen far more liberally, without much consideration for the past use cases that brought the queryable data about. One easy step could be to undo yourself of the shackles that say you can't return lessons by id - simply add whatever code you need to make that happen and don't feel compelled to put that in a box like a repository. Consistency is to be considered, but if the writing imposes the proper transactional boundaries, the reading won't inadvertently observe something it shouldn't. Secondary indexes can help too - they're the sort of thing that can help you find the module id based on the lesson id if you choose to continue to go down the current path.

Yves Reynhout
  • 2,982
  • 17
  • 23
  • I was trying to achieve a model that would support every use case of the application. User wants to buy a course? User wants to attend to a course? Admin wants to add a new lesson to the course? But maybe this is not the best way to model? I'm very new to DDD. As for CQRS it does seem a very good option. In this case I would have commands (mutations) that are implemented on top of domain layer (repositories, AR, entities and so on), and queries implemented directly on top of persistence which return a DTO tailored to that specific use case is that right? – magnomp Dec 16 '21 at 13:28
0

If it is just about reading data (e.g. showing data to a user), you can always bypass the whole aggregate repository and use whatever whatever appropriate read queries you need. Only, if your use case needs to manipulate data go through the aggregate repository to retrieve a full aggregate in order to make sure transactional consistency inside this aggregate as well as business rules are applied when changing said aggregate.

Also, it should be considered that if you do you have valid use cases where you would directly change (not read) an entity inside an aggregate without the need of considering business logic that needs to be owned by the parent aggregate root, you might have missed to discover this entity being modeled as an aggregate on it's own. See also, https://stackoverflow.com/a/67250062/7730554

Andreas Hütter
  • 3,288
  • 1
  • 11
  • 19