Fixers F2
F2 is a framework built using the Kotlin programming language that is built on top of Spring Cloud Function for the server side and ktor for multiplatform client. It aims to make it easier to develop Command and Query Responsibility Segregation (CQRS) based applications.
Overview
The goal of F2 is to help developers build event-driven, scalable, and resilient systems that are easy to understand and maintain. It provides tools and abstractions to handle the different parts of a CQRS-based architecture.
The Command and Query Responsibility Segregation (CQRS) architecture is a pattern that separates the responsibilities of handling commands, which change the state of an application, and queries, which retrieve information from the application.
HTTP is a widely-used protocol for sending and receiving information over the internet, and is a popular choice for building RESTful web services. By supporting HTTP, F2 and its client can easily integrate with a wide range of other technologies and services that use HTTP for communication.
Requirements
- JDK 17+
- Docker (for Storybook/documentation)
- Node.js & Yarn (managed by Gradle for Storybook)
Project Structure
The project is organized into several modules:
Using F2 in your project
Setup
Add the F2 version catalog and BOM in your settings.gradle.kts:
dependencyResolutionManagement {
versionCatalogs {
create("f2") {
from("io.komune.f2:f2-gradle-catalog:${VERSION}")
}
}
}
Apply the BOM to all subprojects in your root build.gradle.kts:
subprojects {
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
dependencies {
"api"(platform(f2.bom))
}
}
pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") {
dependencies {
"commonMainApi"(platform(f2.bom))
}
}
}
Using plugins
The version catalog provides plugin aliases with compatible versions:
plugins {
alias(f2.plugins.fixers.gradle.config)
alias(f2.plugins.fixers.gradle.check)
alias(f2.plugins.kotlin.spring) apply false
alias(f2.plugins.spring.boot) apply false
}
Adding dependencies
With the BOM applied, F2 dependencies require no explicit version:
dependencies {
implementation(f2.dsl.function)
implementation(f2.dsl.cqrs)
implementation(f2.spring.boot.starter.function.http.mvc)
}
Third-party dependencies managed by the BOM (Spring Boot, Ktor, coroutines, etc.) also resolve their versions automatically.
Development
Setup
Clone the repository:
git clone https://github.com/komune-io/fixers-f2.git
cd fixers-f2
Scripts
The project uses make and gradle for build operations.
Note: The Makefile in the root currently points to libs.mk and docs.mk. Use make -f make_libs.mk <target> or make -f make_docs.mk <target> if the default make command fails.
Environment Variables
| Variable | Description |
|---|
VERSION | Project version (default: experimental-SNAPSHOT). |
|
License
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
Usage Guide
Dependencies
Spring Cloud Function
Spring Cloud Function provides a functional programming model for building cloud-native applications,
enabling you to write simple, single-purpose functions that can be easily composed and deployed in a cloud environment.
It allows you to take advantage of the many features of the Spring framework, such as data access, transaction management,
security, and more, while still enjoying the scalability and cost-effectiveness of a serverless architecture.
It supports multiple communication protocols, including HTTP and gRPC, and the choice of protocol
will depend on the specific requirements of your application and the type of function being written.
Ktor
F2 provides a multiplatform client using the Ktor client for HTTP.
The Ktor client is a high-performance HTTP client for the Kotlin language that makes it easy to perform HTTP requests
from your Kotlin applications. It supports both synchronous and asynchronous programming,
and provides a wide range of features for working with HTTP, including connection pooling,
request and response streaming, and automatic decompression of response bodies.
Dsl
Lambda
In Java, a Function, Supplier, and Consumer are functional interfaces that are part of the Java 8 functional programming API.
They are used to represent functions that take one or more arguments, produce a result, or consume a value, respectively.
These functional interfaces can be used to implement functional programming concepts such as higher-order functions and lambda expressions, making it easier to write concise, readable, and maintainable code. For example, you could use a Function to map a list of integers to a list of strings, or a Consumer to iterate over a list of values and perform an action on each one. By using these functional interfaces, you can write code that is more functional and less imperative, resulting in a more declarative and maintainable codebase.
Dependency
implementation("io.komune.f2:f2-dsl-function:${Versions.f2}")
<dependency>
<groupId>io.komune.f2</groupId>
<artifactId>f2-dsl-function</artifactId>
<version>${Versions.f2}</version>
</dependency>
F2Supplier
A F2Supplier does not take any input and produces a result.
For example, you might write a Supplier that returns a random number.
The get method of a Supplier returns the result.
actual fun interface F2Supplier<R> : suspend () -> Flow<R> {
override suspend operator fun invoke(): Flow<R>
}
actual interface F2Supplier<R> {
fun invoke(): Promise<Array<R>>
}
F2Function
A F2Function takes one or more inputs and produces a result.
For example, you might write a Function that takes an Integer and returns a String.
The apply method of a Function takes the input and returns the result.
actual fun interface F2Function<T, R>: suspend (Flow<T>) -> Flow<R> {
override suspend operator fun invoke(msgs: Flow<T>): Flow<R>
}
actual interface F2Function<T, R> {
fun invoke(cmd: T): Promise<R>
}
F2Consumer
A F2Consumer takes a value as input and does not produce a result.
For example, you might write a Consumer that prints a message to the console.
The accept method of a Consumer takes the input and performs an action,
but does not return a value.
actual fun interface F2Consumer<T>: suspend (Flow<T>) -> Unit {
override suspend operator fun invoke(msg: Flow<T>)
}
actual interface F2Consumer<T> {
fun invoke(cmd: T): Promise<Unit>
}
CQRS
Command Query Responsibility Segregation
is a pattern that performs segregate operations that read data from operations that update data by using separate interfaces.
The package f2-dsl-cqrs provide kotlin multplatform interfaces for f2.dsl.cqrs.Event, f2.dsl.cqrs.Command, f2.dsl.cqrs.Query
It also interface for querying a page o result f2.dsl.cqrs.PageRequest and Pagef2.dsl.cqrs
Dependency
implementation("io.komune.f2:f2-dsl-cqrs:${Versions.f2}")
<dependency>
<groupId>io.komune.f2</groupId>
<artifactId>f2-dsl-cqrs</artifactId>
<version>${Versions.f2}</version>
</dependency>
Message
interface Message
interface Event: Message
interface Command: Message
interface Query : Message
Pagination
Page
interface PageDTO<out OBJECT> {
val total: Int
val items: List<OBJECT>
}
class Page<out OBJECT>(
override val total: Int,
override val items: List<OBJECT>,
) : PageDTO<OBJECT>
Page Query Function
Page Extension
fun <OBJECT> OffsetPaginationDTO.: PageQueryResultDTO<OBJECT>
: PageDTO<R>
: Page<R>
inline fun <T, reified R: Any> PageDTO<T>.mapNotNull(transform: (T) -> R?): PageDTO<R>
inline fun OffsetPagination?.toPageRequest(): PageRequest
Function Extension
Spring
F2 provide basic configuration for spring cloud function
Dependency
Dependency to implement function
implementation("io.komune.f2:f2-spring-boot-starter-function:${Versions.f2}")
<dependency>
<groupId>io.komune.f2</groupId>
<artifactId>f2-spring-boot-starter-function</artifactId>
<version>${Versions.f2}</version>
</dependency>
Dependency to bind function to http protocol
implementation("io.komune.f2:f2-spring-boot-starter-function-http:${Versions.f2}")
<dependency>
<groupId>io.komune.f2</groupId>
<artifactId>f2-spring-boot-starter-function-http</artifactId>
<version>${Versions.f2}</version>
</dependency>
Example
- Function Client implementation
Openapi
Integration with Springdoc to generate open api documentation.
<dependency>
<groupId>io.komune.f2</groupId>
<artifactId>f2-spring-boot-openapi</artifactId>
<version>${Versions.f2}</version>
</dependency>
implementation("io.komune.f2:f2-spring-boot-openapi:${Versions.f2}")
- Configuration can be done as describe in springdoc documentation
https://springdoc.org/#features
Serializer
Three serializer can be used via the spring parameter spring.cloud.function.preferred-json-mapper
- jackson
- gson
- kSerialization