2

I'm stumbling trying to find a proper way to model this scenario: I have 3 different aggregates within same Bounded Context:

  1. A Student
  2. A University
  3. A University of Interest

    public class Student : Entity, IAggregateRoot
    {
        public string Name { get; }
        public string StateAbbreviaiton { get; }
        ...
    }
    
    public class University : Entity, IAggregateRoot
    {
        public string Name { get; }
        public string StateAbbreviaiton { get; }
        ...
    }
    
    public class UniveristyOfInterest : Entity, IAggregateRoot
    {
        public Guid StudentId { get; }
        public Guid UniversityId { get; }
        public ResidencyType ResidencyType { get; }
        ...
    }
    

A UniveristyOfInterest is an entity that happens when a student selects a University they are interested in attending. UniveristyOfInterest is an Entity because it will ultimately contain a lot more information about the experience the user could have with the University including financial data, ROI calculations, etc. Each UniveristyOfInterest for a Student will be saved in some repository.

UniveristyOfInterest has an Enumeration called ResidencyType. ResidencyType has 3 possible values: InState, OutState, and Unknown. The business rule is if StateAbbreviation value of Student is same as StateAbbreviation value of University, then ResidencyType is InState, otherwise OutState (assuming we have valid values for both Student and University).

The UniversityOfInterest aggregate must contain the business rules for determining ResidencyType. All of the research I've done recommends Aggregates only know of other aggregates based on the aggregates Id value (no references to foreign aggregates). My UniversityOfInterest constructor is passed StudentId and UniversityId. How do I reach back and get their respective StateAbbreviation values so I can properly administer the business rule for determining ResidencyType inside the UniversityOfInterest aggregate?

I thought about also passing stateAbbreviation for both Student and University in constructor of UniversityOfInterest, but that seems klunky.

Any suggestions on how to properly administer business rule determining ResidencyType requiring data from foreign aggregates within the same Bounded Context?

  • 1
    Change your perspective. You just described three relations in 3rd normal form. How do you apply a rule that needs data from all three relations? Join them. In your case as they are objects in your own memory system. Load them. I would imagine that UniveristyOfInterest would also cache a reference to a University and Student if they were already loaded. Alternately there is a Repository object somewhere that can be asked to retrieve them given that you have the Guids. If your doing this across a network the Service could send the correlated objects to reduce round tripping. – Kain0_0 Aug 6 at 7:01
  • So, you are saying that you have several objects/entities/concepts that are all involved in maintaining an invariant? Sounds like you need some sort of a consistency boundary around them - some way to control how this collaboration of objects is constructed and changed. And that sounds an awful lot like how aggregates are defined. Maybe the best course of action is to rethink your design in terms of what the system itself needs to do, and how it will be used, and create your aggregates around that. – Filip Milovanović Aug 6 at 10:34
  • BTW, the decision whether a model element is an entity or not is not based on on the perception that "it will ultimately contain a lot more information about [...stuff...]" - that's not the determining factor; a thing is an entity if its primary distinguishing characteristic is its identity (rather then an actual value/state). I'm not saying UniveristyOfInterest isn't an entity, just that you should examine your reasons for that choice within the context of the system you are building. – Filip Milovanović Aug 6 at 10:34
1

The UniversityOfInterest aggregate must contain the business rules for determining ResidencyType.

Given that determining ResidencyType requires information from two different aggregates (Student and University), this responsibility doesn't really fit into UniversityOfInterest and would be better modeled in a domain service.

UniversityOfInterest should be limited to modeling the relationship between a Student and a University. Depending on your requirements, you might choose to also pass in the computed value of ResidencyType, but it's not critical to the definition of the aggregate.

  • So, is it appropriate to have business rules reside outside of the aggregate? So, determining ResidencyType could happen in a domain service before the UniversityOfInterest is first created? Subsequently, UniversityOfInterest would be retrieved from a datasource after being saved, which would already have ResidencyType value. – Tom Schreck Aug 6 at 12:34
  • Yes, that is perfectly fine. Most rules beyond basic validation of invariants likely won't fit inside an aggregate, because the aggregate will not have access to all the information necessary to enforce those rules. – casablanca Aug 7 at 3:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.