Performance-wise it’s always hard to tell. It depends on the scale of your project, your coding skills/knowledge/ techniques.
You can actually create whole modules that have nothing to do with Unity and update them with one MonoBehaviour that manages the execution at different points in time.
It has its benefits as you can design the whole system to be an - in itself - closed, self-maintaining system that has well-coordinated creation/setup and disposing/destruction behaviour. Takes years or even decades to be able to create such systems and you can always improve - a never ending learning process.
One of the supreme disciplines (with so many different aspects and approaches) in software engineering. I wish I was much better in this 
Now if you look at that with Unity’s features in mind, like serializable values which are easily tweakable in the inspector, you just loose a lot of freedom especially if you look at it from a designer’s point of view… and that can be a pain as you might want to provide the freedom of wiring and configurating your stuff in the inspector.
ScriptableObjects can help here a lot. They’re underrated and I personally feel dumb that I (for quite some time at least) thought I might not find a usage for them
.
You can also do it the other way around and make everything a MonoBehaviour. But this is simply overkill if you have complex scenario, because this can get out off hands pretty quickly, too. It can be less performant than an alternative approach and every Unity object is basically a native C++ object with a corresponding C# “wrapper” / “accessor” or however you want to call it. But that does not really matter in many situations, again it depends on scale/complexity of the project and target platform etc.
Anyway, other tradeoffs: It already starts with the fact that you have access to any Unity Object directly, even if it’s not meant to be accessible… or even worse, it’ll be a side-effect that happens due to another operation.
Think about the Destroy() method. Nothing prevents you or a team member from a direct call to GetComponent<…> and a subsequent Destroy(…), just as an example. Sure, you can always tell someone to not do X with object Y, if A & B | !C.
So you have to work against that by ensuring everything’s setup as you need, which might result in extremely defensive coding styles. It’s more or less an overhead that could be avoided, always depends what it takes to check the current state and get it back into a valid state, if necessary.
Simply put that’d be an open system with lots of freedom for designers (in regards to Unity features), but also lot’s of potential weaknesses when it comes to coding.
That’s only one of many things to keep in mind when it’s time to decide what has to be a MonoBehaviour or not.
Long story short: There’s no general rule… One the one handthere’s the flexibility, you have to decide whether or not you need the inspector features and in which depth of detail, on the other hand there are always drawbacks that you have to think about.