Short answer: Not Unity-specific, but a decent reference: Design Patterns.
Long answer: It’ll come to you in your moment of need 
When I started Unity development, I tried every hard to find - and spent a lot of time researching - those so-called “best practices”, such as the MVC model, decoupling, etc.
As I got further and further into the game, I came to realize that these are not universal truths; rather, they’re tools to accomplish a specific job.
For example, if I want the destruction of a game object to trigger something in the game logic, I might be better off using an event. On the other hand, if I’m using object pooling, then I may as well assign the game logic script as a public reference in the prefab and call the relevant logic script’s function from the game object’s OnDisable().
Similarly, writing decoupled scripts is great in theory, but, more often than not, it forces you to rely on events to make scripts communicate - and, before you know it, you’ll have dozens of events and hundreds of listeners all ferrying information between scripts, which can quickly become overwhelming and impossible to debug. Besides, if the two scripts are going to be attached to the same component in 99% cases, do you really need to decouple them? Sure, they say that one should write re-usable code - but, honestly, what are the odds you’re going to be reusing your code in another game?
I guess the point I’m trying to make here is that, from my experience, at least, it makes very little sense to try to follow a specific pattern until you have enough experience to appreciate when doing so is or isn’t useful. In other words, I say do as you will as long as you harm no one, including yourself 
By that latter, of course, I mean basic things such as:
- No GameObject.Find() in Update();
- Object pooling, if warranted
- No over-reliance on assigning variables in the inspector
Personally, the way I’m structuring my current project is as follows:
-
GameController.cs, which handles game logic and operations (e.g., purchasing things, applying multipliers, calculating XP requirements, dealing damage, etc)
-
UI_Controller.cs, which handles the majority of general UI interactions (e.g., showing a particular pop-up, refreshing select UI components in its Update() function, disabling UI gameobjects when dictated by the game logic, etc). Predictably, it contains a metric ton of public references to UI gameobjects
-
Attributes.cs, which stores character information and statistics, such as attack damage, level, coins, highest level ever achieved, resistance to specific damage types, etc). Attributes.cs is a static class, so it gets instantiated by GameController as a singleton at the start of every game. It’s also what gets saved and loaded every time you (re)start the game.
-
Finally, I have local, “low-level” scripts handling their specific roles, be they gameplay or UI, for example:
-
UI_StatsScreenController.cs script handles, in its FixedUpdate() function, the text of all player statistics such as total monsters killed, total damage dealt, etc;
-
UI_PurchaseItem.cs handles the UI behavior of items in one of the scrollable purchase menus - when they appear, what image they show, whether the “Buy” button becomes disabled when the player does not have enough money, etc.
-
Fall.cs script takes care of objects falling;
-
Spawn.cs script defines the attributes of an individual monster;
-
Spawner.cs script handles spawning monsters, etc.
Hope it helps,
George