Dependency Injection/Inversion, DDD, and Clean Code with Riverpod #3549
-
I'm having a conceptual issue I can't seem to get myself out of. I found this discussion that I believe relates but it did not resolve in a way that helps me move forward: (#450). I'm attempting to learn app development and a deluge of clean coding concepts all at once. I am writing a practice app with the 4 DDD layers, Presentation, Application, Domain, and Infrastructure. I am using TDD to write tests to lead me and mocking as needed. For the sign-in/auth feature, I've written the entities, states, and the interface for an auth facade ( I am now writing a NotifierProvider for the app's Sign-in page in the Application layer, but I do not know how to inject the interface without providing a concrete implementation. I would rather not write an implementation in the Infrastructure layer just so I can mock it all away in the Application tests. To get around this, I do something like this, @riverpod
class SignInNotifier extends _$SignInNotifier {
late final IAuthFacade _authFacade;
@override
SignInState build() => SignInState.initial();
void injectDependencies(IAuthFacade authFacade) {
_authFacade = authFacade;
}
// methods...
} so that in my testing suite I can mock and inject the interface as needed. This works well enough for isolated development, but now I have to deal with setting the dependency when the code actually runs and this feels wrong. Obviously using a registering system instead like So how does Riverpod's Provider system fit in here? I can't define a provider without a concrete implementation (unless I'm missing something), and I don't see the need to have an implementation when don't need it (I'm writing the Notifier Provider in isolation, the interface is defined, and I mock the implementation outputs as needed). I believe it also breaks the dependency inversion principle. To use a Provider I believe I'll need to turn my whole coding process somewhat upside-down and write a half-way implementation of |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 4 replies
-
Usually, DI in riverpod works like any other provider. Just to give an option. @riverpod
IAuthFacade authFacade(AuthFacadeRef ref) => throw UnimplementedError(); // In "main" or where required
ProviderScope(
overrides: [authFacadeProvider.overrideWithValue(concreteAuthImpl)]
child: ...
) For testing replace |
Beta Was this translation helpful? Give feedback.
-
Also, you cannot inject dependencies into private fields or via the constructor as you would do with DI frameworks on other platforms such as .NET or Java. You have to pull the dependency from the container via your What you can do is create the SignInNotifier class with its dependencies passed in the constructor, and create a provider of SignInNotifier that fetches the dependencies from the container and returns an instance of SignInNotifier. class SignInNotifier {
final IAuthFacade _authFacade;
SignInNotifier(this._authFacade);
// methods...
}
@riverpod
SignInNotifier signInNotifier(SignInNotifierRef ref) {
final authFacade = ref.watch(authFacadeProvider);
return SignInNotifier(authFacade);
} |
Beta Was this translation helpful? Give feedback.
Usually, DI in riverpod works like any other provider. Just to give an option.
For testing replace
concreteAuthImpl
with mock.But maybe I am missing something.