Search code examples
domain-driven-designddd-repositories

Validate based on data in another aggregate root


The short version:

Canteen
    openDays
    
Customer
    id
    bookingDays
        change(newBookingDays){
            if (newBookingDays not in >>>canteen.openDays<<<)
                throw ValidationException
            bookingDays.transition(newBookingDays)
        }

I am storing booking dates for a canteen. The Canteen is an aggregate root in my model and it has a BusinessInterval for each month, which stores on which days it is open. There is another aggregate root Customer, which contains its own BusinessInterval for each month, which stores on which days the customer wants to eat in the Canteen. From my point of view these two aggregate roots make sense, but there is a huge problem here. If I want to modify the BusinessInterval of a Customer e.g. cancel booking or add booking for a day, then I need to know the BusinessInterval of the Canteen for that month, because the Customer cannot book for a day when the Canteen is not open. So I need this info for validation by changing the BusinessInterval of the Customer. Which means I need to transfer information from an aggregate root into another. I cannot move this info into a domain service, because it certainly belongs to the Canteen aggregate. I cannot move the Canteen into the same repo as the Customer, because it does not make sense from design perspective. I don't think this validation code belongs to an application service either, because when I put it into the BusinessInterval of the Customer it is maybe a few lines and it looks very nice there along with the rest of the validation code. They appear to be the same abstraction level, they are doing similar things. On the other hand it seems to be wrong to call a Canteen method every time I instantiate a Customer in the CustomerRepository and to inject the Canteen instance or the CanteenRepository into the CustomerRepository. How should I solve this? I mean I can make it work, but no matter what I try it will violate one design rule or another. :S

Note: From a certain perspective it makes sense to have a Canteen aggregate root and move the Customer and the bookingDays below it, because when the openDays change, then bookingDays can change too. But I solve this by making the openDays immutable after publishing them and the bookingDays can be created only after this.


Solution

  • What I did is this:

    Canteen
        openDays
        bookings[]
            Booking
                id
                Customer.id
                bookingDays: BI
                    change(newBookingDays){
                        if (newBookingDays not in canteen.openDays)
                            throw ValidationException
                        bookingDays.transition(newBookingDays)
                    }
        
    Customer
        id
    

    In reality the Customer is a lot more complex, not just an id, so I don't think the Canteen should handle it, but the booking part can be definitely handled by the Canteen. I like how DDD rule violations let me know that something is broken in the model.