LogoSkills

Dependency Wiring Checklist

**Removed:**

Dependency connection guide for adding new Feature modules (Pure DI - getIt/injectable removed)

Architecture Change (Epic #4144)#

Removed:

  • di/ folder (injector.dart, injector.module.dart)
  • @injectable, @lazySingleton, @singleton, @microPackageInit annotations
  • GetIt-based DI (completely removed from feature code)
  • ExternalModule, PackageModule, Service Locator pattern
  • getIt<T>() calls

New Pattern:

  • Global BLoC: Create directly in bootstrap.dart -> Pass to App via GlobalBlocProviders
  • Page BLoC: Create directly via BlocProvider(create: (_) => MyBloc())
  • UseCase: UseCase([IRepo? repo]) : _repo = repo ?? ConcreteRepo() pattern
  • BLoC access from Widget: context.read<AuthBloc>()

Checklist#

1. Feature Module Structure (No di/ folder!)#

feature/{type}/{feature_name}/lib/src/
โ”œโ”€โ”€ data/
โ”‚   โ””โ”€โ”€ repository/
โ”œโ”€โ”€ domain/
โ”‚   โ”œโ”€โ”€ entity/
โ”‚   โ”œโ”€โ”€ repository/    # I{Feature}Repository (interface)
โ”‚   โ””โ”€โ”€ usecase/
โ”œโ”€โ”€ presentation/
โ”‚   โ””โ”€โ”€ bloc/
โ””โ”€โ”€ route/

2. UseCase Pattern (Optional Constructor Injection)#

class GetDataUseCase {
  /// [GetDataUseCase]๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  const GetDataUseCase([IDataRepository? repo])
      : _repo = repo ?? const DataRepository();

  final IDataRepository _repo;

  Future<Either<Failure, Data>> call(GetDataParams params) {
    return _repo.getData(params);
  }
}

3. BLoC Pattern (Optional Constructor Injection)#

class MyBloc extends Bloc<MyEvent, MyState> {
  MyBloc({
    GetDataUseCase? getDataUseCase,
    AuthBloc? authBloc,  // cross-BLoC ์˜์กด์„ฑ: nullable
  }) : _getDataUseCase = getDataUseCase ?? const GetDataUseCase(),
       _authBloc = authBloc,
       super(const MyState());

  final GetDataUseCase _getDataUseCase;
  final AuthBloc? _authBloc;
}

4. Providing BLoC in Page#

class MyPage extends StatelessWidget {
  const MyPage({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => MyBloc()
        ..add(const MyEvent.loadRequested()),
      child: const MyView(),
    );
  }
}

5. Global BLoC (bootstrap.dart)#

// bootstrap.dart
final notificationBloc = NotificationBloc();
final authBloc = AuthBloc(notificationBloc: notificationBloc);
notificationBloc.initAuthListener(authBloc);

// Pass to App
runApp(App(blocs: GlobalBlocProviders(
  authBloc: authBloc,
  notificationBloc: notificationBloc,
)));

6. App (BlocProvider.value)#

// App
MultiBlocProvider(
  providers: [
    BlocProvider<AuthBloc>.value(value: blocs.authBloc),
    BlocProvider<NotificationBloc>.value(value: blocs.notificationBloc),
  ],
  child: const MaterialApp(...),
)

7. Accessing BLoC from Widget#

// โœ… Use context.read
context.read<AuthBloc>().add(const AuthEvent.signOut());

// โŒ getIt<AuthBloc>() prohibited

8. Feature Module Barrel File#

// lib/{feature_name}.dart
library;

export 'src/data/data.dart';
export 'src/domain/domain.dart';
export 'src/presentation/presentation.dart';
export 'src/route/route.dart';
// No di/ export!

9. Router Initialization#

// late static final + initializeRouter pattern
AppRouter.initializeRouter(authBloc);

10. Tests (Direct Injection)#

class MockGetDataUseCase extends Mock implements GetDataUseCase {}

blocTest<MyBloc, MyState>(
  'emits [loading, loaded] when load succeeds',
  setUp: () {
    when(() => mockGetData(any()))
        .thenAnswer((_) async => right(testData));
  },
  build: () => MyBloc(getDataUseCase: mockGetData),
  act: (bloc) => bloc.add(const MyEvent.loadRequested()),
  expect: () => [
    const MyState.loading(),
    MyState.loaded(data: testData),
  ],
);

Common Mistakes#

Mistake 1: Using getIt#

// โŒ getIt usage prohibited in Feature code
create: (_) => getIt<MyBloc>(),

// โœ… Direct creation
create: (_) => MyBloc(),

Mistake 2: Calling getIt in UseCase#

// โŒ Getting Repository via getIt prohibited
IDataRepository get _repo => getIt<IDataRepository>();

// โœ… Direct creation via constructor default
const GetDataUseCase([IDataRepository? repo])
    : _repo = repo ?? const DataRepository();

Mistake 3: Creating di/ folder#

// โŒ Creating di/ folder prohibited
feature/{type}/{feature_name}/lib/src/di/injector.dart

// โœ… No di/ folder
feature/{type}/{feature_name}/lib/src/
โ”œโ”€โ”€ data/
โ”œโ”€โ”€ domain/
โ”œโ”€โ”€ presentation/
โ””โ”€โ”€ route/

Build Commands#

# Full build (build_runner - Freezed, etc.)
melos run build

# Build specific package only
cd feature/{module_type}/{feature_name}
dart run build_runner build --delete-conflicting-outputs