Fream

New Naviexpert app for iOS

12 November 2015 | Marcin Szajda

Event sourcing is an approach in which changes to application state are persistently stored as a stream of immutable events. This is in contrast to typical CRUD applications, where only the „current” state is stored and mutated when commands come into the system. There’s a lot of great introductory material on event sourcing available (see for example Event Store docs, Martin Fowler’s article or the CQRS FAQ), so I’ll try not to repeat these. The blog is inspired most directly by Greg’s Young „Functional Data Storage” talk (you need a Parleys subscription, though).

The best thing about even sourcing is that you don’t lose information, and after all, information is what IT is all about (well, you don’t lose as much information as in CRUD systems). Events provide a great audit log „for free” (which is something I worked on quite a bit, see Hibernate Envers), but also allow to capture and re-create the application state at any point in time, or provide new views of existing information with ease. Event Sourcing is quite often described together with Command-Query Responsibility Segregation (CQRS), and not without a cause, when using ES some form of CQRS emerges almost naturally. We’ll also see „commands” and „read models” later in the blog, however we won’t be using CQRS in its most „radical” form.

6

Event Sourcing is often presented in asynchronous architectures, which take advantage of message queues, multiple databases and where the read model is eventually consistent with the user commands. However, you can of course use Event Sourcing in much simpler setups as well, with a single, relational data store, using transactions to maintain (at least some) consistency. A lot of „business”/back-office applications, which are not distributed and run on a single node, can still gain a lot of benefits from event-sourced architectures. Let’s see one possible approach, first described in general, language-agnostic terms and then on a concrete example implementation using Scala+Slick.

  1. Lorem ipsum 1
  2. Suspendisse consectetur ac nisl quisque condimentum platea tincidunt vivamus suspendisse sociosqu
  3. Lorem ipsum 1

General Approach

As our main „source of truth”, we’ll be storing all events that take place in the system in an events table. Each event is associated wit…

  • unique event id
  • event type: typically the name of the class containing event data
  • aggregate type: name of the aggregate’s class
  • aggregate new: flag indicating if the event concerns a new or existing aggregate
  • created: timestamp at which the event happened
  • user id: optional id of the user or other actor which triggered the event
11

EVENTS

The central type is Event defined in model.scala, which can be constructed incrementally using a DSL. The data associated with each event is stored in plain-old-case classes; the event type is the name of the case class, and the data is serialized to json. For example, creating a new „user registered” event and marking that it’s a new aggregate in the event meta-data looks like this:

type EventListener[T] = Event[T] => DBRead[List[PartialEvent[_, _]]]
type ModelUpdate[T] = Event[T] => DBReadWrite

object UserRegistered 
{  implicit val afe = AggregateForEvent[UserRegistered, User]}

Note that this is not yet a fully constructed Event object, some fields like the event id, aggregate id or transaction id are filled automatically later.The aggregate for an event is determined via an implicit AggregateForEvent[EventType, AggregateType value, for example defined in the companion object for the event data class, thanks to which the implicit will be looked up automatically:

Have a Project in Mind?
Let’s Talk. This Could be the start of something beautiful.

If you’re planning to create a web portal, mobile app, or any other Internet product, please contact us. We’re always happy to hear a good idea!

E-mail address

fream@fream.pl

Phone

+48 501 408 386

Office address

Fream S.A.
Powstańców Śląskich 28-30
53-333 Wrocław
+48 501 408 386