Accessing data for validation and determining default values using DDD
Asked Answered
R

2

4

Lets take a hypothetical project such as a student enrollment system for a music school.

  • Within the system there needs to exist a way to enter student data for a new student interested in receiving instruction at this school and we can assume that students may have access to this functionality through a web site.
  • Part of the enrollment process asks the student the type of instrument for which she would like to receive instruction.
  • As a result of entering information about her preferred musical instrument the behavior will be to assign a default instructor who is assigned to that group of instruments.
  • Before completing the enrollment request the student will be also be able to change the assigned instructor to different instructor who also is listed as able to give instructed for her selected instrument.

Given this description, the part I'm having a little trouble with is how manage the list of possible instructors out of the ones who are able to give instruction for particular instrument in terms of choosing a default instructor for a new student first, and secondly how to use this same data when it comes time to validate the selected instructor before an enrollment is submitted. Other technical constraints are that up to this point I've been using a self validating technique very similar to the one presented in Jimmy Nilsson's book Applying Domain-Driven Design and Patterns so it is mainly unclear to me the best way to go about continuing following self validating techniques when access to external data is necessary which I would normally see as outside of the scope of the entity being tested for validity.

The options I'm aware of:

  1. Move validation outside of the entity itself. Perhaps validation is moved into a set of services or a single service per entity which analyses current state of the whole entity and deems it valid or not and provides for domain events or other value objects to be emitted that give more insight about what validation rules have been broken. In this case I'm still a bit uneasy about how the service would get access to the necessary information about instructors
  2. Allow for access to a instructor repository from necessary entities that are attempting to perform this validation.
  3. Create a service that allows access to a list of instructors by instrument category. Alternatively create two separate services, one that returns whether a given instructor is in the list of instructors for a given category, and another which returns the default instructor for a given category.
  4. Load a list of instructor value objects within my aggregate root (likely student, or a student enrollment request) that can be used for validation either by the aggregate root or entities contained within the root.

In either of the first two cases above it seems like the use of a instructor repository would be overkill because I don't need to access an aggregate root that represents an instructor but instead in my case I would see the instructor as a value object that describes the student enrollment request and having a repository spit back value objects seems to be blurring the lines of what a repository is supposed to be doing. For the last two options it seems wrong two allow access to data from a service or a factory in the case of option 4 since aren't repositories supposed to be in charge of data access such as this? And if repositories are the right place for this logic where are the appropriate places to access or store references to repositories? I've been convinced that there are reasons not to access repositories directly within any entity or value object that makes up the model so I'm wondering if this is a case where I may have to bend on that assumption. I should also mention that I'm pretty new to DDD and I'm just now encountering some of my head scratching moments and attempting not to box myself in so any knowledgeable input on this topic would be valuable.

Ravenna answered 28/3, 2011 at 3:58 Comment(0)
L
4

Move validation outside of the entity itself.

One instructor shouldn't know about all other instructors. Validation against set of instructors isn't responsibility of one particular instructor.

Allow for access to a instructor repository from necessary entities that are attempting to perform this validation.

Domain model should be persistence ignorant. Need to break that indicates flaws in Your model.

Create a service that allows access to a list of instructors by instrument category.

This bit of information reveals lack of aggregate root - I would call it InstrumentClass.

Introducing that into Your model would solve some of Your issues. InstrumentClass would hold available instructors that teaches particular instrument.

Next thing You need to figure out is how to describe properly student that is assigned to class. Unfortunately I can't name it at the moment (maybe Participation?). But that entity would be used for InstrumentClass to figure out which instructors are too busy.


Here's my "free-style" (just to show what I see) on modeling Your domain:

using System;

