domain-driven-designaggregateroot

Domain driven design: How to add a new database entry for an Aggregate?


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:

Bad:

Return the unsaved CourseParticipation object and let callers save it

Good:

Bad:

Let StudentRepository.save(Student) make the call to CourseParticipationRepository.save for each CourseParticipation object

Good:

Bad:

Redesign 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?


Solution

  • 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.