diff --git a/doc/news/cherrypick-3.x_en.md b/doc/news/cherrypick-3.x_en.md new file mode 100644 index 0000000..0d5626b --- /dev/null +++ b/doc/news/cherrypick-3.x_en.md @@ -0,0 +1,178 @@ +# Release - CherryPick 3.x + +> **CherryPick** — a lightweight and modular DI framework for Dart and Flutter that solves dependency injection through strong typing, code generation, and dependency control. + +Version **3.x** was recently released with significant improvements. + +## Main Changes in 3.x + +* **O(1) dependency resolution** — thanks to Map indexing of bindings, performance does not depend on the size of the scope in the DI graph. This provides noticeable speedup in large projects. +* **Protection against circular dependencies** — checking works both within a single scope and across the entire hierarchy. When a cycle is detected, an informative exception with the dependency chain is thrown. +* **Integration with Talker** — all DI events (registration, creation, deletion, errors) are logged and can be displayed in the console or UI. +* **Automatic resource cleanup** — objects implementing `Disposable` are properly released when the scope is closed. +* **Stabilized declarative approach support** — annotations and code generation now work more reliably and are more convenient for use in projects. + +## Resource Cleanup Example + +```dart +class MyServiceWithSocket implements Disposable { + @override + Future dispose() async { + await socket.close(); + print('Socket closed!'); + } +} + +class AppModule extends Module { + @override + void builder(Scope currentScope) { + // singleton Api + bind() + .toProvide(() => MyServiceWithSocket()) + .singleton(); + } +} + +scope.installModules([AppModule()]); + +await CherryPick.closeRootScope(); // will wait for async dispose to complete +``` + +## Circular Dependency Checking + +One of the new features in CherryPick 3.x is built-in cycle protection. +This helps catch situations early where services start depending on each other recursively. + +### How to Enable Checking + +For checking within a single scope: + +```dart +final scope = CherryPick.openRootScope(); +scope.enableCycleDetection(); +``` + +For global checking across the entire hierarchy: + +```dart +CherryPick.enableGlobalCycleDetection(); +CherryPick.enableGlobalCrossScopeCycleDetection(); +final rootScope = CherryPick.openRootScope(); +``` + +### How a Cycle Can Occur + +Suppose we have two services that depend on each other: + +```dart +class UserService { + final OrderService orderService; + UserService(this.orderService); +} + +class OrderService { + final UserService userService; + OrderService(this.userService); +} +``` + +If we register them in the same scope: + +```dart +class AppModule extends Module { + @override + void builder(Scope currentScope) { + bind().toProvide(() => UserService(scope.resolve())); + bind().toProvide(() => OrderService(scope.resolve())); + } +} + +final scope = CherryPick.openRootScope() + ..enableCycleDetection() + ..installModules([AppModule()]); + +scope.resolve(); +``` + +Then when trying to resolve the dependency, an exception will be thrown: + +```bash +❌ Circular dependency detected for UserService +Dependency chain: UserService -> OrderService -> UserService +``` + +This way, the error is detected immediately, not "somewhere in runtime". + +## Integration with Talker + +CherryPick 3.x allows logging all DI events through [Talker](https://pub.dev/packages/talker): registration, object creation, deletion, and errors. This is convenient for debugging and diagnosing the dependency graph. + +Connection example: + +```dart +final talker = Talker(); +final observer = TalkerCherryPickObserver(talker); +CherryPick.setGlobalObserver(observer); +``` + +After this, DI events will be displayed in the console or UI: + +```bash +┌─────────────────────────────────────────────────────────────── +│ [info] 9:41:33 | [scope opened][CherryPick] scope_1757054493089_7072 +└─────────────────────────────────────────────────────────────── +┌─────────────────────────────────────────────────────────────── +│ [verbose] 9:41:33 | [diagnostic][CherryPick] Scope created: scope_1757054493089_7072 {type: Scope, name: scope_1757054493089_7072, description: scope created} +└─────────────────────────────────────────────────────────────── +``` + +In the log, you can see when scopes are created, which objects are registered and deleted, and catch errors and cycles in real time. + + +## Declarative Approach with Annotations + +In addition to fully programmatic module descriptions, CherryPick supports **declarative DI style through annotations**. +This allows minimizing manual code and automatically generating modules and mixins for automatic dependency injection. + +Example of a declarative module: + +```dart +@module() +abstract class AppModule extends Module { + @provide() + @singleton() + Api api() => Api(); + + @provide() + Repo repo(Api api) => Repo(api); +} +```` + +After code generation, you can automatically inject dependencies into widgets or services: + +```dart +@injectable() +class MyScreen extends StatelessWidget with _$MyScreen { + @inject() + late final Repo repo; + + MyScreen() { + _inject(this); + } +} +``` + +This way you can choose a convenient style: either **purely programmatic** or **declarative with annotations**. + + +## Who Might Find CherryPick Useful? + +* Projects where it's important to guarantee **no cycles in the dependency graph**; +* Teams that want to **minimize manual DI code** and use a declarative style with annotations; +* Applications that require **automatic resource cleanup** (sockets, controllers, streams). + +## Useful Links + +* 📦 Package: [pub.dev/packages/cherrypick](https://pub.dev/packages/cherrypick) +* 💻 Code: [github.com/pese-git/cherrypick](https://github.com/pese-git/cherrypick) +* 📖 Documentation: [cherrypick-di.netlify.app](https://cherrypick-di.netlify.app/) diff --git a/doc/news/cherrypick-3.x_ru.md b/doc/news/cherrypick-3.x_ru.md new file mode 100644 index 0000000..855a41b --- /dev/null +++ b/doc/news/cherrypick-3.x_ru.md @@ -0,0 +1,180 @@ +# Release - CherryPick 3.x + +> **CherryPick** — лёгкий и модульный DI-фреймворк для Dart и Flutter, который решает задачу через строгую типизацию, кодогенерацию и контроль за зависимостями. + +Недавно вышла версия **3.x**, где появились заметные улучшения. + + +## Основные изменения в 3.x + +* **O(1) разрешение зависимостей** — благодаря Map-индексации биндингов производительность не зависит от размера скоупа в DI графе. На больших проектах это даёт ощутимое ускорение. +* **Защита от циклических зависимостей** — проверка работает как внутри одного scope, так и во всей иерархии. При обнаружении цикла выбрасывается информативное исключение с цепочкой зависимостей. +* **Интеграция с Talker** — все события DI (регистрация, создание, удаление, ошибки) логируются и могут выводиться в консоль или UI. +* **Автоматическая очистка ресурсов** — объекты, реализующие `Disposable`, корректно освобождаются при закрытии scope. +* **Стабилизирована поддержка декларативного подхода** — аннотации и генерация кода теперь работают надёжнее и удобнее для использования в проектах. + + +## Пример с очисткой ресурсов + +```dart +class MyServiceWithSocket implements Disposable { + @override + Future dispose() async { + await socket.close(); + print('Socket закрыт!'); + } +} + +class AppModule extends Module { + @override + void builder(Scope currentScope) { + // singleton Api + bind() + .toProvide(() => MyServiceWithSocket()) + .singleton(); + } +} + +scope.installModules([AppModule()]); + +await CherryPick.closeRootScope(); // дождётся завершения async dispose +``` + +## Проверка циклических зависимостей + +Одна из новинок CherryPick 3.x — встроенная защита от циклов. +Это помогает на раннем этапе отлавливать ситуации, когда сервисы начинают зависеть друг от друга рекурсивно. + +### Как включить проверку + +Для проверки внутри одного scope: + +```dart +final scope = CherryPick.openRootScope(); +scope.enableCycleDetection(); +``` + +Для глобальной проверки во всей иерархии: + +```dart +CherryPick.enableGlobalCycleDetection(); +CherryPick.enableGlobalCrossScopeCycleDetection(); +final rootScope = CherryPick.openRootScope(); +``` + +### Как может возникнуть цикл + +Предположим, у нас есть два сервиса, которые зависят друг от друга: + +```dart +class UserService { + final OrderService orderService; + UserService(this.orderService); +} + +class OrderService { + final UserService userService; + OrderService(this.userService); +} +``` + +Если зарегистрировать их в одном scope: + +```dart +class AppModule extends Module { + @override + void builder(Scope currentScope) { + bind().toProvide(() => UserService(scope.resolve()); + bind().toProvide(() => OrderService(scope.resolve())); + } +} + +final scope = CherryPick.openRootScope() + ..enableCycleDetection() + ..installModules([AppModule()]); + +scope.resolve(); +``` + +То при попытке разрешить зависимость будет выброшено исключение: + +```bash +❌ Circular dependency detected for UserService +Dependency chain: UserService -> OrderService -> UserService +``` + +Таким образом, ошибка выявляется сразу, а не «где-то в runtime». + +## Интеграция с Talker + +CherryPick 3.x позволяет логировать все события DI через [Talker](https://pub.dev/packages/talker): регистрацию, создание объектов, удаление и ошибки. Это удобно для отладки и диагностики графа зависимостей. + +Пример подключения: + +```dart +final talker = Talker(); +final observer = TalkerCherryPickObserver(talker); +CherryPick.setGlobalObserver(observer); +``` + +После этого в консоли или UI будут отображаться события DI: + +```bash +┌─────────────────────────────────────────────────────────────── +│ [info] 9:41:33 | [scope opened][CherryPick] scope_1757054493089_7072 +└─────────────────────────────────────────────────────────────── +┌─────────────────────────────────────────────────────────────── +│ [verbose] 9:41:33 | [diagnostic][CherryPick] Scope created: scope_1757054493089_7072 {type: Scope, name: scope_1757054493089_7072, description: scope created} +└─────────────────────────────────────────────────────────────── +``` + +В логе можно увидеть, когда scope создаётся, какие объекты регистрируются и удаляются, а также отлавливать ошибки и циклы в реальном времени. + + +## Декларативный подход с аннотациями + +Помимо полностью программного описания модулей, CherryPick поддерживает **декларативный стиль DI через аннотации**. +Это позволяет минимизировать ручной код и автоматически генерировать модули и mixin для автоподстановки зависимостей. + +Пример декларативного модуля: + +```dart +@module() +abstract class AppModule extends Module { + @provide() + @singleton() + Api api() => Api(); + + @provide() + Repo repo(Api api) => Repo(api); +} +```` + +После генерации кода можно автоматически подтягивать зависимости в виджеты или сервисы: + +```dart +@injectable() +class MyScreen extends StatelessWidget with _$MyScreen { + @inject() + late final Repo repo; + + MyScreen() { + _inject(this); + } +} +``` + +Таким образом можно выбрать удобный стиль: либо **чисто программный**, либо **декларативный с аннотациями**. + + +## Кому может быть полезен CherryPick? + +* проектам, где важно гарантировать **отсутствие циклов в графе зависимостей**; +* командам, которые хотят **минимизировать ручной DI-код** и использовать декларативный стиль с аннотациями; +* приложениям, где требуется **автоматическое освобождение ресурсов** (сокеты, контроллеры, потоки). + +## Полезные ссылки + +* 📦 Пакет: [pub.dev/packages/cherrypick](https://pub.dev/packages/cherrypick) +* 💻 Код: [github.com/pese-git/cherrypick](https://github.com/pese-git/cherrypick) +* 📖 Документация: [cherrypick-di.netlify.app](https://cherrypick-di.netlify.app/) \ No newline at end of file