I described dependency inversion and a way to use in dart/flutter in this article. In this post I will use the same concepts and libraries.
Monorepo
Monorepo refers to the practice of software development in which all components of an application or system are maintained within a single repository. A good article that describes well what it is, pros and cons is the following: monorepo.tools.
In the case of a flutter application an excellent monorepo manager is Melos, here a tutorial on how to create and manage it.
A concrete use case
Suppose our application is a social app and we need to manage posts and users. The application is divided into packages where each package contains views and logics to manage a specific section of the application (users, posts, ...). Route is defined within the core
package. At this point each package defines its routes which will then be displayed in the sidebar (see uml). The result is as follows:
- The abstract class Route is in core;
- Its implementations are in different packages (PostRoute, UserRoute, ...).
Implementation
This can be the folder structure,
app
- lib
...
- pubspec.yaml
modules
- core
- pubspec.yaml
...
- user
- pubspec.yaml
...
- post
- pubspec.yaml
...
where the various pubspec.yaml
will look like:
## app
dependencies:
core:
path: ../modules/core
user:
path: ../modules/user
post:
path: ../modules/post
## user
dependencies:
core:
path: ../core
// app code
@Injectable(as: Navigation)
class AppNavigation implements Navigation {
@override
List<Route> routes = [UsersRoute()];
}
// core code
@injectable
class SideBarMenu extends StatelessWidget {
const SideBarMenu(this.navigation);
final Navigation navigation;
@override
Widget build(BuildContext context) {
return Column(
children: navigation.routes
.map(
(r) => Link(
url: r.url,
child: Text(r.name),
),
)
.toList(),
);
}
}
abstract class Navigation {
List<Route> get routes;
}
abstract class Route {
String get url;
String get name;
Widget view();
}
// post package
class PostsRoute implements Route {
String url => "posts";
String name => "Posts";
Widget view => Container();
}
Each package defines a micropackage (core
, user
, posts
). Additionally in core
we define getIt and export it for the other modules.
// only in core
GetIt getIt = GetIt.instance;
// every package
@InjectableInit.microPackage()
initMicroPackage() {}
In app
we init the injection and register the various micro packages.
@InjectableInit(
initializerName: 'init',
preferRelativeImports: true,
asExtension: false,
// packages
externalPackageModulesBefore: [
ExternalModule(CorePackageModule),
ExternalModule(UserPackageModule),
ExternalModule(PostPackageModule),
])
FutureOr<void> configureInjection() => init();
In this case all packages were registered (in order) with the method externalPackageModulesBefore
but there are three properties that can be used (in order of registration): 1. externalPackageModulesBefore
, 2. externalPackageModules
, 3. externalPackageModulesAfter
.
Conclusion
This concludes the article on Monorepo & Dependency Injection.
For more information on micropackage I recommend reading injectable package.
Top comments (0)