com.fraktalio.fmodel.domain.View.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of domain Show documentation
Show all versions of domain Show documentation
Fmodel provides just enough tactical Domain-Driven Design patterns, optimised for Event Sourcing and CQRS. The domain model library is fully isolated from the application layer and API-related concerns. It represents a pure declaration of the program logic.
/*
* Copyright (c) 2021 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.
*/
package com.fraktalio.fmodel.domain
/**
* An interface of the [_View]
*
* @param Si Input_State type
* @param So Output_State type
* @param E Event type
*
* @author Иван Дугалић / Ivan Dugalic / @idugalic
*/
interface I_View {
val evolve: (Si, E) -> So
val initialState: So
}
/**
* A convenient typealias for the [I_View] interface. It is specializing the three parameters [I_View] interface to only two parameters interface [IView].
*
* `Si=So -> S`
* `E -> E`
*
* @author Иван Дугалић / Ivan Dugalic / @idugalic
*/
typealias IView = I_View
/**
* [_View] is a datatype that represents the event handling algorithm,
* responsible for translating the events into denormalized state,
* which is more adequate for querying.
*
* It has three generic parameters [Si], [So], [E], representing the type of the values that [_View] may contain or use.
* [_View] can be specialized for any type of [Si], [So], [E] because these types does not affect its behavior.
* [_View] behaves the same for [E]=[Int] or [E]=YourCustomType, for example.
*
* [_View] is a pure domain component
*
* @param Si Input_State type
* @param So Output_State type
* @param E Event type
* @property evolve A pure function/lambda that takes input state of type [Si] and input event of type [E] as parameters, and returns the output/new state [So]
* @property initialState A starting point / An initial state of type [So]
* @constructor Creates [_View]
*
* @author Иван Дугалић / Ivan Dugalic / @idugalic
*/
data class _View(
override val evolve: (Si, E) -> So,
override val initialState: So,
) : I_View {
/**
* Left map on E/Event parameter - Contravariant
*
* @param En Event new
* @param f
*/
inline fun mapLeftOnEvent(crossinline f: (En) -> E): _View = _View(
evolve = { si, en -> this.evolve(si, f(en)) },
initialState = this.initialState
)
/**
* Dimap on S/State parameter - Contravariant on the Si (input State) - Covariant on the So (output State) = Profunctor
*
* @param Sin State input new
* @param Son State output new
* @param fl
* @param fr
*/
inline fun dimapOnState(
crossinline fl: (Sin) -> Si,
crossinline fr: (So) -> Son
): _View = _View(
evolve = { sin, e -> fr(this.evolve(fl(sin), e)) },
initialState = fr(this.initialState)
)
/**
* Left map on S/State parameter - Contravariant
*
* @param Sin State input new
* @param f
*/
inline fun mapLeftOnState(crossinline f: (Sin) -> Si): _View =
dimapOnState(f) { it }
/**
* Right map on S/State parameter - Covariant
*
* @param Son State output new
* @param f
*/
inline fun mapOnState(crossinline f: (So) -> Son): _View =
dimapOnState({ it }, f)
}
// ### Extension functions ###
// We could have included these functions inside the _View data type if we had changed _View’s type arguments to be invariant but this would cascade poorly for inference.
// If we move it to an extension then E, Si, ... are no longer coming from the instance but from the environment in invariant position which is acceptable.
/**
* Apply on S/State parameter - Applicative
*
* @param Si State input type
* @param So State output type
* @param E Event type
* @param Son State output new type
* @param ff
*/
fun _View.applyOnState(ff: _View Son, E>): _View = _View(
evolve = { si, e -> ff.evolve(si, e)(this.evolve(si, e)) },
initialState = ff.initialState(this.initialState)
)
/**
* Product on S/State parameter - Applicative
*
* @param Si State input type
* @param So State output type
* @param E Event type
* @param Son State output new type
* @param fb
*/
fun _View.productOnState(fb: _View): _View, E> =
applyOnState(fb.mapOnState { b: Son -> { a: So -> Pair(a, b) } })
/**
* Combines [_View]s into one bigger [_View]
*
* Possible to use when [E] and [E2] have common superclass [E_SUPER]
*
* @param Si State input of the first View
* @param So State output of the first View
* @param E Event of the first View
* @param Si2 State input of the second View
* @param So2 State output of the second View
* @param E2 Event of the second View
* @param E_SUPER super type for [E] and [E2]
* @param y second View
* @return new View of type [_View]<[Pair]<[Si], [Si2]>, [Pair]<[So], [So2]>, [E_SUPER]>
*/
inline fun _View.combine(
y: _View
): _View, Pair, E_SUPER> {
val viewX = this
.mapLeftOnEvent { it as? E }
.mapLeftOnState> { pair -> pair.first }
val viewY = y
.mapLeftOnEvent { it as? E2 }
.mapLeftOnState> { pair -> pair.second }
return viewX.productOnState(viewY)
}
/**
* A typealias for [_View], specializing the [_View] to two generic parameters: S and E, where Si=S, So=S, E=E
*/
typealias View = _View
© 2015 - 2024 Weber Informatics LLC | Privacy Policy