Try organizing your code around the data your components have to work with and let your code react to changes to that data. Using Scriptable-objects is a great way to facilitate this model, because with them your game components will reference data assets instead of other components, reducing dependencies, which gets harder and harder to manage as the complexity of your application grows.
I can really recommend watching Ryan Hipple's presentation titled "Game Architecture with Scriptable Objects". [0]
At our studio we've built a framework based on these ideas for a high-complexity game project and we're very happy with it so far. We have a clean and well-performing codebase that's easy to maintain and test, because no sub-system has any hard dependency on any other sub-system.