In Domain Driven Design, what is the best way to create the database entry when a new object is created inside an Aggregate?
As an example, consider a Student
entity representing a student at a university. The student takes courses, and these are part of the student aggregate as a list of CourseParticipation
value objects. Those objects track the student's participation rate and final grade. When the student registers for a new course, we call Student.participateIn(Course)
. In Java, it looks like this:
public class Student {
private List<CourseParticipation> courseParticipations;
public void participateIn(Course course) {
// Business logic: Check if the student is allowed to participate
courseParticipations.add(new CourseParticipation(this, course));
}
}
Now the new course participation must be stored in the database in a table called course_participations
. The usual way to do this in my application is to call CourseParticipationRepository.save(CourseParticipation)
. How do I make this call when the object is created inside an aggregate?
Options I could think of or read on the internet:
Inject CourseParticipationRepository
into Student
and save inside the participateIn(Course)
method.
Good:
student.participateIn(course)
and are doneBad:
Student
objects ... is it a good idea to give each one a reference to the repository singleton?Return the unsaved CourseParticipation
object and let callers save it
Good:
Bad:
CourseParticipation
objectLet StudentRepository.save(Student)
make the call to CourseParticipationRepository.save
for each CourseParticipation
object
Good:
CourseParticipation
object is not exposed to the caller of participateIn
Bad:
StudentRepository
needs access to the internal list of CourseParticipation
objects, which would otherwise not be exposedRedesign the database tables or use an object database
I read this advice in a lot of posts about Domain Driven Design. It makes sense for greenfield projects. But it is not an option for the application I work on.
What is the best or most common option? Are there other solutions?
The agggregate shouldn't care about the table structure. That (and the requirement that the aggregate is a unit of consistency) ultimately means that the third approach is the way.
The StudentRepository needs access to the internal list of CourseParticipation objects, which would otherwise not be exposed
The list of course participations is part of the Student
. Saving the student implies saving the participations.
The save method can become very complex if the aggregate maps to many database tables Avoiding unnecessary database calls (i.e., saving unchanged entries) can be hard
Them's the breaks, unfortunately.
It is basically the case that DDD aggregates assume an object store (or an event store, if event sourcing) in the sense that that abstraction matches the minimum functionality required for saving/retrieving an aggregate. A relational schema, especially one which aspires to higher normal forms, is going to introduce overhead/complexity.