bloc icon indicating copy to clipboard operation
bloc copied to clipboard

Why shouldn't blocs reference other blocs?

Open bw-flagship opened this issue 1 year ago • 7 comments

First of all: Thanks for this great package!

Description

We have a medium to large-sized codebase with hundreds of cubits. Many cubits reference each other.

My colleagues started to do some refactorings because they read in the bloc documentation that blocs should not reference each other.

Source in the docs: https://bloclibrary.dev/#/architecture?id=bloc-to-bloc-communication

I am wondering if is a good rule. When cubits reference each other, I feel like the code is easy to read and easy to maintain. Instead, if Cubit A and Cubit B logically depend on each other, it's very complicated code because you route the information through at least one other layer, just to avoid the reference.

The only reason I can see in the docs it this:

Generally, sibling dependencies between two entities in the same architectural layer should be avoided at all costs, as it creates tight-coupling which is hard to maintain. Since blocs reside in the business logic architectural layer, no bloc should know about any other bloc.

In all architecture patterns that crossed my way, it was allowed (and common) that entities inside the same architectural layer reference each other. One very obvious example is that in frontend frameworks, ui elements get referenced by other ui elements.

Am I missing something? Looking forward to hearing different opinions :)

bw-flagship avatar Nov 17 '23 15:11 bw-flagship

That's because a Cubit is similar to a ViewModel and manages the state for one View. Coupling Cubits to each other will indirectly couple the state management of multiple views, which can easily lead to side effects and bugs. If you want to share logic then use a pattern such as Clean-Architecture and create "Use-Case" classes in your domain layer, which you can then use within your Cubits

MrIceman avatar Nov 21 '23 12:11 MrIceman

@MrIceman Thanks for your reply.

I try to create an example to make it clearer:

image

Here, the ToDosCubit needs to reference the dataPreferenceCubit to know if the data should be synced.

Would you say there needs to be some kind if "usecase"-class which keeps the state of the data preferences, so the todosCubit can access it?

bw-flagship avatar Nov 21 '23 13:11 bw-flagship

I think your instincts are correct here. Adding mounds of code to decouple all the blocs is just going to make things more complex and harder to debug.

In your example here with DataPreferences, this is not view state, it's global state that many actors across the application would be interested in. The solution is to either let all of the other cubits reference it, which is straightforward and simple to debug, or to create an abstraction to somehow pass this state around to all of the other cubits indirectly.

The question needs to be asked: what value do you get from this decoupling? I see two primary benefits: future flexibility, and easier testing. But do these things even matter to you?

  • Future flexibility: The de-coupled method allows you to potentially delete the DataPreferencesCubit completely, and replace with something else, without breaking anything... but is this likely to happen? Is this benefit valuable to you? Would it really be much work to do the same thing with the coupled implementation inside a modern IDE?

  • Easier Testing: The de-coupled method also makes it easier to unit-test the other cubits, but again, is this useful to you? Are you actually writing, or planning on writing these tests? If the answer is no, there is actually no value to be gained here.

On the other side of the coin, the coupled method is easier to debug and simpler to grok. These benefits almost certainly do have value for you.

esDotDev avatar Nov 21 '23 19:11 esDotDev

@esDotDev Thanks for sharing your insights— I thoroughly agree. After working with both architectures, I find that the one where cubits reference other cubits is significantly easier to read, maintain, and debug.

That said, I do recognize the advantages of extreme decoupling, and I appreciate your explanation of them.

In my current practice, I recommend allowing cubits to reference other cubits in smaller to medium-sized teams (1-5 developers). For larger teams (6 or more developers), I suggest adopting extreme decoupling.

However, I still believe it's a mistake for the documentation to rigidly state that blocs should not reference other blocs.

bw-flagship avatar Nov 22 '23 09:11 bw-flagship

@bw-flagship i believe your DataPreferencesCubit should instead be some kind of service or repository that is injected into your TodosCubit so that it can be properly mocked for testing and re-used from other areas of your app where it may be useful to know about some other preferences that the user has.

blackchineykh avatar Nov 28 '23 19:11 blackchineykh

@blackchineykh Yes, this would work, even though it adds just another component with redundant information. (and I can mock the DataPreferencesCubit as well as any other class)

bw-flagship avatar Nov 29 '23 08:11 bw-flagship

Had the exact same question months ago, see #3816.

wujek-srujek avatar Dec 01 '23 23:12 wujek-srujek

Closing this as a duplicate of #3816.

I'll try to leave my thoughts on the original issue within the next few days, apologies for the delayed response.

felangel avatar Apr 04 '24 18:04 felangel