This leads to a few questions:
Obviously, the network system needs access to a lot of information, how do people usually decouple this?
Networking is not easy to decouple. It's often not fully practical. The low-level implementation should be a separate module, of course, but actually networking a game requires conscious and deliberate decisions in the implementation of game logic, the game design, and even art. There is no way to magically make a game networked in a completely decoupled and transparent fashion.
Should events be send somehow to the network system, or should the system poll for data and events itself?
We implemented a polling system for a rough draft system recently (had to get a non-networked big game engine networked in a few weeks for proof-of-concept reasons) and the performance is _terrible_. Imagine that you have 1,000 network-aware game objects in your world. You have to poll every object every network tick, and polling the object typically means iterating over the networked properties and comparing their values with old values to see if a change happened, which likewise is very hard to do in a type-agnostic decoupled way.
An explicit system works much better. If a networked property on a component changes, the setter code in the component (or owning system if using an ECS) should signal to the network layer precisely which property on which component on which game object changed, and what the change was (e.g. pass in old and new values so delta compression is possible).
Different component properties may need to be handled very differently, too. Some properties are basically "changes almost every tick" like position/rotation and are streaming non-precious data (if you lose a change, it doesn't matter, because next tick you'll get another update anyway) while other properties are precious (the remote end really needs to know the exact current value, but if the value flips from A to B and back to A again it doesn't matter if the value B was lost) while yet other properties are _transition sensitive_ (the remote end needs to know that the property flipped from A to B and back and cannot lose that transition). This again is why you can't fully decouple networking; a separate networking engine can't possibly know which properties on which components have which requirements without the component informing the network engine.
Messages have to be set up in such a way that you're not sending every single game message over the wire but only the ones that need to be replicated to remote ends. Again, a generic decoupled networking engine can't know this by itself.
There's then the security issues. You can't just read any update over the wire and apply it since then a client could just send health updates every tick constantly giving itself 1000 HP and becoming effectively invincible. The networking definition needs to be very clear on which properties are server-authoritative and which are client-authoritative (there should be few to none of the latter in most games). Likewise for messages, you need to be clear about which end is allowed to send or receive which messages, so clients can't send state-change messages to the server for things the server should be authoritative over.
Ultimately you need to build a network engine that decouples the details of how state changes are sent/received, how messages are sent/received, how decoding works, the network tick, low-level TCP/UDP/IP details, etc. but which exposes high-level operations and meta-data configuration logic to the component/system and game logic code. That code then has to use those high-level operations, working with design and art to ensure that the game plays smoothly, fairly, and hides the inherent network latency and lag spikes. Decouple the implementation but be prepared to spend considerable time integrating networking support at an intimate level with much of the rest of the game's code.