Example 001: Simple signal
Ниже представлен пример того, как бизнес-логика (сервис) описывается независимо от фреймворка, а React-компонент выступает лишь в роли "потребителя" данных и подписывается на изменения через метод engine.use():
import { AbstractService } from '@pravosleva/reactive-engine'
import { ReactiveEngine } from '@pravosleva/reactive-engine/react'
class Logic extends AbstractService {
public counter = this.engine.signal<number>(0, 'example-001:signal:counter');
public inc() {
this.counter.value += 1
}
}
const engine = new ReactiveEngine()
export const Example001 = () => {
const logic = engine.inject(Logic)
const counter = engine.use(logic.counter)
return (
<div>
<div>Signal example</div>
<code>{counter}</code>
<div>
<button
onClick={() => logic.inc()}
>+ INC</button>
</div>
</div>
)
}Использование Core-сервисов в React-окружении (Полиморфизм и DI)
Архитектура движка построена на принципах инверсии управления (IoC) и полиморфизма, что позволяет переиспользовать бизнес-логику между различными окружениями без изменений в коде самих сервисов. Класс ReactiveEngine из пакета @pravosleva/reactive-engine/react является прямым наследником базового класса ядра. Благодаря структурной типизации в TypeScript и механизмам наследования в JavaScript, расширенный инстанс движка полностью совместим с базовыми сервисами.
Когда вы импортируете AbstractService из ядра и инициализируете React-версию движка, метод .inject(Logic) корректно обрабатывает зависимость. В процессе регистрации DI-контейнер передает сервису ссылку на текущий запущенный экземпляр движка (this.engine). Таким образом, базовый сервис внутри себя незаметно для собственной логики начинает работать с расширенным React-движком. Это обеспечивает строгую изоляцию слоев: ваши сервисы остаются «чистыми» от UI-фреймворков и не содержат лишнего рантайма, но при этом без проблем реактивно связываются с компонентами через метод engine.use() на стороне React-слоя.
Как это устроено под капотом (Under the Hood)
Архитектура движка построена на принципах инверсии управления (IoC) и полиморфизма, что позволяет полностью отделить бизнес-логику приложения от UI-слоя (React) и переиспользовать сервисы в любом другом JavaScript-окружении.
Архитектура путей и полиморфизм
Класс ReactiveEngine из пакета @pravosleva/reactive-engine/react является прямым наследником базового класса ядра. Благодаря структурной типизации в TypeScript и механизмам наследования в JavaScript, расширенный инстанс движка полностью совместим с базовыми сервисами.
Когда вы импортируете AbstractService из ядра и инициализируете React-версию движка, метод .inject(Logic) корректно обрабатывает зависимость. В процессе регистрации DI-контейнер передает сервису ссылку на текущий запущенный экземпляр движка (this.engine). Таким образом, базовый сервис внутри себя незаметно для собственной логики начинает работать с расширенным React-движком.
Разделение ответственности и ограничения типов
Такой подход накладывает строгие и полезные архитектурные ограничения на уровне компиляции TypeScript:
- Внутри Core-сервисов (
Logic): Свойствоthis.engineимеет тип базового класса ядра. Вы защищены от случайного вызова UI-специфичных методов (например, вы не сможете вызватьthis.engine.use()внутри сервиса). Логика остается абсолютно чистой. - Внутри React-компонентов (
Example001): Переменнаяengineимеет тип расширенного класса из/react. Вам доступны как методы управления контейнером (.inject()), так и реактивные хуки для UI (.use()), которые под капотом безопасно связываются с жизненным циклом компонентов React.