You want "pretty" OOP? Tell Don't Ask.
You want performance and reliable multithreading? Command-Query Separation. Which is basically the same as functional programming :) Make all the functions pure except for a few clearly marked ones with side effects.
It's especially visible in gamedev. After a decade of trying to make OOP work with multithreading people moved on to Entity Component Systems (which is basically imperative programming with some relational and functional aspects).
It's also why functional programming is getting more popular - because it is pull by default. It's also why almost all graphical shaders are functional even if they look like C :) Many inputs one output and no side effects :)
That's not functional programming, it's just pure functions. Shaders make use of sequential execution of statements, and they don't support recursion.
It is the pure function nature of functional programming that makes it easy for concurrent and parallel tasks. Recursion is neither here nor there
Code is functional if it separates side-effects to small, clearly marked parts of the codebase.
I have a background in Databases and it's always interesting how ECS face some of the same problems the DB field faced, they just haven't realised it yet. They require super fast joins to figure out which components to feed to the systems for example.
I'd predict massive cool strides in the DB and ECS/Gamedev/Simulation world, once the two realise how much they can learn from one another.
Uuid are Entities, for easy distribution. Components are just entity attribute subsets in the DB. Systems are external components subscribing to queries.
This wouldn't have all the nice memory layout properties of ecs, but the thing is that "normal" doesn't need fast. "normal" needs correct, and provenance, and accountability.
If you wanted subparts in such an architecture that are high speed you'd make them regular ecs components, and have them log into the append only store at intervals/checkpoints/whatever.