solid-software / hydrated

🚰 A BehaviorSubject for Flutter with automatic persist and hydrate.
https://pub.dev/packages/hydrated
MIT License
115 stars 18 forks source link

Abstract methods to store/load the object #1

Closed magillus closed 5 years ago

magillus commented 5 years ago

I love this feature to persist the value on the Bloc. However shared preferences dependency makes it Flutter only package (maybe it is fine for now). In case of Bloc pattern, it should not know anything about Flutter, because we could use that Bloc on AngularDart for example. Probably Hummingbird will make AngularDart obsolete but not sure when that can happen.

I think this package can go to Dart-only and provide flutter (shared preference, etc.) overrides, also will give any other future storage mechanisms (Firebase for example).

Suggestion: AbstractHydrate with methods to override: Fetches from a storage by a key (current HydrateSubject could override this with SharedPreferences use)

T fetch() {
}

Stores value (serializes if needed/supported) and current HydrateSubject can override this with current implementation.

store(T val) {
}

This would allow to have the hydrate package Dart only, and 'flutter_hydrate' (or other name) for example use SharedPreferences. In future more "plugin" packages can be created to serialize or store to DB or store to file, even Firebase.

Just idea, see my https://pub.dartlang.org/packages/flutter_fimber and https://pub.dartlang.org/packages/fimber packages that follows that logic.

lukepighetti commented 5 years ago

Hi @magillus!

My first goal with Hydrated was to make it easy to refactor a Flutter BLoC app to persist very basic values, but through conversations with community members I quickly realized that it could be much more.

I must first admit ignorance with how to architect a package to be both abstract and useful. I am familiar with projects like Apollo who allow different storage drivers to be used with their software. Thing is, they have one god class that gets instantiated with a driver, not dozens of smaller streams that need that treatment.

So Hydrated makes a few assumptions as of today

  1. You're using Flutter
  2. The only streams you need persisted are BehaviorSubjects
  3. Those BehaviorSubjects are of type int, double, bool, String, List<String>
  4. You can use shared_preferences

Perhaps the larger question that Hydrated can solve is: how do we persist streams in Dart? That is an interesting problem to solve.

I have not even considered this but I think it is a worthwhile goal assuming there are contributors to the project. I know I won't be able to build/maintain something like that on my own.

Perhaps there is a way to create a StreamTransformer that accepts a storage driver as an argument.

MisterJimson commented 5 years ago

I took a crack at this #2

Would need further separation and multiple pub packages to remove the dependancy on shared pref, but it's a good start and allows people to swap in whatever implementations they want.

lukepighetti commented 5 years ago

Just looked over #2 and if I understand correctly you extend AbstractHydratedSubject and use HydratedSetup in the constructor to build out your own type of HydratedSubject, overriding hydrate() and persistValue(). I think this would be an interesting way to allow people to build out many different flavors of HydratedSubject.

Is it feasible to have AbstractHydratedSubject be a private class and use Hydrated as a collection for multiple different types of HydratedSubjects to handle Flutter, AngularDart, and server needs?

I am not sure how projects handle deps in those situations. Is Dart tooling smart enough to ignore SharedPreferencesHydratedSubject (yikes) deps if you're only importing a file that contains DatabaseHydratedSubject?

Maybe something like:

import "package:hydrated/shared_preferences.dart" show HydratedSubject

Or a compositional approach

import "package:hydrated/hydrated"

hydratedSubject(String key) => new HydratedSubject(SharedPreferencesDriver(key));

final count$ = hydratedSubject("count");
lukepighetti commented 5 years ago

I need to use HydratedSubject more in production before I can have a clear opinion on which direction is the best way to go. I feel like there are limitations to the current implementation that I cannot possibly understand yet. Is it too restricting for the project to only care about BehaviorSubjects that hydrate? Is it better to hydrate the root of a stream or the tail? Should persist/hydrate happen at the beginning of a mapped stream or the end? Does the StreamController need to be aware of the hydration process if we hydrate at the end of a mapped stream?

MisterJimson commented 5 years ago

The reason it’s not private is so people can use their own logic without requiring updates to this repo.

For example store data in a sqlite db or use a web API, etc. All without changes to this repo.

lukepighetti commented 5 years ago

Yeah so the question is: is it better to have one repo for all implementations or separate repos for every single driver

MisterJimson commented 5 years ago

Ideally the main package wouldn't have a shared preferences dependancy.

You can keep them all in this repo, but in different packages/projects in the root.

MisterJimson commented 5 years ago

Also don't think you need to write implementations for every variation out there. You only need 2 or 3. The reason the abstract is there is so people can do what fits their needs.

lukepighetti commented 5 years ago

@magillus @MisterJimson Hey guys, I appreciate the interest in making Hydrated a more abstract and useful package to the larger Dart community. I've given it some thought and since I have no interest in Dart for the web or server I will be keeping this package Flutter specific. But I do encourage those with more diverse experience with Dart to fork the project and create something more abstract. I would be able to contribute to the Flutter implementations of such a project.

Cheers

magillus commented 5 years ago

I understand, I will revise this functionality and see if I need this in future.

lukepighetti commented 5 years ago

I spoke with @Solido briefly about adding Flutter specific drivers to Hydrated, for example, one that automatically persists to SharedPrefs (current), Firebase Realtime Database, Firestore, Sqflite, MMKV, etc.

I have no experience managing a project with a scope of that width so I would appreciate some community comment on how we might move forward with abstracting storage layer (or supporting multiple storage layers) while keeping Flutter as a first class citizen @magillus @MisterJimson

It's very important to me that the average Flutter developer be able to drop a HydratedSubject in place of a BehaviorSubject and have local persistence. Anything outside of that is extracurricular in my mind, but I recognize that the community has expressed a desire for more.

With that said, I am not sure how this project can provide anything better than someone forking and creating their own with a specific driver. Perhaps I am blind to a solution.