2016-07-14



alvinashcraft
shared this story
from Khalid Abuhakmeh.

Back Story

My employer likes to give out what you may think of as "Monopoly money" as an incentive to employees when they act extraordinarily. My coworkers and I call them Ritter Bucks, and they can be used to buy items from an internal company store: shirts, candy, and more. While it works for our home office, it is hard to involve our remote offices and remote employees in this practice. A few coworkers and I talked about digitizing the program so all employees could participate, and potentially start an internal economy fueled by good will.

Transactions and Marten as an Event Store

For those of you who are not familiar with Marten, it is a document database interface built on top of PostgreSQL. It also supports event store functionality. An event store database tracks every action as an independent event and gives the developer two main options of querying for information: Reading individual events or aggregating events into a Projection.

Anyone with a bank account will be familiar with the concept of transactions. A banking transaction involves a debit or a credit. The accounts balance is an aggregation of all transactions. The bank transaction problem is an excellent problem for an event store to solve.

Code

For the impatient, head over to my GitHub repository for a working example: marten-bank-sample. The rest of the post will walk through what I did to build this working model.

Events

This sample is comprised of a few events that comprise the domain: AccountCreated, AccountCredited, AccountDebited, and InvalidActionAttempted. Let's take a look at a few of these events.

I store a new event when I create a new account in my system.

Since I am building a banking application, my core events are Transaction.

Projection

Events are only half of the work. A projection is what you will most likely present to the user. In this example, our projection is an Account.

Marten uses the Account class to run matching events through the Apply methods. I get to write the logic that transforms the projection over time. In the code above, I am just changing the account's balance.

Program

The main program is where the Marten setup happens and where you see it all come together. I have broken it down to make more sense.

The above code shows where I setup the event store. I tell Marten about all of the events, and I inform it to build our projection every time an event is appended to our stream. Next, let's create the accounts.

Every account has a unique stream identifier. The identifier allows me to aggregate and project. In my example, I have two accounts which I use to perform a bank transaction.

Since I am asking Marten to build my projections and store them in the database, I can load them directly without dealing directly with events.

In my model, I decided that each stream should have a corresponding credit and debit. The approach makes sure that each stream can be aggregated on its own.

I have combined all these code snippets in a working example, and the output is as follow.



Pretty cool! I'm in the money now!

Show more