No description
Find a file
2026-02-04 16:04:00 +00:00
.mvn/wrapper Initial commit 2026-02-04 16:34:32 +01:00
src Improve readme 2026-02-04 17:03:15 +01:00
.gitignore Initial commit 2026-02-04 16:34:32 +01:00
mvnw Initial commit 2026-02-04 16:34:32 +01:00
mvnw.cmd Initial commit 2026-02-04 16:34:32 +01:00
pom.xml Initial commit 2026-02-04 16:34:32 +01:00
README.md Update README.md 2026-02-04 16:04:00 +00:00

Hexagonal architecture with Javalin

The bowling kata, with a simple HTTP API on top

Notes about architecture and testing strategy

It's important that the *Request, *Response records declared in the routes package are NOT public.

Any change in those classes would be a breaking API change :)

This is also why we use plain Map<String, Object> in the tests - that way if the returned JSON changes, our tests break.

Future work

For now we just have a Game instance directly in the app data, because there's no persistence at all.

You get one Game per Javalin app, which is enough for this kata.

In real life, you'd probably have something like:

public interface GameRepository {
  /**
   * Store a new game in the repo
   *
   * @return the newly created ID
   */
  String createGame();

  void saveGame(String id, Game game);

  Game getGame(String id);
}

and then, in main

GameRepository gameRepository = new SQLGameRepository();

var app = Javalin.create(config -> {
  config.appData(GAME_REPOSITORY_KEY, gameRepository);
});

and in the routes:

var id = "..."; // extracted from the request
var repo = context.appData(Application.GAME_REPOSITORY_KEY);
var game = repo.getGame(id);
// same code as before

That way you can either:

  • inject the SqlGameRepository in the end-to-end tests

or, if you want a different kind of tests:

  • mock the GameRepository in the end-to-end test and focus on testing the status codes and the shape of the json requests/response - but in that case, you're more or less writing contract tests (which are fine too!)