Event-sourced game implementation example – Part 3/3: Grabbing some data – statistics service

Event-sourced game implementation example – Part 3/3: Grabbing some data – statistics service

Event-sourced game implementation example – Part 3/3: Grabbing some data – statistics service

Welcome to the last part of dice game post series!

We already have fully working game that we created in 1st and 2nd parts.

Today we’ll see how we can take advantage of the fact that we’re using event sourcing.

We’ll create a separate statistics project that will catch all DiceRolled events and count how many times each number have been rolled.

For the statistics storage we’ll once again use Akka persistence. In fact it’s such a simple case that any storage mechanism will work great, anyway we’re already revolving around event sourcing so why not use it one more time. Of course the events saved inside statistics module are completely separate from the game’s ones, thus the term event may refer to either game event or statistics eventdepending on the context it’s used in.

We’ll also create a REST API to access the statistical data. A single GET route will suffice here.

Summing up, here’s what we’ll need to do:

  • catch events
  • update and store the statistics
  • expose REST API

I’ll focus on each of these in the corresponding sections below.

Catching events

Similar to the webapp part, here, once again, we’ll use Reactive Rabbit to consume the events from RabbitMQ.

We’ll create another queue and bind it to the events exchange:

https://gist.github.com/LukasGasior1/8bcf66e19ac388ae7104

[StatsApp.scala]

The "x-match" -> "all", "type" -> "DiceRolled" bind arguments mean: “I want to receive DiceRolled events from all played games”.

Once the queue is created and bound all incoming events are routed to SubscriberActor (which is given a reference to statsActor – more on which later):

https://gist.github.com/LukasGasior1/b1e96a9f5ac09d0b50e5

[SubscriberActor.scala]

It extracts the rolledNumber value from each incoming event and notifies statsActor that it should increment the rolls count for the given number.

Notice that for this use case we don’t need to map incoming event to any case class. We’re only interested in one field: rolledNumber and that’s what we extract.

Storing events

Now it’s time to take a look at the StatsActor itself.

It’s a PersistentActor that can handle IncRollsCount command. Once it receives it, RollsCountIncreased event is generated and saved.

https://gist.github.com/LukasGasior1/253cd74fcdb53770460b

[StatsActor.scala]

Here, in contrast to GameActor (link) from 1st partpersistenceId is constant as we’ll have only one instance of StatsActor ever (created when the application starts).

If you’d like to read more about PersistentActor, please refer to the 1st partof this series.

The internal state is just a simple case class that stores all needed information (a Map of “rolled number” -> “total rolls count” entries).

https://gist.github.com/LukasGasior1/cf586835a7b05e58c216

[Stats.scala]

Another message that StatsActor handles is GetState that sends back the current state to the sender.

REST API

Finally, we need a way to access our data from outside.

Let’s setup a simple spray server for it. It’ll handle the stats route:

https://gist.github.com/LukasGasior1/91ba5c4b99e7f715a379

[StatsApi.scala]

We follow the actor-per-request pattern here, that means for every stats request we create a GetStatsRequestActor actor to handle it:

https://gist.github.com/LukasGasior1/88b05f530bcad0069c60

[GetStatsRequestActor.scala]

It fires the GetState query to statsActor and waits for the response. Once the response arrives it completes the request with formatted data:

https://gist.github.com/LukasGasior1/ea33ee79859950d9b363

[StatsApi.scala]

Testing

Now that we have all needed components, it’s time to test our statistics service.

First, let’s run everything we need, i.e.:

  • RabbitMQ
  • game backend server
  • game frontend app
  • our shiny statistics app

Here are the commands I use:

https://gist.github.com/LukasGasior1/ebfb1eb07442f9d490c7

If everything went good, we should now have all services running.

What does our statistics service return? Let’s check that…

https://gist.github.com/LukasGasior1/497b70e5b80ba9e0768a

As expected, we don’t have any dice rolls statistics yet. It should change after playing some rounds. Here’s what I got a few rounds later:

https://gist.github.com/LukasGasior1/7e0063eb0dd597be6aed

Great! We now have the information how many times each of the numbers have been rolled.

Summary

Good news, we’ve gone through the final part of this series. We have a working backend, frontend, and now, also a separate statistics service.

As you can see, it’s not that difficult to use CQRS/ES in a project. On the contrary, it can make some things easier. It obviously doesn’t fit all use cases, but it works especially well when there are lots of asynchronous stuff going on.

As you can see in the example of StatsActor, Akka persistence is not reserved for big, complex projects – it can, as well, easily be used in smaller ones. Whether you find it useful or not, I hope you enjoyed reading and will come back to us for more!

Don’t hasitate to clone the repo and play with it a little. Just to remind, full source code is available on GitHub.

Below, you’ll find a list of various resources I came across that I find readworthy.

Thanks for reading!

Other parts:

Links

Do you like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.

See also

Download e-book:

Scalac Case Study Book

Download now

Authors

Łukasz Gąsior

I'm a passionate Scala developer, working on commercial Java and Scala projects since 2013. I'm a big fan of DDD, CQRS/ES and distributed systems. I've been using technologies such as Akka (Core, Persistence, Streams, Cluster), Play! Framework, Spray and many others.

Latest Blogposts

17.04.2024 / By  Michał Szajkowski

Mocking Libraries can be your doom

Test Automations

Test automation is great. Nowadays, it’s become a crucial part of basically any software development process. And at the unit test level it is often a necessity to mimic a foreign service or other dependencies you want to isolate from. So in such a case, using a mock library should be an obvious choice that […]

04.04.2024 / By  Aleksander Rainko

Scala 3 Data Transformation Library: ducktape 0.2.0.

Scala 3 Data Transformation Library: Ducktape 2.0

Introduction: Is ducktape still all duct tape under the hood? Or, why are macros so cool that I’m basically rewriting it for the third time? Before I go off talking about the insides of the library, let’s first touch base on what ducktape actually is, its Github page describes it as this: Automatic and customizable […]

28.03.2024 / By  Matylda Kamińska

Scalendar April 2024

scala conferences april 2024

Event-driven Newsletter Another month full of packed events, not only around Scala conferences in April 2024 but also Frontend Development, and Software Architecture—all set to give you a treasure trove of learning and networking opportunities. There’re online and real-world events that you can join in order to meet colleagues and experts from all over the […]

software product development

Need a successful project?

Estimate project