category: Good pratices
In the action trigger
The network call is done before the action itself, in the code that call it. (For instance in the input and IA code). The action itself is not coupled with the networking layer. However you have to remember sending message whenever you are calling the action. Furthermore, if you introduce lag compensation mechanisms to the mix, you have to find ways to integrate it into the action itself to handle reconciliation.
For instance: In order to provide a better player experience, you start the action on the client before the server is able to tell you if the action was possible. If the server answer "no" to this question, you need to implement logic to cancel changes made by the action.
In the action itself
The network call is done inside the action code. The main advantage is to allow you to reuse the action whenever necessary without having to worry about integration with the network layer. However, this introduce a thight coupling between the action and the network layer. Are you sure that you want to send a packet whenever the action is called? Do you have enough context inside the action to send this packet? Dealing with these issues can complicate the code necessary to call the action in the game.
When data change
The last option is to detect data change (for instance the players position updates) and send the update as they occur, for instance by checking the value at regular time intervals, or by listening to events using the event or observable patterns.
This approach has the main advantage to separate clearly the action and networking, letting developers update actions without having to bother about how changes are propagated to the other peers. Furthermore, it allow you to easily apply bufferization or throtlling out of the game design layer if necessary.
However it has 2 main drawbacks: First, it's not always easily applicable ("player use a skill" for instance). Secondly it doesn't provide good ways to deal with the lifecycle of monitored objects. You have to add ways to detect when you need to start, and stop monitoring data changed, and make sure these functionalities are well understood when updating the game code. Without that, difficult to diagnose bugs could arise.
On Unity3D at least, our replicator plugin works this way by monitoring changes to properties on GameObject, and replicating them between game clients.