Skip to content

Commit

Permalink
✨ refactor: lift dependency injection to main and add documentation f…
Browse files Browse the repository at this point in the history
…or its role

           - Moved dependency injection from `MyApp` to `main` to align with Onion Architecture principles.
           - Updated `MyApp` to receive injected dependencies through its constructor.
           - Added Dartdoc to the `main` function explaining its role as the entry point and dependency injector in the outermost circle of the architecture.

Signed-off-by: Dmytro Turskyi <[email protected]>
  • Loading branch information
Turskyi committed Sep 28, 2024
1 parent c71f4fe commit 2a53dcc
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
/coverage/
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ using [streams](https://dart.dev/libraries/async/using-streams).

The project follows the four main layers of Onion Architecture:

1. **Domain Model**: Core business logic and entities.
1. **Domain Model**: Core business logic and models.
2. **Domain Services**: Business rules and operations.
3. **Application Services**: Application-specific logic and orchestration.
4. **Outermost Layer**: Includes User Interface, Infrastructure (DB and/or WS),
Expand Down Expand Up @@ -253,27 +253,34 @@ class _MyHomePageState extends State<MyHomePage> {
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
const MyApp({required this.presenter, super.key});
final CounterPresenter presenter;
@override
Widget build(BuildContext context) {
final FakeCounterDataSource dataSource = FakeCounterDataSource();
final IncrementCounterFakeImpl incrementCounter =
IncrementCounterFakeImpl(dataSource);
final CounterPresenter presenter = CounterPresenter(incrementCounter);
return MaterialApp(
title: 'Flutter Demo',
title: 'Flutter Onion Architecture Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page', presenter: presenter),
home: MyHomePage(
title: 'Onion Architecture Demo Home Page',
presenter: presenter,
),
);
}
}
void main() => runApp(const MyApp());
void main() {
final FakeCounterDataSource dataSource = FakeCounterDataSource();
final IncrementCounter incrementCounter = IncrementCounterFakeImpl(
dataSource,
);
final CounterPresenter presenter = CounterPresenter(incrementCounter);
runApp(MyApp(presenter: presenter));
}
// Infrastructure Component
abstract interface class CounterDataSource {
Expand Down
38 changes: 25 additions & 13 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import 'package:counter_with_onion_architecture/core/application_services/counter_presenter.dart';
import 'package:counter_with_onion_architecture/core/domain/services/increment_counter.dart';
import 'package:counter_with_onion_architecture/core/domain/services/increment_counter_fake_impl.dart';
import 'package:counter_with_onion_architecture/infrastructure/fake_counter_data_source.dart';
import 'package:counter_with_onion_architecture/user_interface/my_app.dart';
import 'package:flutter/material.dart';

/// The [main] is the ultimate detail — the lowest-level policy.
/// It is the initial entry point of the system.
/// Nothing, other than the operating system, depends on it.
/// It is in this [main] component that dependencies should be injected.
/// The [main] is a dirty low-level module in the outermost circle of the onion
/// architecture.
/// Think of [main] as a plugin to the [MyApp] — a plugin that sets up the
/// initial conditions and configurations, gathers all the outside resources,
/// and then hands control over to the high-level policy of the [MyApp].
/// When [main] is released, it has utterly no effect on any of the other
/// components in the system. They don’t know about [main], and they don’t care
/// when it changes.
void main() => runApp(const MyApp());
/// The [main] function represents the ultimate detail — the lowest-level policy
/// in the system.
/// It serves as the initial entry point of the application, and nothing, other
/// than the operating system, depends on it.
/// In the [main] function, dependencies are injected and configurations are
/// set up.
/// This makes [main] a low-level module located in the outermost circle of the
/// onion architecture.
/// Think of [main] as a plugin to the application — a module that gathers all
/// external resources, sets initial conditions, and hands control over to the
/// high-level policy represented by [MyApp].
/// Changes to [main] do not affect any other component in the system, as they
/// are decoupled from it and remain unaffected by its modifications.
void main() {
final FakeCounterDataSource dataSource = FakeCounterDataSource();
final IncrementCounter incrementCounter = IncrementCounterFakeImpl(
dataSource,
);
final CounterPresenter presenter = CounterPresenter(incrementCounter);
runApp(MyApp(presenter: presenter));
}
12 changes: 3 additions & 9 deletions lib/user_interface/my_app.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import 'package:counter_with_onion_architecture/core/application_services/counter_presenter.dart';
import 'package:counter_with_onion_architecture/core/domain/services/increment_counter.dart';
import 'package:counter_with_onion_architecture/core/domain/services/increment_counter_fake_impl.dart';
import 'package:counter_with_onion_architecture/infrastructure/fake_counter_data_source.dart';
import 'package:counter_with_onion_architecture/user_interface/my_home_page.dart';
import 'package:flutter/material.dart';

class MyApp extends StatelessWidget {
const MyApp({super.key});
const MyApp({required this.presenter, super.key});

final CounterPresenter presenter;

@override
Widget build(BuildContext context) {
final FakeCounterDataSource dataSource = FakeCounterDataSource();
final IncrementCounter incrementCounter =
IncrementCounterFakeImpl(dataSource);
final CounterPresenter presenter = CounterPresenter(incrementCounter);

return MaterialApp(
title: 'Flutter Onion Architecture Demo',
debugShowCheckedModeBanner: false,
Expand Down
34 changes: 17 additions & 17 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "5.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
Expand All @@ -71,18 +71,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
url: "https://pub.dev"
source: hosted
version: "10.0.4"
version: "10.0.5"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.5"
leak_tracker_testing:
dependency: transitive
description:
Expand All @@ -95,10 +95,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "5.0.0"
matcher:
dependency: transitive
description:
Expand All @@ -111,18 +111,18 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.8.0"
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
version: "1.12.0"
version: "1.15.0"
path:
dependency: transitive
description:
Expand Down Expand Up @@ -180,10 +180,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
version: "0.7.2"
vector_math:
dependency: transitive
description:
Expand All @@ -196,10 +196,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev"
source: hosted
version: "14.2.1"
version: "14.2.5"
sdks:
dart: ">=3.4.3 <4.0.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^4.0.0
flutter_lints: ^5.0.0

flutter_test:
sdk: flutter
Expand Down
11 changes: 10 additions & 1 deletion test/widget_test.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import 'package:counter_with_onion_architecture/core/application_services/counter_presenter.dart';
import 'package:counter_with_onion_architecture/core/domain/services/increment_counter.dart';
import 'package:counter_with_onion_architecture/core/domain/services/increment_counter_fake_impl.dart';
import 'package:counter_with_onion_architecture/infrastructure/fake_counter_data_source.dart';
import 'package:counter_with_onion_architecture/user_interface/my_app.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
final FakeCounterDataSource dataSource = FakeCounterDataSource();
final IncrementCounter incrementCounter = IncrementCounterFakeImpl(
dataSource,
);
final CounterPresenter presenter = CounterPresenter(incrementCounter);
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
await tester.pumpWidget(MyApp(presenter: presenter));

// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
Expand Down

0 comments on commit 2a53dcc

Please sign in to comment.