If you dont already use it, you may wanna have a look at the new DOTS (ECS, Jobs, Burst) architecture recently introduced to Unity. You basically define Components, which only contain a single piece of information. For example, you would have an HpComponent and a PositionComponent. Entities are then defined by the components they are made of. An Enemy would, for example, contain an HpComponent, as well as a PositionComponent. Systems are then used to execute code, like pathfinding, for all Entities that use a specific component, in this case the PositionComponent. This results in a very efficient memory layout, that can easily be multithreaded using Jobs, which also enables you to make use of the Burst compiler, for huge (!) performance speedup (we are talking in regions of factor 2 - 100 times faster).
Your question was mostly concerned with architecture, not performance, but using DOTS would force you into a very specific way of writing your code (solving your architecture problems), with the added benefit of giving you huge amounts of free performance.
That said, DOTS is not object oriented anymore, thus it will take some time getting used to and (because of that) may not feel very readable. In case your MMOs are for one reason or another not really in need of free performance, or you simply want / need to stay with an object oriented aproach, then we are back to the original question.
Generally speaking “specific solutions being better than others in some circumstances” is always true. However, for most problems commonly solved by programmers, there exist specific programming patters, which can be seen as a state of the art solution for given problems. You may wanna look up programming/design patters to get an idea how specific problems are approached.
That said (if you do not want to use DOTS), i’d go with a strictly object oriented, inheritance based approach for organising your code. You could, for example, define an abstract class for Entities, that contains all the common attributes and functions all Entities must have. Classes like Enemy or MainCharacter will then inherit from the abstract Entity class and add to it, or overwrite some parts of it. Other required functionality can be added through Interfaces. You could, for example define a CharacterCameraSettings interface, which contains, for example, the (unimplemented) GetCameraOffset function. The entire example would work out like this:
public abstract class Entity
{
internal int currentHP;
}
public interface CharacterCameraSetting
{
Vector3 GetCameraOffset();
}
public class MainCharacter : Entity, CharacterCameraSetting
{
public MainCharacter()
{
// we can access all the abstract entity data:
currentHP = 10;
}
// We were forced (!) to implement this method because we implement the interface
public Vector3 GetCameraOffset()
{
throw new System.NotImplementedException();
}
}
You can save lists of Entities if you are only concerned with, for example, HP, but still save your MainCharacter in that list, since it is also an Entity. In the same way, you could have a variable of type CharacterCameraSetting and save an instance of your MainCharacter in it, if all you are concerned with is the contents of the CharacterCameraSetting (in the above example that would be the GetCameraOffset method).
Hope this helps!