distage: Staged Dependency Injection

distage is a pragmatic module system for pure-FP Scala & Scala.js. It combines simplicity and expressiveness of pure FP with the flexibility and extreme late-binding, traditionally associated with object-oriented dependency injection frameworks, such as Guice.

distage is suitable for wiring Tagless Final Style, ZIO ZLayer-based applications, and ordinary FP, actor-based or imperative Scala applications.

Getting started

The best way get started is to clone distage-example sample project and play around with it.

It shows how to write an idiomatic distage application from scratch and how to:

Why distage?

  1. Faster applications and tests: distage guarantees that no unnecessary instantiations will happen during your tests or application startup. distage itself is very fast, in part due to not relying on any sort of runtime reflection.
  2. Quick failure detection: distage performs all the integration checks for your application and tests even before any instantiations happened.
  3. Simple tests: distage eliminates all the hard work of setting up your test environments, especially configurable ones.
  4. Better integration tests: distage-testkit allows you to reuse expensive resources (like database connections and docker containers) across multiple integration tests, gaining performance and without sacrificing correctness.
  5. Simple development workflow: distage-framework allows you to develop Role-Based Applications, a fusion of Microservices and Monoliths, letting you run all your services in one process for development purposes (and even switch to mock implementations with a single commandline argument).
  6. Easier deployments: Role-based applications allow you to deploy and orchestrate less components and achieve higher computational density.
  7. Fast compile times and low mental overhead: Unlike fully compile-time DIs, distage does not impose a compile time penalty. distage extensions are simple to write and do not require type-level programming.
  8. Simple debugging: distage provides you important insights about your application and allows you to introspect and modify it on the fly, before any instantiations happen.
  9. High Correctness: distage supports resources and lifecycle natively and guarantees proper cleanups even when something went wrong.
  10. No reflection: distage generates constructors and type information at compile-time and does not use Scala reflection. As such, it’s compatible with GraalVM Native Image and Scala.js.
  11. Non-invasive: distage is designed to not impact the way your Scala code is written, it just removes all the initialization boilerplate. You don’t need to learn magic tricks to write components in a distage application.

distage is recommended by industry leaders:

Given its native support for type classes and higher-kinded types – both features indispensable to functional programming – distage is one of the leading dependency injection libraries out there. Bonus points for being built by a wicked-smart team that contributes to ZIO!

โ€” John A. De Goes

FAQ

Q: How to pronounce distage?

A: ‘Dee-stage’

Q: Isn’t it unsafe to use runtime dependency injection?

A: distage is split into two stages, first a wiring plan is calculated, only afterwards it is executed. Because of this, you can test the Plan for errors very fast, without executing any effects of your wiring โ€“ if tests pass, the wiring will succeed at runtime. Testing can also be performed at compile-time โ€“ distage-framework module a few experimental macros for aborting compilation on planning errors.

Q: How do I switch between production and test implementations of components?

A: Use Activation Axis

Documentation

Further reading

Example projects:

Support Chats:

Slides:

Videos: