@fraktalio/fmodel-ts
Version:
Functional domain modeling with TypeScript. Optimized for event sourcing and CQRS
330 lines • 20.1 kB
JavaScript
;
/*
* Copyright 2023 Fraktalio D.O.O. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "
* AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Decider = void 0;
/* eslint-disable functional/no-mixed-types, functional/no-classes */
/**
* `_Decider` is a datatype that represents the main decision-making algorithm.
* It has five generic parameters `C`, `Si`, `So`, `Ei`, `Eo` , representing the type of the values that `_Decider` may contain or use.
* `_Decider` can be specialized for any type `C` or `Si` or `So` or `Ei` or `Eo` because these types does not affect its behavior.
* `_Decider` behaves the same for `C`=`Int` or `C`=`YourCustomType`, for example.
*
* `_Decider` is a pure `internal` domain component.
* It is not exported, and rather used to differentiate covariant and contravariant parameters, so we can map over them correctly.
*
* _Decider` is used to model simpler (three parameter) public `Decider` component that has more practical usage.
* `Decider` has three type parameters: `C`, `S`, `E`, in where `E` = `Ei` = `Eo` and `S` = `Si` = `So`
*
* @typeParam C - Command
* @typeParam Si - Input_State type
* @typeParam So - Output_State type
* @typeParam Ei - Input_Event type
* @typeParam Eo - Output_Event type
*
* @author Иван Дугалић / Ivan Dugalic / @idugalic
*/
class _Decider {
constructor(decide, evolve, initialState) {
this.decide = decide;
this.evolve = evolve;
this.initialState = initialState;
}
/**
* Contra (Left) map on C/Command parameter - Contravariant
*
* @typeParam Cn - New Command
*/
mapContraOnCommand(f) {
return new _Decider((cn, s) => this.decide(f(cn), s), (s, e) => this.evolve(s, e), this.initialState);
}
/**
* Dimap on E/Event parameter - Contravariant on input event and Covariant on output event = Profunctor
*
* @typeParam Ein - New input Event
* @typeParam Eon - New output Event
*/
dimapOnEvent(fl, fr) {
return new _Decider((c, s) => this.decide(c, s).map(fr), (s, ein) => this.evolve(s, fl(ein)), this.initialState);
}
/**
* Dimap on S/State parameter - Contravariant on input state (Si) and Covariant on output state (So) = Profunctor
*
* @typeParam Sin - New input State
* @typeParam Son - New output State
*/
dimapOnState(fl, fr) {
return new _Decider((c, sin) => this.decide(c, fl(sin)), (sin, e) => fr(this.evolve(fl(sin), e)), fr(this.initialState));
}
/**
* Contra (Left) map on S/State parameter - Contravariant
*
* @typeParam Sin - New input State
*/
mapContraOnState(f) {
return this.dimapOnState(f, identity);
}
/**
* (Right) map on S/State parameter - Covariant
*
* @typeParam Son - New output State
*/
mapOnState(f) {
return this.dimapOnState(identity, f);
}
/**
* Right apply on S/State parameter - Applicative
*
* @typeParam Son - New output State
*/
applyOnState(ff) {
return new _Decider((c, s) => ff.decide(c, s).concat(this.decide(c, s)), (s, e) => ff.evolve(s, e)(this.evolve(s, e)), ff.initialState(this.initialState));
}
/**
* Right product on S/State parameter - Applicative
* Combines state via intersection (So & Son)
*
* @typeParam Son - New output State
*/
productOnState(fb) {
return this.applyOnState(fb.mapOnState((son) => (so) => {
return Object.assign({}, so, son);
}));
}
/**
* Right product on S/State parameter - Applicative
* Combines state via tuple [So, Son]
*
* @typeParam Son - New output State
*/
productViaTuplesOnState(fb) {
return this.applyOnState(fb.mapOnState((b) => (a) => [a, b]));
}
/**
* Combine Deciders into one big Decider
*
* The States/S are combined via `intersection`
*/
combine(y) {
const deciderX = this.mapContraOnCommand((c) => c)
.mapContraOnState((sin) => sin)
.dimapOnEvent((ein) => ein, (eo) => eo);
const deciderY = y
.mapContraOnCommand((c) => c)
.mapContraOnState((sin) => sin)
.dimapOnEvent((ein) => ein, (eo2) => eo2);
return deciderX.productOnState(deciderY);
}
/**
* Combine Deciders into one big Decider
*
* The States/S are combined via `tuples`
*/
combineViaTuples(y) {
const deciderX = this.mapContraOnCommand((c) => c)
.mapContraOnState((sin) => sin[0])
.dimapOnEvent((ein) => ein, (eo) => eo);
const deciderY = y
.mapContraOnCommand((c) => c)
.mapContraOnState((sin) => sin[1])
.dimapOnEvent((ein) => ein, (eo2) => eo2);
return deciderX.productViaTuplesOnState(deciderY);
}
}
/**
* `Decider` is a datatype that represents the main decision-making algorithm.
* It has three generic parameters `C`, `S`, `E` , representing the type of the values that `Decider` may contain or use.
* `Decider` can be specialized for any type `C` or `S` or `E` because these types does not affect its behavior.
* `Decider` behaves the same for `C`=`Int` or `C`=`YourCustomType`, for example.
*
* `Decider` is a pure domain component.
*
* @typeParam C - Command type
* @typeParam S - State type
* @typeParam E - Event type
* @param decide - A function/lambda that takes command of type `C` and input state of type `S` as parameters, and returns/emits the list of output events `E[]`>
* @param evolve - A function/lambda that takes input state of type `S` and input event of type `E` as parameters, and returns the output/new state `S`
* @param initialState - A starting point / An initial state of type `S`
*
* @author Иван Дугалић / Ivan Dugalic / @idugalic
*
* @example
* ```typescript
* export const orderDecider: Decider<OrderCommand, Order | null, OrderEvent> =
* new Decider<OrderCommand, Order | null, OrderEvent>(
* (command, currentState) => {
* switch (command.kind) {
* case "CreateOrderCommand":
* return currentState == null
* ? [
* {
* version: 1,
* decider: "Order",
* kind: "OrderCreatedEvent",
* id: command.id,
* restaurantId: command.restaurantId,
* menuItems: command.menuItems,
* final: false,
* },
* ]
* : [
* {
* version: 1,
* decider: "Order",
* kind: "OrderNotCreatedEvent",
* id: command.id,
* restaurantId: command.restaurantId,
* menuItems: command.menuItems,
* final: false,
* reason: "Order already exist!",
* },
* ];
* case "MarkOrderAsPreparedCommand":
* return currentState !== null
* ? [
* {
* version: 1,
* decider: "Order",
* kind: "OrderPreparedEvent",
* id: currentState.orderId,
* final: false,
* },
* ]
* : [
* {
* version: 1,
* decider: "Order",
* kind: "OrderNotPreparedEvent",
* id: command.id,
* reason: "Order does not exist!",
* final: false,
* },
* ];
* default:
* // Exhaustive matching of the command type
* const _: never = command;
* return [];
* }
* },
* (currentState, event) => {
* switch (event.kind) {
* case "OrderCreatedEvent":
* return {
* orderId: event.id,
* restaurantId: event.restaurantId,
* menuItems: event.menuItems,
* status: "CREATED",
* };
* case "OrderNotCreatedEvent":
* return currentState;
* case "OrderPreparedEvent":
* return currentState !== null
* ? {
* orderId: currentState.orderId,
* restaurantId: currentState.restaurantId,
* menuItems: currentState.menuItems,
* status: "PREPARED",
* }
* : currentState;
* case "OrderNotPreparedEvent":
* return currentState;
* default:
* // Exhaustive matching of the event type
* const _: never = event;
* return currentState;
* }
* },
* null,
* );
* ```
*/
class Decider {
constructor(decide, evolve, initialState) {
this.decide = decide;
this.evolve = evolve;
this.initialState = initialState;
}
/**
* Contra (Left) map on C/Command parameter - Contravariant
*
* @typeParam Cn - New Command
*/
mapContraOnCommand(f) {
return asDecider(new _Decider(this.decide, this.evolve, this.initialState).mapContraOnCommand(f));
}
/**
* Dimap on E/Event parameter - Profunctor
*
* @typeParam En - New Event
*/
dimapOnEvent(fl, fr) {
return asDecider(new _Decider(this.decide, this.evolve, this.initialState).dimapOnEvent(fl, fr));
}
/**
* Dimap on S/State parameter - Profunctor
*
* @typeParam Sn - New State
*/
dimapOnState(fl, fr) {
return asDecider(new _Decider(this.decide, this.evolve, this.initialState).dimapOnState(fl, fr));
}
/**
* Combine multiple Deciders into one Decider - Monoid
*
* State/S is combined via `intersection / (S & S2)`. It only makes sense if S ans S2 are objects, not primitives.
* Check alternative method `combineViaTuples`
*
* Intersections provide more flexibility and can handle more complex scenarios,
* while tuples are more straightforward and may be more suitable for simple cases.
*
* Flexibility: If you anticipate needing to access individual components of the combined state separately, using tuples might be more appropriate, as it allows you to maintain separate types for each component. However, if you primarily need to treat the combined state as a single entity with all properties accessible at once, intersections might be more suitable.
*
* Readability: Consider which approach makes your code more readable and understandable to other developers who may be working with your codebase. Choose the approach that best communicates your intentions and the structure of your data.
*
* Compatibility: Consider the compatibility of your chosen approach with other libraries, frameworks, or tools you're using in your TypeScript project. Some libraries or tools might work better with one approach over the other.
*/
combine(decider2) {
return asDecider(new _Decider(this.decide, this.evolve, this.initialState).combine(new _Decider(decider2.decide, decider2.evolve, decider2.initialState)));
}
/**
* Combine multiple Deciders into one Decider - Monoid
*
* State/S is combined via `tuples / [S, S2]`. Check alternative method `combine`
*
* Tuples are more straightforward and may be more suitable for simple cases,
* while intersections provide more flexibility and can handle more complex scenarios.
*
* 1. Flexibility: If you anticipate needing to access individual components of the combined state separately, using tuples might be more appropriate, as it allows you to maintain separate types for each component. However, if you primarily need to treat the combined state as a single entity with all properties accessible at once, intersections might be more suitable.
*
* 2. Readability: Consider which approach makes your code more readable and understandable to other developers who may be working with your codebase. Choose the approach that best communicates your intentions and the structure of your data.
*
* 3. Compatibility: Consider the compatibility of your chosen approach with other libraries, frameworks, or tools you're using in your TypeScript project. Some libraries or tools might work better with one approach over the other.
*/
combineViaTuples(decider2) {
return asDecider(new _Decider(this.decide, this.evolve, this.initialState).combineViaTuples(new _Decider(decider2.decide, decider2.evolve, decider2.initialState)));
}
}
exports.Decider = Decider;
/**
* Identity function
*/
const identity = (t) => t;
/**
* Creates `Decider` from internal `_Decider`
*
* @param decider
*/
function asDecider(decider) {
return new Decider(decider.decide, decider.evolve, decider.initialState);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVjaWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvZG9tYWluL2RlY2lkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7OztHQVdHOzs7QUFFSCxxRUFBcUU7QUFFckU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFDSCxNQUFNLFFBQVE7SUFDWixZQUNXLE1BQXNDLEVBQ3RDLE1BQTRCLEVBQzVCLFlBQWdCO1FBRmhCLFdBQU0sR0FBTixNQUFNLENBQWdDO1FBQ3RDLFdBQU0sR0FBTixNQUFNLENBQXNCO1FBQzVCLGlCQUFZLEdBQVosWUFBWSxDQUFJO0lBQ3hCLENBQUM7SUFFSjs7OztPQUlHO0lBQ0gsa0JBQWtCLENBQUssQ0FBZ0I7UUFDckMsT0FBTyxJQUFJLFFBQVEsQ0FDakIsQ0FBQyxFQUFNLEVBQUUsQ0FBSyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsRUFDeEMsQ0FBQyxDQUFLLEVBQUUsQ0FBSyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFDbkMsSUFBSSxDQUFDLFlBQVksQ0FDbEIsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILFlBQVksQ0FDVixFQUFvQixFQUNwQixFQUFtQjtRQUVuQixPQUFPLElBQUksUUFBUSxDQUNqQixDQUFDLENBQUksRUFBRSxDQUFLLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFDMUMsQ0FBQyxDQUFLLEVBQUUsR0FBUSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsRUFDNUMsSUFBSSxDQUFDLFlB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