ive been looking into this recently. here's what i've come up with so far. this is for real time non-mission based games. turn based and mission based (run a mission, then between-mission menus, etc) are similar.
modules and their public methods from highest level to lowest level: note that constructors and destructors might be used for init and end methods.
1. program(init, run, end) . program.run is the main menu (continue, new, load, settings, help, quit).
2. game (init run end). game.run is the main game loop.
3. render_all, process_input, update_all, and play_sfx_all. render_all simply sets up the camera and such, then calls the render routine for all visible objects.
process_input handles input. if a game object is selected, its input handler (interactions menu or whatever) is called. update_all makes the calls to update the game objects, game clock, and game world. play_sfx_all plays/stops the ambient sfx for any nearby game objects. a list of game objects might have a play_or_stop_sfx method. it would play or stop the sfx for each object in the list.
4. game specific code that uses more than one type of game object, such as distance from player object to some NPC object.
5. lists of game objects: PCs, targets/NPCs/monsters, projectiles, dropped objects, etc.
most games i've done have a player, targets, and projectiles at the very least.
Caveman 3.0 has players, NPCs/monsters, projectiles, and world objects.
SIMSpace v8.0 has galaxies, star systems, starports, targets, projectiles, and particles (streaming stars).
Airships! v1.0 just has a player and targets. projectiles are just another kind of target. and the player is a target too. player info is copied to their target as needed. this way generic target methods can be used for the player as well as targets. the game has many more variables for the player's ship than for other types of targets, otherwise a player object would not be needed. just a variable for which target was the player's ship. and a single target list could be used. this would work for a simple game like doom, where there's not much additional data tracked for the player vs a target - IE just handful of ints for inventory levels and current weapon. pretty much everything else: hp, dp, location, orientation, etc is data that both player and targets have, in that type of game.
Typical methods for lists of game objects: add, remove, render, update, play_sfx, handle_input, etc. not all types of objects require all methods. these may be list or list entry methods, whichever makes more sense. list entries need not be separate objects. the list itself can be the fundamental data type in some/many/all cases. most games have a few lists for different types of objects.
6. camera object. can_see_location() method. does clip range and frustum cull, plus any other game specific culling used.
7. low level game specific routines such as world coordinate systems for large game worlds.
8. low level generic game library or engine code such as render queue, timers, asset pools, animation systems, etc.
9. directx and win APIs (or OpenGL and Linux, etc).
entities should be composed of generic drawing and location info that you can pass to and from the physics and AI engines, and can pass to the render engine.
write generic code that uses locations and drawing info. to process game objects, extract their locations and drawing info, and pass that on to the generic routines.
when i say "composed of", i mean composition vs inheritance, not components as in ECS.
as for what to put in separate files:
any module that you want to be able to link by itself to a program should be a separate file.
modules that use other modules should auto-include. so your generic GUI module which uses your custom font module should auto-include your custom font module.
APIs that are typically used together can go in a single file (compiles and links faster). but for organization sake, you may want separate modules, along with a wrapper module (basically just a .h file that includes the other modules). this keeps the files S.R.P friendly.
in the end, its a mater of personal preference. Caveman 1.0 had about 20 code modules. all nicely divvied up as to what they did and such.
Caveman 3.0 is just 3: gamelib, audio, and game. all the generic modules except audio are in the gamelib. all the game specific code (about 125,000 lines of it) is in the game module. all the code is still divvied up into separate modules with good SRP. they just appear one after the other in the same file, that's all.