Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can non aggregate-root hold a reference for another non aggregate-root?

If I have two aggregates like this:

First Aggregate :

  • WorktimeRegulation (Root)
  • Worktime
  • RegulationEnrolment

Clarification with data:

WorktimeRegulation :

 public class WorkTimeRegulation : Entity<Guid>, IAggregateRoot
    {
        private WorkTimeRegulation()//COMB
       : base(Provider.Sql.Create()) // required for EF
        {
        }
        private WorkTimeRegulation(Guid id) : base(id)
        {
            _assignedWorkingTimes = new List<WorkingTime>();
            _enrolledParties = new List<RegulationEnrolment>();
        }
        private readonly List<WorkingTime> _assignedWorkingTimes;
        private readonly List<RegulationEnrolment> _enrolledParties;
        public string Name { get; private set; }
        public byte NumberOfAvailableRotations { get; private set; }
        public bool IsActive { get; private set; }
        public virtual IEnumerable<WorkingTime> AssignedWorkingTimes { get => _assignedWorkingTimes; }
       public virtual IEnumerable<RegulationEnrolment> EnrolledParties { get => _enrolledParties; }
        //...
    }

Id|    Name            |   NumberOfAvailableRotations|  IsActive 

 1|    General Rule    |          2                  |    true   

Worktime :

public class WorkTime : Entity<Guid>
    {
        private WorkTime()
      : base(Provider.Sql.Create()) // required for EF
        {
        }
        private WorkTime(Guid id) : base(id)
        {
            ActivatedWorkingTimes = new List<WorkingTimeActivation>();
        }
        private ICollection<WorkingTimeActivation> _activatedWorkingTimes;

        public string Name { get; set; }
        public byte NumberOfHours { get; set; }
        public byte NumberOfShortDays { get; set; }
        public Guid WorkTimeRegulationId { get; private set; }
        public virtual ICollection<WorkingTimeActivation> ActivatedWorkingTimes { get => _activatedWorkingTimes; private set => _activatedWorkingTimes = value; }
        //....
   }

Id|  Name   |   NumberOfHours| NumberOfShortDays |WorkTimeRegulationId 

1 | Winter  |     8          |    1              |    1
2 | Summer  |     6          |    0              |    1

Second Aggregate :

  • Shift (Root)
  • ShiftDetail
  • ShiftEnrolment

Clarification with data:

Shift :

  public class Shift : Entity<Guid>, IAggregateRoot
    {
        private readonly List<ShiftDetail> _assignedShiftDetails;
        private readonly List<ShiftEnrolment> _enrolledParties;


        public string Name { get; set; }
        public ShiftType ShiftType { get; set; }
        public int WorkTimeRegulationId { get; set; }
        public bool IsDefault { get; set; }
        public virtual WorkingTimeRegulation WorkTimeRegulation { get; set; }
        public virtual IEnumerable<ShiftDetail> AssignedShiftDetails { get => _assignedShiftDetails; }
        public virtual IEnumerable<ShiftEnrolment> EnrolledParties { get => _enrolledParties; }
        //...........
   }

Id|  Name      |  ShiftType  |  WorkTimeRegulationId  | IsDefault 
1 | IT shift   |  Morning    |    1                   |  1 

ShiftDetail:

  public class ShiftDetail : Entity<Guid>
    {
        public Guid ShiftId { get; private set; }
        public Guid WorkTimeId { get; private set; }
        public DateTimeRange ShiftTimeRange { get; private set; }
        public TimeSpan GracePeriodStart { get; private set; }
        public TimeSpan GracePeriodEnd { get; private set; }
        public virtual WorkTime WorkTime { get; private set; }

        private ShiftDetail()
        : base(Provider.Sql.Create()) // required for EF
        {
        }
        //..........
   }

ShiftId  WorkTimeId shift-start  shift-end   
  1          1        08:00        16:00
  1          2        08:00        14:00

My questions here:

  • Is it okay for non aggregate-root (ShiftDetail) to hold a reference for another non aggregate-root (WorkTime)?
  • The domain expert clarify that: To create a valid shift then we should have a shift detail for every worktime related to a specific worktimeRegulation. And can't update the number of workhours in worktime if there's reference in shiftDetails. The previous example shows that we have two worktimes(winter,summer), So we have a shiftdetail for winter to stick with 8 workinghours and a shiftdetail for summer to stick with 6working hours. Now I feel that invariant of shift details controlled by non-aggregate root(worktime) How to force this invariant?

  • According to the previous information do I make a mistake related to aggregates specifications?

like image 430
Anyname Donotcare Avatar asked Nov 08 '22 02:11

Anyname Donotcare


1 Answers

Is it okay for non aggregate-root (ShiftDetail) to hold a reference for another non aggregate-root (WorkTime)?

No, unless they exist in the same Aggregate.

You may hold references only to other Aggregate root's ID.

You may hold a reference to the ID of a nested Entity from another Aggregate but you should note that this ID is opaque, you may not assume anything about how it is used internally by it's Aggregate root to find the nested Entity.

Now I feel that invariant of shift details controlled by non-aggregate root(worktime) How to force this invariant?

You can enforce an invariant in two ways:

  1. Inside an Aggregate. This means that the Aggregate must me sufficient large, it must own all the state it needs to. This enforcement is strongly consistent.

  2. Coordinated by a Saga/Process manager. This component react to changes inside possible multiple Aggregates and send commands to other Aggregates. A Saga is the opposite of an Aggregate. This enforcement is eventually consistent.

like image 136
Constantin Galbenu Avatar answered Nov 13 '22 00:11

Constantin Galbenu