ResoCoder / flutter-tdd-clean-architecture-course

https://resocoder.com/flutter-clean-architecture-tdd/
1.99k stars 623 forks source link

Clean architecture with bloc as tree structure(lift state up) #12

Closed cosinus84 closed 4 years ago

cosinus84 commented 4 years ago

I have an AuthBloc on top of the tree. Beneath are ProfileBloc, PostBlocs... When an event is launched to ProfileBloc/PostBlocs usecase called, repository. I get back from repository an 401 error, usecase returns Either as you used. The problem is how do you think is the best way to "tell" this to AuthBloc (because it handles the authentication state)?

  1. Pass the AuthBloc instance to each sub blocs, so they can call authBloc.add(LogoutEvent())
  2. Use a AppEventService using service locator. AuthBloc will listen/filter to AuthExternalEvents from AppEventService. ProfileService will add AuthExternalEvents to AppEventService (seems like an EventBus)
  3. Same as point (2) but add an interceptor to Dio (api calls) that will add the event to AppEventService in case of 401
  4. When creating the repository that will be injected in blocs constructors. Repository(onRevoke: () => authBloc.add(Logout())) (inspired by Felix Angelov)

Any help with this so that still remains in a clean architecture way?

markfili commented 4 years ago

Any new conclusions about this issue?

markfili commented 4 years ago

for future reference, this question is also being answered here: https://github.com/felangel/bloc/issues/766#issuecomment-599152570

cosinus84 commented 4 years ago

@markfili Blocs will use repositiories, repositiories will have ApiService injected in constructor(you can test you repositories). ApiService can be a singleton by using get_it. Inside ApiService we use Dio. With ApiService you can have request that requires token and others that don't

 dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options) {
      if (options.headers.containsKey("requiresToken")) {
        options.headers.remove("requiresToken");
        options.headers["x-auth-id"] = _auth.token;
      }
      return options;
    }));
// so you will use like this
await api.dio.get("/me",
          options: Options(headers: {"requiresToken": true}));

So if we get an 401 from a request inside ProfileRepository (ProfileRepository also has ApiService injected). ApiService can expose an auth stream (like Firebase, FirebaseAuth.instance.onAuthStateChanged, you can use RX). So if 401 is received any subscriber/listener will get this event, UserRepository is injected to AuthBloc constructor and is a listener to this stream. Regarding UserRepository you should expose the stream to AuthBloc (because on dispose AuthBloc can unsubscribe)

I hope I helped you somehow.