5
votes

If I have two classes [Shift,ShiftDetails] where the [Shift] is the aggregate root. based on a specific field I want to constraint the number of instances of ShiftDetails.


I create a ShiftDetailsView class then create two methods in Shift aggregate root to control the invariant:

  • public IEnumerable ConstructShift();//based on the NumberOfSuccessions field

This method Should create number of intialized ShiftDetailsView based on that field and then add them to a list and return the result back to user(developer) as IEnumerable.

Then the user should call CompleteShift after filling the intial IEnumerable returned from the previous method:

  • public List CompleteShift(IEnumerable shiftDetailsViews)

I did two separate steps to control the number of childs through the aggregate root and I think there are better ways to do that to guarantee the (ACID) of the whole thing.


Based on the comments, The question needed more clarification, and because I try to simplify the problem, I made a mistake and change the real problem. because when we treat with domain problems we have to be accurate and clarify the exact problem. So I will try to explain it in more detail.

I have the following Two aggregates:

1. First Aggregate

1-WorkingSystem: (aggregate root)

    private readonly ICollection<WorkingTime> _assignedWorkingTimes;

    public string Name { get; private set; }
    public short NumberOfSuccessions { get; private set; }
    public Week WeekStart { get; private set; }
    public bool IsActive { get; private set; }
    public bool IsDefault { get; private set; }
    public short NumberOfWeekends { get; private set; }
    public virtual ICollection<WorkingTime> AssignedWorkingTimes { get => _assignedWorkingTimes; }

Example:

 Id |Name     | NumberOfSuccessions|WeekStart|IsActive|NumberOfWeekends 
  1 |Employees|       2            |Sunday   |  1     |   2
  2 |Lecturers|       1            |Saturday |  1     |   1

2-WorkingTime:

public string Name { get; set; }
public short NumberOfHours { get; set; }
public int WorkingSystemId { get; private set; }

Example:

Id|Name   |  NumberOfWorkingHours  | WorkingSystemId 
 1|Summer |  8                     | 1
 2|Winter |  6                     | 1
 3|General|  8                     | 2

2. Second Aggregate

3-Shift (aggregate root).

private readonly List<ShiftDetail> _assignedShiftDetails;
public string Name { get; set; }
public ShiftType ShiftType { get; set; }
public int WorkingSystemId { get; set; }
public virtual WorkingSystem WorkingSystem { get; set; }
public virtual IEnumerable<ShiftDetail> AssignedShiftDetails { get => _assignedShiftDetails; }

Example:

Id|Name            |ShiftType | WorkingSystemId 
1 |restaurant-shift|Morning   |  1

4- ShiftDetails:

public Guid ShiftId { get; private set; }
public int WorkingTimeId { get; set; }
public DateTimeRange ShiftTimeRange { get; set; }
public virtual WorkingTime WorkingTime { get; set; }

Example:

 ShiftId = 1|WorkingTimeId = 1|ShiftStart = 8:00|ShiftEnd = 16:00
 ShiftId = 1|WorkingTimeId = 2|ShiftStart = 9:00|ShiftEnd = 15:00

Now I want to constraint the number of details based on the NumberOfSuccessions for each WorkingSystem! and because I have an access to WorkingSystemId in my aggregate root Shift then I can access this info.

Could you help me to control the number of instances based on a field in the aggregate root ?

2
Well, you could simply add an AsignShiftDetail(detail) method which throws an exception when it's called more than NumberOfDetails. But choosing the best solution depends on your domain. What are you modeling? What problem are these classes trying to solve? - Ali Doustkani
is ShiftDetails and ShiftDetailsView the same?Because I feel it may be misleading if correct please fix them - amirhamini
@amirhamini no it's a smaller version of the domain class - Anyname Donotcare
Why do you need ConstructShift()? Do you have to setup some kind of template to be filled based on the current shift instance or it's just a matter of letting the client know how many shifts must be provided? - plalx
@plalx:This's a very good question. based on what i have read, the aggregate root (shift) in my example should control the Invariant (the number of shift details) in my example for the system to be in a consistent state. So I create this method for the client to call it and it will take care of returning IEnumerable of shiftDetailsViews it's initialized by the correct number of empty shiftDetailsViews then the client can fill this initial template IEnumerable through looping on it and setting their values then call CompleteShift(IEnumerable shiftDetailsViews) at the end. - Anyname Donotcare

2 Answers

4
votes

Could you help me to control the number of instances based on a field in the aggregate root ?

The using of these two methods are in the creation of new shift only. and because I'm new to DDD I think I didn't constrain the invariants in the proper way

Oh, I see -- you are worried specifically about what happens in the transaction that creates the aggregate.

The usual pattern, described by Eric Evans (chapter 6), is that the domain model will expose a factory that knows how to take data from the application and return a new aggregate instance.

Within that factory, you are creating all of the values that make up the initial state of the aggregate. The values understand their own constraints ("I am an Amount, so I need an integer greater than or equal to zero"; "I am a Money, so I need a non null Amount and a non null CurrencyCode).

The factory accepts data from the application, constructs the graph of values, and in the end calls the constructor of the aggregate with the current state.

It's a perfectly normal thing to have values in the model that have the responsibility for validating data that has been passed in, and that can include having a value that verifies two other values are in agreement.

So it might be, for example, that you factory will take a NumberOfDetails and a List<ShiftDetails>, and from them produce a VerifiedDetails type, from which the aggregate will eventually be constructed.

A very good read on this approach is Scott Wlaschin's book Domain Modeling Made Functional.

-1
votes

I would add a method named AssignedShiftDetail(ShiftDetail shiftDetail) to the Shift class and will implement any related logic inside this method.