1

It's my first try at DDD and I would like to get advise on modeling issues.

Here is my domain: The management of multiple schools.

  • A school has multiple students, teachers, …
  • For a school, there is (every year) a new schoolYear (which has a value object holding 2017-2018 for instance)
  • Classes, student scores are related to a schoolYear (for instance, Student A can be in class C1 in school year 2017-2018 and in classe C2 next year 2018-2019)

My first doubt is in the modeling of schoolYear.

I already have the school entity as a root aggregate. My first approach was to make the school aggregate handle the addition of schoolYear (so that I can avoid duplicates, or that I can create the next schoolYear, …)

=> schoolYear is part of the school aggregate

But then I had to model classes and student scores… which depend on the schoolYear.

So in my classes aggregate, I have to hold a reference to a schoolYear… which breaks the rule that states

"Cannot hold a reference to an internal entity from an outside aggregate."

In my domain, a lot of entities are depending on a specific schoolYear. Maybe it should be an aggregate…

On the other hand, the schoolYear of a given class is used for searching classes.

Can I get some advice about this modeling issue?

Another depending question is about the identity of schoolYear.

  • Option A: Generate a UUID as is and holds schoolId + year as attributes
  • Option B: Id is schoolId and year, so we cannot create the same schoolYear twice for the same school and the same year

Any advise on this?

Thanks a lot helping me enter the DDD world!

Golo Roden
  • 140,679
  • 96
  • 298
  • 425
Stéphane
  • 514
  • 5
  • 16

2 Answers2

6

To answer your question I had to make a few assumptions. So maybe my answer doesn't perfectly reflect your actual intentions, but I have tried to follow as closely as possible to what you described. Hence, please take my answer with a grain of salt.

First of all, what caught my eye is that you are thinking about an object model. Domain-driven design is less about the objects, but rather about the processes. So while you have focused on the nouns, I'd suggest to focus more on the verbs: What are the things that can have happened in your domain?

While thinking about this question it became clearer to me that the central thing is the class: Yes, you need to found a school initially, but once it has been founded, there aren't many actions taking place over time. Maybe it gets renamed, but that's it.

The same is true for a schoolYear. My assumption is (I don't know whether this is correct for your concrete scenario) that there is for example only a single school year 2017/2018 which is valid for all schools. Maybe this is different in your scenario, because different schools can have different school years, but I assumed them to be the same overall.

Moreover, there are teachers and students, which are pretty simple things as well.

This leaves us with class. Originally, a class gets setup for a specific school, and for a specific school year. Then you assign a teacher to it (I assume that there is only one teacher per class, and I'm also assuming that a single teacher can be assigned to multiple classes). Students then join the class, or leave. And finally, once in a year, you move the class to the next school year.

This means that class should be an aggregate, which knows to which school and school year it belongs, and which has a teacher, and contains a list of enrolled students. You have commands on this class, such as:

  • setup (school, schoolYear)
  • assign (teacher)
  • join (student)
  • leave (student)
  • move (schoolYear)

Then, school is just another aggregate, which has commands such as:

  • found
  • rename

Again, schoolYear can be a simply aggregate, which has commands such as:

  • begin
  • end

And for teacher and student - well, they are aggregates as well. I won't list the commands for them here explicitly, but I think you can come up with them on your own.

Finally, I believe that, as said, you should rather focus on the verbs and what can happen, than on the nouns. Once you do this you need to think about which verbs can happen at the same time, because they don't interfere with each other, and which verbs can't happen concurrently. This is what leads you to how to define your aggregates, as aggregates are transactional boundaries that limit which actions can happen at the same time, and which ones need to be run sequentially.

There is a nice description of how to do modeling with your team in the documentation of wolkenkit, a CQRS and event-sourcing framework for JavaScript and Node.js that my colleagues and I are working on. Maybe it is helpful for you.

Hope this helps :-)

Golo Roden
  • 140,679
  • 96
  • 298
  • 425
  • Thanks for the reply. I think you're right : I'm more focused on nouns and should focus on verbs... bad old habits... Over the domain, I wasn't complete enough. I get what you proposed but in my scenario, each school year is not present for each school : A schoolyear can exists for SchoolA but not yet for SchoolB. (in my web interface, I must be able to list available schoolyears for a given school) Furthermore, classes are more groups of students. And I have to store which student was in which classes for a given schoolyear. So I cannot really use the move(year) method. – Stéphane Dec 13 '17 at 08:33
  • That's what I meant when I said, I had to make a number of assumptions. Anyway, I think if you try to approach the problem more from a domain point of view, and less from a technical point of view, and if you focus more on verbs rather than nouns, you should be able to figure out how to model it on your own now ;-) … PS: If the answer was helpful, I would be more than happy if you could vote it up :-) – Golo Roden Dec 13 '17 at 08:56
  • Done. Any advise on the identity of SchoolYear? see 2 options in initial question. Thanks again. – Stéphane Dec 13 '17 at 09:00
  • PS: [Execution in the kingdom of nouns](http://steve-yegge.blogspot.de/2006/03/execution-in-kingdom-of-nouns.html) me be a good read as well :-) – Golo Roden Dec 13 '17 at 09:00
  • Regarding your point that you need to be able to figure out which student was in which class for a given school year - you get these information from the replay of events that led to the current state (as long as you do event-sourcing). Regarding your question with the two options, I'm not sure yet, I need to think about this a little bit more. – Golo Roden Dec 13 '17 at 09:02
  • After some thoughs... I always come back to the same issue. I build an aggregate root following what it can do with it (verbs) and respect invariants.. Every thing looks fine. Then I find another aggregate that must have a reference to an entity that's inside another aggregate... And Bam, I'm stuck.Example: If I model SchoolYear inside School aggregate. How do I reference SchoolYear in other aggregates (for instance in a cursus that's given only for this particular SchoolYear)? – Stéphane Dec 14 '17 at 15:57
-1

For your question about the identity of SchoolYear. If you are sure that schoolId will not change once it has been created then I would suggest to go with schoolId-year instead of UUID, as it is more descriptive than UUID. But make sure that the combination, i.e schoolId-year is globally unique if SchoolYear is an aggregate.

msmani
  • 730
  • 1
  • 7
  • 24
  • I don't know yet if SchoolYear will be an aggregate (it was my initial question). But I'm sure schoolId + year is unique through the system... That means that each reference to this aggregate will have to store the 2 values (schoolId and year). – Stéphane Dec 14 '17 at 15:22
  • So it means that for each referencing entities, I have to hold a reference to both SchoolId and Year. That's right? Thanks for your help – Stéphane Dec 14 '17 at 16:03