Payam Mohammadi
Payam Mohammadi

Reputation: 136

DDD: Can aggregates get other aggregates as parameters?

Assume that I have two aggregates: Vehicles and Drivers, And I have a rule that a vehicle cannot be assigned to a driver if the driver is on vacation.

So, my implementation is:

class Vehicle {
    public void assignDriver(driver Driver) {
        if (driver.isInVacation()){
            throw new Exception();
        }

        // ....
    }
}

Is it ok to pass an aggregate to another one as a parameter? Am I doing anything wrong here?

Upvotes: 3

Views: 1790

Answers (3)

Amokrane Belloui
Amokrane Belloui

Reputation: 1

Suppose you're in a micro-services architecture, you have a 'Driver Management' service, and an 'Assignation Service' and you're not sharing code between both apart from technical libraries. You'll naturally have 2 classes for 'Driver', An aggregate in 'Driver Management' which will hold the operations to manage the state of a driver. And a value object in the 'Assignation Service' which will only contain the relevant information for assignation. This separation is harder to see/achieve when you're in a monolithic codebase

I also agree with @plalx, there's more to it for the enforcement of the rule, not only a check on creation, for which you could implement on of the solutions he suggested. I encourage you to think in events, what happens when:

  • a driver has scheduled vacation
  • when he's back from vacation
  • if he changes he vacation dates

Did you explore creating an Aggregate for Assignation?

Upvotes: 0

plalx
plalx

Reputation: 43718

I'd say your design is perfectly valid and reflects the Ubiquitous Language very well. There's several examples in the Implementing Domain-Driven Design book where an AR is passed as an argument to another AR.

e.g.

  1. Forum#moderatePost: Post is not only provided to Forum, but modified by it.

  2. Group#addUser: User provided, but translated to GroupMember.

If you really want to decouple you could also do something like vehicule.assignDriver(driver.id(), driver.isInVacation()) or introduce some kind of intermediary VO that holds only the necessary state from Driver to make an assignation decision.

However, note that any decision made using external data is considered stale. For instance, what happens if the driver goes in vacation right after it's been assigned to a vehicule?

In such cases you may want to use exception reports (e.g. list all vehicules with an unavailable driver), flag vehicules for a driver re-assignation, etc. Eventual consistency could be done either through batch processing or messaging (event processing).

You could also seek to make the rule strongly-consistent by inverting the relationship, where Driver keeps a set of vehiculeId it drives. Then you could use a DB unique constraint to ensure the same vehicule doesn't have more than 1 driver assigned. You could also violate the rule of modifying only 1 AR per transaction and model the 2-way relationship to protect both invariants in the model.

However, I'd advise you to think of the real world scenario here. I doubt you can prevent a driver from going away. The system must reflect the real world which is probably the book of record for that scenario, meaning the best you can do with strong consistency is probably unassign a driver from all it's vehicules while he's away. In that case, is it really important that vehicules gets unassigned immediately in the same TX or a delay could be acceptable?

Upvotes: 4

desertech
desertech

Reputation: 1029

In general, an aggregate should keep its own boundaries (to avoid data-load issues and transaction-scoping issues, check this page for example), and therefore only reference another aggregate by identity, e.g. assignDriver(id guid).

That means you would have to query the driver prior to invoking assignDriver, in order to perform validation check:

class MyAppService {
  public void execute() {
    // Get driver...
    if (driver.isInVacation()){
      throw new Exception();
    }
    // Get vehicle...
    vehicle.assignDriver(driver.id);
  }
}

Upvotes: 1

Related Questions