モジュール間通信の目的
Flutter の大規模開発では、複数の機能モジュール(features)が共存します。直接依存すると構造が崩れやすく、保守性が低下します。そのため、契約(contracts)を介した疎結合な通信が重要です。
基本原則
- features モジュール間で直接依存しない
- core/contracts にインターフェースを定義
- 実装は各モジュール内で行い、DI で注入
- Provider を通して利用
モジュール間通信の構造例
core/
contracts/
auth_service.dart # 契約(インターフェース)
di/
providers.dart # Provider トークン
features/
auth/
data/services/
auth_service_impl.dart # 実装
auth_module.dart # DI 登録
契約の定義(core/contracts/auth_service.dart)
abstract class AuthService {
Future<bool> isLoggedIn();
Future<String?> currentUserName();
}
Provider トークン(core/di/providers.dart)
final authServiceProvider = Provider<AuthService>((ref) {
throw UnimplementedError('AuthService is not provided');
});
class AppBootstrap {
final List<AppModule> modules;
AppBootstrap(this.modules);
(GoRouter, List<Override>) build() {
final router = GoRouter(
routes: [for (final m in modules) ...m.routes],
);
final overrides = <Override>[
for (final m in modules) ...m.overrides,
];
return (router, overrides);
}
}
実装
features/auth/data/services/auth_service_impl.dart
class AuthServiceImpl implements AuthService {
@override
Future<bool> isLoggedIn() async => true;
@override
Future<String?> currentUserName() async => 'Poyopoyo';
}
モジュール登録
features/auth/auth_module.dart
class AuthModule extends AppModule {
@override
List<Override> get overrides => [
authServiceProvider.overrideWith((ref) => AuthServiceImpl()),
];
@override
List<GoRoute> routes = const [];
}
PoyopoyoApp 配置
final modules = <AppModule>[
SettingsModule(),
AuthModule(),
];
final bootstrap = AppBootstrap(modules);
final (router, overrides) = bootstrap.build();
呼び出し
final authService = ref.read(authServiceProvider);
authService.isLoggedIn();
メリット
- 機能モジュール間の依存を排除し疎結合化
- 実装の差し替えが容易(テスト用モックなど)
- モジュール追加・削除が容易