Dependencies Injection
If you are familiar with Angular or NestJS, you may already be familiar with Dependencies Injection.
Module
In ayanami world, every Module is Injectable. So you can easily combine modules by Inject one to the others.
@Module('A')class ModuleA extends Ayanami<AState> {defaultState = {}}@Module('B')class ModuleB extends Ayanami<BState> {defaultState = {}constructor(private readonly moduleA: ModuleA) {super()}}
Access AppState
from the other Module
Unlike Redux, Ayanami does not have Global Store object to store the whole state in application. But with Dependencies Injection, we still can access state from the other parts in our application:
@Module('A')class ModuleA extends Ayanami<AState> {defaultState = {}}@Module('B')class ModuleB extends Ayanami<BState> {defaultState = {}constructor(private readonly moduleA: ModuleA) {super()}@Effect()addAndSync(payload$: Observable<number>) {return payload$.pipe(withLatestFrom(this.moduleA.state$),map(([payload, stateA]) => {...}))}}
Trigger actions of the other modules
Just be similar with Emit actions in Effect using ActionsCreator, we can create Action
using ActionsCreator in the other Module class
.
@Module('A')class ModuleA extends Ayanami<AState> {defaultState = {}@ImmerReducer()set(state: Draft<AState>, payload: string) {state.syncFromB = payload}}@Module('B')class ModuleB extends Ayanami<BState> {defaultState = {}constructor(private readonly moduleA: ModuleA) {super()}@Effect()addAndSync(payload$: Observable<number>) {return payload$.pipe(withLatestFrom(this.moduleA.state$),map(([payload, stateA]) => {return this.moduleA.getActions().set(`${stateA.syncFromB}${payload}`)}))}}
Service
If you want create a pure Service which seperated from Module class
, you can just decorate it by Injectable
decorator from ayanami.
@Module('Simple')class SimpleModule extends Ayanami<SimpleState> {defaultState = {}constructor(private readonly httpClient: HttpClient) {super()}@Effect()create(payload$: Observable<CreateEntityPayload>) {return payload$.pipe(withLatestFrom(this.state$),exhaustMap(([payload, state]) => {return this.httpClient.post(`/resources/${state.id}`, {body: payload,})}))}}
import { Injectable } from 'ayanami'import { Observable } from 'rxjs'@Injectable()export class HttpClient {constructor(private readonly tracer: Tracer) {}get () {}post<T>(config: Config = {}): Observable<T> {return this.send<T>({...config,method: 'POST',})}delete() {}put() {}private send<T>(config: Config): Observable<T> {return this.tracer.send(config)}}
import { Injectable } from 'ayanami'import { Observable } from 'rxjs'export type TraceId = string & {readonly traceIdTag: unique symbol}@Injectable()export class Tracer {send<T>(config: Config): Observable<T> {const traceId = this.generateTraceId()this.traceStart(traceId)return new Observable<T>((observer) => {const { config, abortController } = this.convertConfig(config, traceId)fetch(config).then((res) => {this.traceEnd(traceId, res)return res.json()}).then((data) => {observer.next(data)observer.complete()}).catch((e) => {observer.error(e)})return () => {abortController.abort()}})}private convertConfig(config: Config, traceId: TraceId): { config: FetchInit, abortController: AbortController } {}private traceStart(traceId: TraceId) {}private traceEnd(traceId: TraceId, res: Response) {}private generateTraceId(): TraceId {}}