w461
w461

Reputation: 2678

How to access/inject ObjectBox database in repository in Flutter - Reso Coder DDD

all the examples, I have seen, initialize ObjectBox in a State(less/full)Widget. I am using a layered architecture (currently refactoring to DDD) and wonder, how to inject my ObjectBox properly.

In my repository, I inject the data sources using the injectable and the getit packages with

@injectable
@LazySingleton (as: IJournalsRepository)
class JournalsRepository implements IJournalsRepository {
  final JournalsRemoteDataSource journalsRemoteDataSource;
  final JournalsLocalDataSource journalsLocalDataSource;
  JournalsRepository(this.journalsLocalDataSource, this.journalsRemoteDataSource);

Those packages then create an instance of JournalsRemoteDataSource and of JournalsRemoteDataSource and inject it into the repository.

The ObjectBox example shows for initialization

class _MyHomePageState extends State<MyHomePage> {
  Store? _store;
  @override
  void initState() {
    super.initState();
    openStore().then((Store store) => _store = store;);
  }

  @override
  void dispose() {
    _store?.close();  // don't forget to close the store
    super.dispose();
  }
}

So I am lacking an idea on how an injector could initialize ObjectBox or how I could access the objectBox object from within the injected JournalsRemoteDataSource if I would initialize objectBox in MyApp() (which is upstream to the HomePage)

PS: reopening the box in JournalsRemoteDataSource on every read/write event has a very poor performance

========== UPDATE ========== supplementing my comment to @vaind

I have found your answer on this similar question in the meantime (not sure why I did not see it, initially). I hope to get this approach working here, too. However, I have still issues initializing the store. My prototype comes from Firestore and looks like this:

import 'package:firebase_auth/firebase_auth.dart';
import 'package:injectable/injectable.dart';

@module
abstract class FirebaseInjectableModule {
  @lazySingleton
  FirebaseAuth get firebaseAuth => FirebaseAuth.instance;
}

though I do not understand where the getter firebaseAuth comes from and haven't found any explanation, yet. Anyways, I adapted that to

import 'package:injectable/injectable.dart';
import 'package:objectbox/objectbox.dart';
import 'package:test/objectbox.g.dart';

@module
abstract class ObjectboxInjectableModule {
  @lazySingleton
  Future<Store> get store async => await openStore();
}

and use this with

@LazySingleton (as: ILocalDataSource)
class ObjectBoxDataSource implements ILocalDataSource {
  final Store _store;
  final Box<JournalOboxEntity> _box;
  ObjectBoxDataSource(this._store) : _box = _store.box();

Besides final Store _store being grey in IntelliJ (unused variable), I receive the error

You tried to access an instance of Store that is not ready yet
'package:get_it/get_it_impl.dart':
Failed assertion: line 404 pos 9: 'instanceFactory.isReady'

Upvotes: 2

Views: 1947

Answers (2)

w461
w461

Reputation: 2678

So following another answer of vaind, I implemented this as follows. My architecture follows a merge of Reso Coder's DDD and Clean Architecture tutorials. Basically it is DDD with the local/remote data source layer of Clean Architecture.

INFRASTRUCTURE directory

abstract data sources

abstract class ILocalDataSource {
  Future<JournalDto> getJournal(int id);
  Future<void> storeJournal(JournalDto record);
}
abstract class IRemoteDataSource {
  Future<JournalDto> getJournal(int problemClassId);
}

data source implementation

@LazySingleton (as: ILocalDataSource)
class ObjectBoxDataSource implements ILocalDataSource {
  final Store _store;
  final Box<JournalOboxEntity> _box;
  ObjectBoxDataSource(this._store) : _box = _store.box();

injectable module in infrastructure/core

@module
abstract class ObjectBoxInjectableModule {
  @preResolve               // <<<<<<<<<<<<< needed for async init
  @lazySingleton
  Future<Store> get store async => await openStore();
}

And now the trick to get it work: My later errors where caused by an injector init not yet finished. After changing injection.dart in the root folder to a Future and awaiting the call in main(), it worked. injection.dart now looks like this:

final GetIt getIt = GetIt.instance;

@injectableInit
Future<void> configureInjection(String env) async {
  $initGetIt(getIt, environment: env);
}

Upvotes: 1

vaind
vaind

Reputation: 2386

I don't have experience with packages get_it & injectable, but from the docs, I think the following alternatives would work. Using get_it directly, not sure about the right way to achieve the same with injectable (generator for get_it) but I guess if you're familiar with it you can configure it to generate the same code.

Alternative A, lazy (async) singleton

GetIt.I.registerSingletonAsync<Store>(openStore);

Alternative B, setup in main(), probably preferrable

change your main to sth like:

void main() async {
  GetIt.I.registerSingleton<Store>(await openStore());
  runApp(MyApp());
}

Note: Looks like get_it provides a way to reset, which would result in reopening the same store. To avoid issues if you use that, you'd also need to implement a version of get_it's dispose that calls store.close().

Upvotes: 0

Related Questions