I have two models in my current design, student and group. Student and group are both aggregate roots.
A student can be added to a group (method on group aggregate root), and it can be active for either the whole duration of the group or part of it. The group has an invariant which says that "all students added to a given group has to either be active for the full duration of the group, or have start- and end-dates that are inside the groups start and end dates. These connections (group.student) are saved as a child entity (0..*) that belongs to the groups aggregate. So far so good.
However, Group also has an property called "course-code", which is a value object conatining a specific code along with additional (for this example irrelevant) data.
The part I'm struggling with. One business rule of the domain and system is that a student can only have one active group-connection per course-code at any given date. By that I mean that if a student is added to a group the Group.AddStudent(student, daterange)-method has to check if the student has an active connection to another group with the same course-code at the supplied daterange. This is data that doesn't belong to a single group aggregate and neither to the student as you currently can't query student for its active groups by (f.eg) student.groups. I've intentionally modeled it like this to avoid a bi-directional relationship.
What I've thought about
- Put the whole action (add to group) in a domain service that can ask repositories for the data and enforce /hold invariants.
- Pass all groups the student is currently connected to to the AddStudentToGroup-method (or possibly just groups on the same course-code).
- Add a property to a student which would hold all courses it's currently studying with their respective dateranges. This would allow method to ask the passed in student if the supplied date is overlapping any existing range or not. This also has the upside of enabling a student to stand more on its own instead of having a list of group-ids that needs to be loaded each time you require some data about them.
Someone else who's encountered a similar situation? How would/did you solve it?