Predable is state management framework without framework code.
MIT License
Predable is type only state management framework.
You can use the framework without framework.
This concept is similar with Almin.js and Predable less framework codes.
See Implementations
Domain has logic. It is just pure JavaScript.
export class CounterId {
type = "Domain";
constructor(public readonly value: string) {}
}
export type CounterProps<Id extends CounterId> = {
id: Id; // nominal id
// other values
data: any;
};
// @Cost: low
export class Domain<Id extends CounterId> {
id: Id;
// other values
data: any;
constructor(props: CounterProps<Id>) {
this.id = props.id;
// other values
this.data = props.data;
}
// domain logic
eatData(data: any) {
return new Domain({
...this,
data,
});
}
}
A Repository store a domain instance eventually. Repositories use Key-Value map object in memory or Database.
import { Domain, CounterId } from "./Domain";
export class DomainMap<K extends CounterId, V extends Domain<K>> extends Map<K, V> {
private __last__value__: undefined | V;
read(): undefined | V {
return this.__last__value__;
}
write(entity: V): this {
return this.set(entity.id, entity);
}
set(key: K, value: V): this {
super.set(key, value);
this.__last__value__ = value;
return this;
}
}
export const createCounterRepository = <T extends Domain<CounterId>>() => {
return new DomainMap<T["id"], T>();
};
export const domainRepository = createCounterRepository();
UseCase is a procedure that use Domain. Read a data from repository, Domain work something, and store the domain to repostiory.
import { domainRepository } from "./Repository";
import { Domain, CounterId } from "./Domain";
export class Payload {}
export const createAction = (infra = { domainRepository }) => {
return {
execute(payload: Payload) {
const domain =
infra.domainRepository.read() ??
new Domain({
id: new CounterId("unique-id"),
data: {},
});
// Domain works
const newDomain = domain.eatData(payload);
// Domain works
infra.domainRepository.write(newDomain);
},
};
};
State represent a data for UI-specific. Domain represent a data for Application.
In sometimes, State and Domain is same value, but a state is beging mapped value from a domain.
import { domainRepository } from "./Repository";
export const createCounterStore = (infra = { domainRepository }) => {
const get = () => {
return { domain: infra.domainRepository.read() };
};
const select = <R>(selector: ({ domain }: ReturnType<typeof get>) => R) => {
return selector(get());
};
return {
// @Cost: middle
get,
// @Cost: middle
select,
};
};
// @Cost: low
export const store = createCounterStore({ domainRepository });
// State
const initialData = {
initial: true,
};
// @Cost: high
export const getState = () => {
return {
data: store.select(({ domain }) => domain?.data ?? initialData),
};
};
There no framework code.
These code can be used as follows.
import { createAction } from "./UseCase";
import { getState } from "./State";
console.log("initial state", getState()); // { data: { initial: true } }
createAction().execute({
value: "new value",
});
console.log("updated state", getState()); // { data: { value: 'new value' } }
You can opt-in Predable framework by following changes.
UseCase:
+ import { wrapPredableUseCase } from "../frameworks/WrapPredableUseCase";
- export const createAction = (infra = { domainRepository }) => {
+ export const createAction = wrapPredableUseCase(function DebuggableUseCaseName(infra = { domainRepository }) {
State:
+ import { wrapPredableStore } from "../frameworks/PredableState";
- export const createCounterStore = (infra = { domainRepository }) => {
+ export const createCounterStore = wrapPredableStore(function DebuggableSelectorName(infra = { domainRepository }) {
This does not change interface, It just wraps your implementations!
Predable framework provide these feature
See Releases page.
Install devDependencies and Run npm test
:
npm test
Pull requests and stars are always welcome.
For bugs and feature requests, please create an issue.
git checkout -b my-new-feature
git commit -am 'Add some feature'
git push origin my-new-feature
MIT azu