Inversion of Control can prevent changes to the Game Layer or Network Layer when something changes in one of the other layers. First let's consider a
Ditch your existing messaging layer for now, and instead lock down your generalised
Message format / class inasmuch as possible.
Messages are not only going to contain data members; they will also carry functionality. In this way, we prevent any changes to the message format from affecting Game or Network layer classes, because it is the messages that take action, based on their own data. This is Inversion of Control. What messages are, and what they do, is bound into one class (per message type).
Polymorphism can help us further. I know it's trite, but building to interfaces is your friend. You might have different subtypes of
Message. Any external class, then, may receive a generalised
Message, and not know what it's most derived type actually is; so the external class will not know how to process it... nor need it do so: For if the
Message itself is equipped to act based on it's type (as per Strategy Pattern / functors), it could modify your data model directly once it reaches the Game Layer, as follows:
class SomeGameLogicClass implements IMessageQueueKeeper
for each (message in messages)
//Irrespective of whether a KillMessage or a SpawnMessage,
//the Message has now written to the game's data model --
//which your GameLogic classes may interface with directly.
//(Assume all Messages are given access to the data model.)
In fact, your
Messages could simply tie into your pre-existing messaging layer classes for their functionality; client classes (Network, Game) would be none the wiser.
The generalised type
Message enables all of this.
Furthermore, how do we populate the message queue? Well, what we don't want is for the Network classes to have type-specific references to the Game classes. What we can instead do is allow them to maintain generalised references to one another, via the example interface
IMessageQueueKeeper. This means you could take the Network layer and plug it into a completely different game, provided that interface were implemented by that other game. Now you can let your Network Layer reference the above class, so that it may feed it
Messages. You can do similar, in reverse, allowing your Game layer to notify some vague and generalised Network class of new messages to be boiled down and sent onto the wire.
Both Game and Network layer are producers and consumers of
Messages (of various derived types). Let the interfaces that you build for them, reflect that.
And voilà! No more tight coupling. :)