public class Main{
  public Main(){
    var instructor = new Instructor();
    var instrument = new Instrument("saxaphone");
    var saxaphoneClass = new InstrumentClass(saxaphone,teacher);
    var me=new Person("Arnis");
    //here, from UI, I can see available classes, choose one
    //and choose according instructor who's assigned to it
    var request=me.RequestEnrollment(saxaphoneClass, instructor);
    saxaphoneClass.EnrollStudent(request);
  }
}
public class Person{
  public IList<EnrollmentRequest> EnrollmentRequests { get; private set; }
  public EnrollmentRequest RequestEnrollment
   (InstrumentClass instrumentClass,Instructor instructor){
    if (!instrumentClass.IsTeachedByInstructor(instructor))
      throw new Exception("Instructor does not teach this music instrument");
    var request=new EnrollmentRequest(this,instrumentClass,instructor);
    EnrollmentRequests.Add(request);
    return request;
  }
}
public class EnrollmentRequest{
  public Person Person{ get; private set; }
  public InstrumentClass InstrumentClass { get; private set; }
  public Instructor Instructor{ get; private set; }
}
public class InstrumentClass{
  public void EnrollStudent(EnrollmentRequest request){
    var instructor=request.Instructor;
    var student=new Student(request.Person);
    var studies=new Studies(this,student,instructor);
    //TODO: this directiveness isn't good
    //student/instructor should listen for class events themselves
    //and class should listen if by any reason instructor or student cannot
    //participate in studies
    student.EnrollInClass(studies);
    instructor.AssignStudent(studies);
    Studies.Add(studies);
  }
  public bool IsTeachedByInstructor(Instructor instructor){
    return Instructors.Contains(instructor);
  }
  public InstrumentClass
   (Instrument instrument, params Instructor[] instructors){
    Instrument=instrument; Instructors=instructors.ToList();
  }
  public IList<Instructor> Instructors{get;private set;}
  public IList<Studies> Studies { get; private set; }
  public Instrument Instrument { get; private set; }
}
public class Studies{
  public Student Student { get; private set; }
  public Instructor Instructor { get; private set; }
  public InstrumentClass InstrumentClass { get; private set; }
}
public class Student{
}
public class Instructor{
}
public class Instrument{
}
Landreth answered 28/3, 2011 at 8:8 Comment(5)
Thanks for validating some of my concerns. I'm going to think about what you have written and see if any of our suggestions lead me to a reasonable solution.Ravenna
"Validation against set of instructors isn't responsibility of one particular instructor." I don't think I suggested that it was, the aggregate root I had in mind was either Student, or StudentEnrollmentRequest for this portion of the model. It just isn't clear to me how the list of instructors fit's into that model.Ravenna
@Ravenna Student and EnrollmentRequest sounds nice. But I still think InstrumentClass is what You are looking for.Landreth
The problem with this design is that it assumes that during student enrollment activity that there is any knowledge of the instructor. The case as I presented above was that the student enrolls giving details of an instrument and only then is an instructor automatically assigned to that enrollment based on whether the instrument was in the instrument class. Only after that fact is the student allowed to change the preselected instructor to one of her choice and at that point the newly selected instructor could be validated once again based off of the selected instrument.Ravenna
for the sake of the hypothetical case didn't clearly state it but I was intending this example not to complicate things with the matter of classes and such assuming that these might be private instructions. I really want to focus on the core features of defaulting the instructor based on an actual instrument selection and also validating an instructor if changed in an enrollment request. Thanks for your valuable input though.Ravenna
I
2

My answer does not cover the detailed implementation / code as it seems that you are using this as an exercise to learn more about DDD.

Remember that in most cases you'll not be able to get the model right the first time and need to evolve the model. As you “play with it” certain rigid parts will become more flexible to change. (Like a gardening glove as per Eric's analogy). As you gain new insights into the domain, you'll find that you need to introduce new concepts into your model. Using “simple examples” has dangers for example they can lack depth. But simple examples are sometimes needed to get the hang of DDD and fortunately we can evolve the example too ;)

One thing I've heard Eric Evans mention was that if the domain does not feel right or you have trouble expressing something in a model you might be missing a concept. Naturally if you have the concepts in your domain you can “get a feeling” or find a natural place where validation will occur.

Having set the context I thus have a proposition as follow:

Enterprise Patterns and MDA has some complex patterns but the idea you can take away is the CapacityManager of the Inventory archetype as guidance. Unfortunately the model on pg 278 is not available online but have a look at the ServiceInventory archetype.

The instructors are selling their services to students. (Instructors get a salary last time I checked :). If I were to map your example to the Inventory archetype for ideas I would use:

  • Inventory - List of instruments/courses
  • ServiceType - Instrument / Course details, start end etc
  • ServiceInventoryEntry - Instrument + places available (uses capacity manager)
  • ServiceInstance - Enrollment - place in class (Scheduled, Booked, Canceled, Completed)
  • CapacityManager (used by ServiceInventoryEntry)
  • ReservationRequest - Enrollment request

I think the key concept added is the CapacityManager: You can call the ServiceInventoryEntry::getCourses() method which will use the CapacaityManager (service or class) to show you / calculate the available teachers or return the default one. Thus, call it multiple times depending on the context: Default or Propose a list of available places/seats/instructors.

With this model you should be able to find natural place (where and when) to validate. The guidance from Streamlined Object Modeling is to put validation where the data is. Not to be taken as a hard rule but there is a natural tendency for objects to have the proper concerns and data grouped together. For example the capacity manager knows about enrollments and instruments. (From MDA - CapacityManger: Manages utilization of capacity by releasing ServiceInstances)

To get your aggregates see what transactions / changes you'll do so that you can ensure they enforce the invariants (rules). In your example I would make ServiceType(Course) a value object, ServiceInventoryEntry and ReservationRequests aggregate roots. (Depends on how complex you want to take your rules). You can also add Students and Teachers as parties as per the MDA book. I tend to use repositories to get hold of my aggregates and then also rely on inversion of control as per Jimmy's book you referenced.

The reason I like the MDA patterns is that it makes me think of use cases and model concepts that I or the business would not have imagined. But, be careful to only model what you need since the MDA patterns can be big and even enticing. The good thing is that they are designed to be modular or "down scalable".

So in short: - Your aggregate roots should ensure your domain is in a valid state (Rules / Invariants) - Put the validation where the data is. Your model will guide your.

Interracial answered 3/4, 2011 at 1:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.