Building an Enterprise-less

online bank

Anton Keks / @antonkeks / Codeborne

A bit of history


Founded in 2010 by former Swedbank developers


Acquired Hansabank, a very innovative bank of 2000s

After 2008 closed many branches/ATMs

In Estonia, most banking business is online



Bank Saint-Petersburg

Ural Reconstruction and Development Bank

GlobalFinance / Markswebb awards

Hand-crafted code/frameworks

produce simpler code

easier to customize

more reliable

you can fix it in case of urgent problems

The Enterprise (thinking)

Makes you write more code

Makes your app slow

Makes debugging hard

Makes sysadmin's life harder

If it breaks, it is broken


A platform for online banks

Private + Corporate + Mobile + Web site

Java 8 / Play Framework 1.3

Play framework

Rails, Django inspired

We use newly-released v1.3

not 2.x, which is for Scala

Why Play is cool?

Fresh, no bullshit, anti-enterprise

Writing few code that also works

Full-stack, quick start

Influenced our architecture a lot

In more detail

Code reloads

"Hit refresh" workflow

(You can use springloaded in your own project)

Play 2 is much slower at that

Disadvantage: it recompiles code itself

Good/Bad: so-called "Magic", or enhancers

Tries to make Java a better language

Properties, less boilerplate

Config files with support for environments


Localization files (multilanguage support)

Own HTTP server (based on Netty)

play start
play stop

No even servlets :-)


Signed cookies

String-only up to 4kb

No server state

Deployment during working hours!

Better back button support

+ Flash

Async support

Inspired by Node.js


Returns request thread to the pool

Serves all static files asynchronously

But, hard to call DB stored procedures that way


For asynchronous stuff

And processing (e.g. deferred and recurrent payments)

      new DeferredPaymentProcessor(payment).now();


public class DeferredPaymentProcessor extends Job { ... }


Dependency management (using ivy)

Many 3rd-party ones (e.g. pdf, excel)

And we write our own (logging, testing, cms)


Most stuff can be customized/redefined

e.g. ModuleInheritingPlugin

Alive and open-source @github

Client side

MVC is much easier than single-page apps

If needed, write modular JS/AJAX

Less & Autoprefixer for CSS

.book {
  padding-top: @defaultPadding + 10px;

  .title {
    font-weight: bold;
    transform: scale(1.2);


ActiveRecord pattern on top of JPA/Hibernate

User user = User.find("byUsername").first();
user.lastLoginTime = now();;

save() must be explicit

Hibernate in JPA mode (no detached save)


DB migration

Play evolutions are good

We use Liquibase (via plugin)

<changeSet id="123" author="Codeborne">
  <addColumn table="users">
    <column name="lastLoginAt" type="timestamp"/>

Supports almost any database

We share most (but not all) changesets between banks

Changesets must be backwards-compatible


Necessary evil

Abstraction layer between ibank and the backend(s)

ibank -> service -> banking system

MVC -> MVSC :-)


@Inject CardService cardService;
List<Card> cards = cardService.activeCards(customer);

class BankOne extends Module {
  public void configure() {

class Demo extends Module {
  public void configure() {

Services are non-abstract classes

Integrations, customizations


In every bank, there are plenty:

Core system (ABS)

Card/processing system



Customer support

Loyalty program


etc, etc, etc

More integrations - harder to be reliable

Parallel development is hard with non-agile devs

How do we do it?

  1. We write higher-level APIs for protocols first
  2. Then implement business logic using this API/DSL

SOAP client

soapService.request("Cards").param("CustomerID", 123L).send();

No code generation!

No problems with SOAP 1.0, 1.1, 1.2 interoperation

Stored procedures

dbService.query("get_customer", 123L);

Unfortunately, many of these (over)use XML

DOM and xpath are slow: custom SAX-based XML parser

new XMLParser((path, value) -> {
  switch (path) {
    case "messages/message": message = new Message(); break;
    case "messages/message/@id": = value; break;
    case "messages/message/subject": message.subject = value; break;

Template-based generation



You can even use it for more bizarre SOAP requests :-)


(Unit-)Test Driven Integration

Almost like TDD

  1. Call a real service (black box)
  2. Save the response
  3. Write a test for parsing of it
  4. Write code to make test pass

"Standard" Rest API

Alternative inverse integration

(Bank implements our spec)

GET /customers/123
GET /customers/123/accounts
GET /customers/123/accounts/123123123123/transactions

Automated testing tool with warnings and errors

Hard to make changes in the spec

Problems with backends

Backends are slow

Caching via Guice interceptors

bindInterceptor(any(), annotatedWith(CacheFor.class), this);


Prefetching jobs

Backends can be offline


Health monitor

Temporary storage

Deferred executor jobs

Backends have partial data

Merge data from multiple sources

All in the name of UX


CSS first


You can override any file in sub-module

You can write 100% custom code

No architectural restrictions

(besides discouraging of copy-paste)


Essential part of knowing that your code (still) works

Good: testing is part of Play

Bad: Rails-style "boot-up-everything" tests

Slow + Not great for TDD

Unit + UI

1500 tests, 4-5 mins

Run all during every build

Optimizations + Parallel execution

Unit tests

Fast, preferred

Business logic and all special cases

Mockito helps

userService = mock(UserService.class, RETURNS_DEEP_STUBS);

UI tests

Slower, for "happy paths"

Selenide helps


In-memory DB: H2

Play supports initial data via Fixtures

Restore state before each test

Demo integration


Most developers don't log properly

Good logs are



Completely traceable

Multiple files

  • request.log
  • security.log
  • services.log
  • general.log

Logging tips

Request ID / Thread ID


Java stack traces only when needed

Play: Self4J over Log4J

grep/awk are your best friends

Build process


Compile + compress resources + unit tests + ui tests

Any green build can go to production


2 servers

Stateless: deployment during working hours

Simple DNS-based load balancing

No loss of sessions


Most of our customers had a previous online bank

With some users

Transition period

Continuous data migration

Embedded/live documentation e.g. banklink protocol description



play-web @github

Thank you!

Play modules & Selenide:


Sounds fun?

Anton Keks / @antonkeks / Codeborne