When triggering commands that generate the same data each time, cache the packet response to not waste CPU and I/O resources. For some packets the database gets hit every time * N users, and this leads to excessive database load. All emulators currently available experience this design flaw.
Meth0d and/or matty13 built a novel (for the retro community) way of caching packets by key-value. An example of it's usage can be found here.
Data was cached on the protocol level, similar to how reverse http proxies like Varnish (L1 tier) and Apache Traffic Server (L2, L3 tier) used by Wikimedia and New York Times cache content to achieve high throughput and low latency responses.
Caching on the protocol level bypasses most if not all game logic on second (or first, depending on implementation, stale-while-revalidate is a well suited pattern that could be adopted for e.g. navigator; initializing cache on constructor/loading data, scheduling a refresh decoupled from response when client asks for it, limited per X time) packet received, this lowers database load and considerably (just ask popular retro's on their specced VPSes) during peak traffic and/or when dealing with scriptkiddies.
In this age of 1TB RAM servers and 2TB PMEM devices with ridiculous throughput and nanosecond latencies; what is being taught in computer science classes (memory being expensive, CPU re-calculation being cheap) no longer applies. It now makes more sense to calculate once and store in memory and to recalculate only when absolutely necessary. Such application architecture allows the CPU to spend most of it's time towards expensive game logic like pathfinding, instead of shuffling data around.
Implementing this would take a concurrent (being N thread R/W access safe) key-value store implementation like ristretto or a remote one like Redis.
Response packets could then be accessed by each game service, allowing each to implement custom behavior. (e.g. item inventory being cached per user by attaching user ID to the key).