Clean Architecture why do we have use cases?
Asked Answered
T

2

10

in Clean Architecture we have use cases as business logic rules. but we can also call functions in the repository directly so we don't need use cases. what are the reasons behind this?

sample use case

class GetMarketUseCase implements UseCase<Stream<ResponseModel>, void> {
  final PriceTrackerRepository priceTrackerRepository;

  GetMarketUseCase(this.priceTrackerRepository);

  @override
  Stream<ResponseModel> call(void params) {
    return priceTrackerRepository.getMarketWithSymbols();
  }
}

sample repository

class PriceTrackerRepositoryImpl implements PriceTrackerRepository {
  late final PriceTrackerDataSource priceTrackerDataSource;

  PriceTrackerRepositoryImpl(this.priceTrackerDataSource);

  @override
  Stream<ResponseModel> getMarketWithSymbols() {


    return _marketStreamController.stream;
  }
Tactic answered 12/10, 2022 at 7:46 Comment(0)
R
14

Because it prevents your presenter become a God object when it must handle UI logic and buniness logic too.

For example: a logout use case, you need to call API logout inside AuthenRepo, unregister Firebase FCM token, close socket, and maybe clear some local data inside CartRepo, UserRepo, ... then imagine put all those things in Presenter, what a mess instead of create a LogoutUseCase call to every repositories you need

And moreover, you can use it for many places, like when user press Logout button, when user login token is expired, ... just call LogoutUseCase instead of copy code from this Presenter to another Presenter, also make is easy for you when you need to change something about logout requirement

Code example for Presenter is Bloc:

AuthBloc with UseCase:

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  AuthBloc(AuthState state) : super(state) {
    on<AuthLogoutEvent>(_onLogout);
  }

  Future<void> _onLogout(
      AuthLogoutEvent event,
      Emitter<AuthState> emit,
      ) async {
    await getIt<LogoutUseCase>().call(NoParams());
  }
}

AuthBloc without UseCase:

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  AuthBloc(AuthState state) : super(state) {
    on<AuthLogoutEvent>(_onLogout);
  }

  Future<void> _onLogout(
      AuthLogoutEvent event,
      Emitter<AuthState> emit,
      ) async {
    await getIt<AuthRepo>().logout();
    await FirebaseMessaging.instance.deleteToken();
    await getIt<SocketRepo>().close();
    await getIt<CartRepo>().clearData();
    await getIt<UserRepo>().clearData();
    // maybe more Repo need to call here :((
  }
}

In your example above, it is only simple use case with only action getMarketWithSymbols(), then I agree Usecase here is redundant, but for consistency, it should have and who know, in the future this UseCase need to scale up, then it will easy for you then.

Regina answered 23/10, 2022 at 8:44 Comment(2)
So my question is how would handle if there was in error in LogoutUseCase.... let us say await FirebaseMessaging.instance.deleteToken(); How would send that error message back the presentation layer?Buonaparte
LogoutUseCase should return a Result which contain error if fail, inside LogoutUseCase you will have some code to return the Result you need then in your presenter you can catch that result and emit an error message to UIRegina
O
4

We need a usecase as a kind of intermediate link between presentation and domain layers, to ensure independence of all layers

enter image description here

Orangy answered 12/10, 2022 at 8:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.