Rho + DDD + doobie + Docker scratchpad
My little sandbox for playing around with the FP + OOP + DDD combination, in particular using Rho, doobie, Docker, integration testing, etc in a project. In the future, this might be spun out into a G8 template.
Goals:
- Finding that sweet spot between FP, OOP and light DDD
- Get feet wet with using Rho as a routing + API spec layer on http4s through building not-completely-trivial CRUD API endpoings with one-to-many parent-child modeling
- Docker
- For booting up app w/ Psql db
- For integration tests
- Migrations tests
- Doobie for interfacing with Postgres
- H2 for simple repository tests
Explores:
- Using http4s with cats-effect
- Using rho for Swagger support
- Chimney for assisting in DDD model transforms between layers
- Using doobie with cats-effect
- Pureconfig for loading configuration
- Flyway for handling migrations
- Docker testkit for bringing up Docker containers on a per-test-suite basis
- sbt-native-packager for bundling the app into a Docker image
- sbt-docker-compose for bringing up a docker-compose environment based on the main project and a docker-compose.yml file
- Can run the whole project from sbt via
sbt dockerComposeUp
(stopped viadockerComposeStop
) - Also used for running Integration tests from a separate subproject
- Can run the whole project from sbt via
- Swagger API spec w/ UI via Webjar
Usage
Docker is essential to having fun here, so go download it if you don't have it installed.
Browse the API Spec (Swagger)
sbt dockerComposeUp
and go to the root in your browser (e.g. localhost). It will start a
docker-compose environment that includes the web-server and dependencies.
Tests
sbt test
will run normal testssbt dockerComposeTest
will run integration tests
Notes
On Rho
- Tying route definition + doc generation together with route implementation is genius. It means the spec is always up
to date
- The type-level mechanism (e.g. that huge HList for capturing status + response types) is amazing. A lot of things
just work
- Multiple branches in your code that respond with different statuses and types get reflected in the generated docs.
AnyVal
wrappers are exposed in the documentation as their underlying type.
- Composing routes + documentation is just by value
- The type-level mechanism (e.g. that huge HList for capturing status + response types) is amazing. A lot of things
just work
- At the "end of the world", you can combine all your Rho service into a single http4s service (currently by calling
.toService
and passing in a Rho middle ware). This causes the routes and specs to get compiled.- This means it's entirely possible to have Rho work along side APIs defined as vanilla http4s endpoints.
- Areas for improvement
- Support for more areas of spec generation
- On models themselves; completely lacking, save for
withCustomSerializer
usage (see docs), which is not optimal (far away from actual models). Would be awesome if it:- It could be extended (or default to) using Swagger API annotations is they are there.
- Allowed defining the case-ness (e.g. snake or camel)of outputted JSON schema annotations, for example
- Support more things like endpoint tags and summary (maybe these exist, not sure)
- On models themselves; completely lacking, save for
- Documentation is a bit sparse; there's a wiki and some examples ... but that's it.
- Support for more areas of spec generation
On http4s
-
Integration with Cats standard libs is amazing
- Not being tied to a specific Effect is very liberating (ie working with
F[_]
throughout is very clean, no need forimplicit eCtx: ExecutionContext
boilerplate everywhere)
- Not being tied to a specific Effect is very liberating (ie working with
-
Comes out of the box with support for various middlewares (auth and there is also tsec, a crypto lib with an http4s integration module.
-
Used in production at a number of places, including Verizon, Jack Henry, etc.
-
Various integrations exist, like
- kamon-http4s
- typedapi
- Various JSON libs like circe, json4s (powered by Jackson or Native), Play-JSON, etc.
-
http4s itself supports running on various servers other than Blaze (default), including as a Servlet, enabling usage in:
This is interesting because it makes migration easy (mount old Servlets with new ones.)
On Chimney
- This is the real deal for DDD in Scala. Makes DDD so easy and painless.
On Doobie
- Integrates well with the rest of the system due to Cats as lingua franca
- What-you-write-is-what-you-get SQL usage
- JDBC-based so it still blocks a thread somewhere; there is an issue for a way of emulating Slick3-style asynchronicity
- Safe: type-checked of course, but there are also query soundness checks (see
QuerySoundnessSpec
) to check your queries against a real DB.
Docker usage
- Using docker through the sbt plugin system smooths out integration testing and local manual testing. A boon for lowering the barrier of entry for devs getting started.
Feedback
Help me learn ! Ask questions, submit issues and PRs :)