Flip the problem.
The ProductType aggregate knows the details of Product aggregate since the product's structure is governed by it. Instead of trying to create a product with a ProductType parameter, invoke a method on ProductType to create a new product instance. In other words, the factory method to create a new product will be part of ProductType.
In the use case you describe, you are trying to use information stored in Customer to decide how to modify Order.
One good answer, as mentioned by Rik D, is to simply pass the Customer information to the Order as a command argument. That could mean passing the Customer domain entity, but that opens up the possibility that the Order will try to change the ...
Your intuition is good and you can have a @TransactionalEventListener if you using spring for instance.
I have did it by the past and it works well, do not forget to add rollback transaction listener as well or wrap it in a transaction. Of course your EventDispatcher must commit events on transaction.
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 ...