Don't over-engineer something which can be easily modeled in a simplistic fashion and changed later if the need arises.
The premise behind a component based architecture isn't to abolish coupling completely, but to hide that coupling effectively so that designers can easily combine a set of features exposed through an API to create unique and creative game objects with ease. So what becomes important to us developers is how and where we implement the coupling.
The simplest of approaches would be to leverage the observer pattern; allowing systems to register as a listener to other systems. Depending on your approach, this may lead to the massive switch statement event handler or too an excessive multiple inheritance problem. I'd argue if you end up with either, chances are the system is likely doing too much and might suffice for being split into smaller chunks of behavior.
Another approach would be to have a system expose a set of event objects that another system could subscribe/unsubscribe as needed. These event objects are analogous to signal/slots which can be found in boost and several other libraries. The benefit is that using signal/slots, the connection between the subscriber and publisher can be cleaned up when either go out of scope. Function callbacks are wonderful constructs, but do be weary of their side affects, particularly on your cache if over used inside your main game loop.
One last approach would involve reversing the flow of data. In the above two scenarios, you're pushing data from a higher level system to the lower systems, but sometimes it can be far more efficient and flexible to poll for information depending on circumstances.
In this case, you have an event buffer that holds events your game loop has fired in the current frame sorted by time. When a system processes it's update tick, it checks whether any events of interest exist in the event buffer and processes them. This would allow you to skip events if multiple ones exist and it would also allow events to be popped from the event buffer and replaced with modified versions that perhaps are of interest to a system that fires later in the update cycle too (e.g. system that modifies damage event based on armor, shields, absorbs, etc.). In this case, you can easily store the events placed in the buffer for later playback if that was something of interest.
Edited by crancran, 09 September 2013 - 10:05 PM.