There is a balance that needs to be achieved between looseness of coupling and complexity. As the person responsible for the overall architecture, you need to strike that balance.
When we call a
ToString() method on an object, we can do so safely because we know all of our classes derive from a base class that has a
ToString() method, and that method can be overridden in derived classes to achieve better output. Do we add event listeners to
ToString()? No, we don't. Why not? Because it would add additional complexity for no additional benefit.
Do we add event listeners to actions which may or may not have a corresponding target to receive the action? Of course. Why? Because it generalizes our class model, and allows optional message passing where it is appropriate.
If you have two classes that will always work together, but whose methods will never talk to any other class (at least within the realm of their respective Interfaces), you would not decouple them with events. Why? Because decoupling doesn't make any sense in that context. Decoupling can actually make things more difficult, because there are situations where one class must have knowledge of the other class's state in order to function properly (in other words, they must coordinate their efforts). Decoupling makes classes know less about each other, not more.