# Event sourcing for the JVM, on your terms

> Event sourcing utilities for the JVM, based on the CloudEvents specification.

Occurrent stores your events as CloudEvents and keeps Occurrent out of your domain model. Use one library or all of them, with or without Spring Boot.

- [Get started](/docs/overview/getting-started/)
- [Get started with Spring Boot](/docs/overview/getting-started-spring-boot/)
- [GitHub](https://github.com/johanhaleby/occurrent)

## Your domain is a function. Occurrent runs it.

Write your domain as a pure function from past events to new events. The application service loads the stream, runs your function, and appends the new events under optimistic concurrency.

* Java

  ```java
  // Your domain is a pure function from past events to new events,
  // with no dependency on Occurrent.
  List<GameEvent> handle(List<GameEvent> history, GameCommand command) { ... }


  // Occurrent's application service loads the stream, runs your function,
  // and appends the new events under optimistic concurrency.
  ApplicationService<GameEvent> applicationService = new GenericApplicationService<>(eventStore, cloudEventConverter);
  applicationService.execute(gameId, history -> handle(history, command));
  ```

* Kotlin

  ```kotlin
  // Your domain is a pure function from past events to new events,
  // with no dependency on Occurrent.
  fun handle(history: List<GameEvent>, command: GameCommand): List<GameEvent> { ... }


  // Occurrent's application service loads the stream, runs your function,
  // and appends the new events under optimistic concurrency.
  val applicationService = GenericApplicationService(eventStore, cloudEventConverter)
  applicationService.execute(gameId) { history -> handle(history, command) }
  ```

## Stream or DCB, the same event store and domain model

Both application services run against the same event store, a single store with stream and [DCB](/docs/layer-0/dcb-event-store/) capabilities. The `decide` and `evolve` are identical too. A DCB decider only adds the consistency boundary the command reads and the tags for the events it writes, so a rule can span more than one entity without a shared stream.

### Stream

* Java

  ```java
  // One boundary per stream
  var game = Decider.create(
      initialState, this::decide, this::evolve
  );
  deciderApplicationService.execute(gameId, command, game);
  ```

* Kotlin

  ```kotlin
  // One boundary per stream
  val game = Decider.create(
      initialState, ::decide, ::evolve
  )
  applicationService.execute(gameId, command, game)
  ```

### DCB

* Java

  ```java
  // A boundary you define per decision, across entities
  var game = DcbDecider.create(
      initialState, this::decide, this::evolve,
      this::boundaryFor, this::tagsFor
  );
  dcbDeciderApplicationService.execute(command, game);
  ```

* Kotlin

  ```kotlin
  // A boundary you define per decision, across entities
  val game = DcbDecider.create(
      initialState, ::decide, ::evolve,
      ::boundaryFor, ::tagsFor
  )
  dcbApplicationService.execute(command, game)
  ```

## Pick the layer you need

Occurrent is built in [layers](/docs/overview/layers/). Each one is built from the one below it, and you can use any of them directly, even different ones in the same application.

[Layer 0: CloudEvents](/docs/layer-0/)An event store and subscriptions that read and write CloudEvents. Full control over how events are stored, for infrastructure such as a forwarder or an audit log.

[Layer 1: Domain events](/docs/layer-1/)Your own event types, mapped to CloudEvents for you. The ApplicationService, queries, subscriptions, projections, snapshots and sagas. Most applications start here.

[Spring Boot](/docs/spring-boot/)A starter that wires the event store, subscriptions and converter, and annotations such as @Subscription, @Projection and @Saga that need no wiring code.

[Layer 3: Your own framework](/docs/layer-3/your-own-framework/)Build a framework shaped after your own business from the layer 1 parts. Occurrent gives you the bricks.

## What is Occurrent?

## Unintrusive

Design your domain model without *any* dependency on Occurrent or any other library. Express it with pure functions that return events, and use Occurrent to store them.

## Simple

Pick only the libraries you need. There’s no all or nothing.

## Control

You’re in control. Magic is kept to a minimum, and data is stored in a standard format, [CloudEvents](https://cloudevents.io/). You serialize and deserialize the event data yourself.

## Composable

Use the Occurrent components as building blocks for your own pipelines. They’re small, so you can rewrite one to fit your needs, or write your own domain specific layer on top of Occurrent.

## Library

Designed to be used as a library, not a framework, as far as possible.

## Pragmatic

Need consistent projections? Write projections and events in the same transaction, with tools you already know such as Spring’s `@Transactional`.

## Interoperable

[CloudEvents](https://cloudevents.io/) is a [CNCF](https://www.cncf.io/) specification for describing event data in a common way, across services and platforms.

## Data format

Events are stored as CloudEvents in the database too, so you can use the database to your advantage. Create your own indexes for fast and fully consistent queries on one event stream, or several.

## Works with what you already use

[MongoDB](/docs/stores/mongodb-event-stores/)[In-memory](/docs/stores/in-memory-event-store/)[Spring Data](/docs/stores/spring-modules/)[Spring Boot 4](/docs/spring-boot/)[Project Reactor](/docs/layer-0/reactive-subscriptions/)[RabbitMQ](/docs/stores/rabbitmq-and-kafka/)[Kafka](/docs/stores/rabbitmq-and-kafka/#kafka)[Java 21 and Kotlin](/docs/overview/getting-started/)

Occurrent 0.34.0 is the latest release. Read what’s new in the [news](/news/), and see [Upgrading](/docs/upgrading/) when you move from an older version.

If you like Occurrent, please consider starring us on GitHub:Like Occurrent? Star us on GitHub:[Star Occurrent on GitHub](https://ghbtns.com/github-btn.html?user=johanhaleby\&repo=occurrent\&type=star\&count=true\&size=large)