A simple opinionated framework for booting applications. Intended to be much simpler than Spring Boot or even modern iterations like Quarkus and Micronaut, but still featureful enough to be useful. Handles stopping, starting, lifecycle management, logging, configuration, and other basic reusable components, and integrates Kodein-DI.
An important element of simplicity is to prefer the use of explicit references and configuration. Spring Boot’s runtime discovery of components and plugins via annotation and reflection is easy but not simple. To use Spring Boot, you have to understand two layers: the libraries underneath Spring, as well as all of Spring’s own auto-magic annotations. Too many projects use Spring Boot, but fail to understand the hidden complexities, resulting in nasty bugs in production.
The goal of Bootable is to provide a minimal framework for running Kotlin (or Java?) applications, and then gets out of your way.
Useful reading:
-
Creates a DI context.
-
Manages the lifecycle of application services.
-
Allows registration of lifecycle controllers, which can be used to control the lifecycle of application services in various ways. The basic built-in implementation of this is the
AppStartStopLifecycleController
which starts application services on app start, and shuts them down on app close, in priority order. Another basic implementation, intended to be used in conjunction withAppStartStopLifecycleController
, is theStopSignalHandlerLifecycleController
, which registers signal handlers, which, when received, cleanly shuts the system down. Other implementations are possible: for example starting/stopping services at runtime based on a command system, like start/stop messages from RPCs or web requests.
Bootable is modular, and provides a number of modules to help you build your application.
The core module is simply com.github.rocketraman.bootable:boot
, which provides the basic application lifecycle features of Bootable.
Add additional modules as you wish.
See cfg4k.
Add this module to your project to use cfg4k for configuration. Note that while cfg4k works well, it is unmaintained. We recommend using Hoplite instead.
See hoplite.
Add this module to your project to use Hoplite for configuration.
Warning
|
Over-using this abstraction is a code smell. |
This module simply exposes a strongly-typed global environment
variable to the system, which is initialized via the configuration system when environmentConfigModule
is imported.
This should be rarely used, but sometimes it is useful to have a way to identify the environment e.g. dev, test, prod, or other. Good use cases are tagging metrics with the environment, or extremely complex environment-specific configuration that can’t be easily abstracted using one of the Configuration modules.
Note that this is also useful in a JavaScript environment, where the environment may be bound to a global variable by something like dotenv-webpack. The environment abstraction may be used to abstract away the underlying details of this binding in a way that can be used in both frontend and backend in the common code source set.
Note
|
We currently do not publish multi-platform versions of these modules, but may do so in the future. |
This module provides all the necessary imports to configure Log4j2 as the logging system for the application. It provides good default logging layouts, and supports multiple output types e.g. plain (both color and not), JSON, and GCloud. See LoggingType.
Provides a basic Ktor service abstraction with Bootable lifecycle integration i.e. the server starts and stops with the application. Does not attempt to configure Ktor in any significant way — this is left to the user. See KtorService.
See the projects in the examples
directory.
-
Application components that have a lifecycle should implement
AppService
orAdvancedAppService
, and these should be bound into the DI context viabindAppService
. -
Start the system with the
boot
function.
-
Customize with additional
LifecycleController
implementations. -
Override the
boot
function to achieve more custom behavior.
The heavy-weight nature of DI frameworks like Spring has given DI a bit of a bad rap.
Kodein-DI is a simple, lightweight, and fast dependency injection framework.
Many people argue that DI is unnecessary with Kotlin, and that simple constructor injection is sufficient.
This is absolutely true for small projects, but as a project grows, inevitably the need for modularity and more complex component scopes arises.
Consider a multi-module app, each module of which exposes one or more AppService
implementations without having any code be aware of all the possible implementations in advance.
All the modules that Bootable provides connect together via Kodein-DI.
That being said, nothing Bootable does is highly tied to Kodein-DI. Internally, Bootable is DI agnostic. See, for example, the main framework class Bootable.kt which does the basic work of starting and stopping the system. There are no references to any DI framework — it simply uses constructor injection.
To skip use of Kodein-DI, it is simple to use this class directly rather than the boot
convenience function which initiates the system via Kodein-DI.
Each module exposed by Bootable via Kodein-DI modules can also be exposed via other frameworks, or no framework at all.
Therefore, in the future Kodein-DI may be replaced in Bootable with some other DI-agnostic approach such as context injection or even basic constructor injection.
-
❏ Do we need really Kodein-DI? At the moment, only the
boot
function uses it, and that could easily be "fixed". The config and environment modules also expose Kodein modules, but could just as easily not do so. -
❏ Multiple logging implementations for different scenarios, loggingInit via
ServiceLoader
— or perhaps remain opinionated and do not do this? -
❏ Create a boot-server-http-ktor-cohort module using Cohort
-
❏ Update the ktor example to integrate Kompendium — this is outside the scope of Bootable, but it would be a good example of how to integrate Bootable with other libraries
-
❏ Make multiplatform — some modules are backend only e.g. Hoplite, Log4j2, but others could easily be adapted for multiplatform usage
Raman Gupta <[email protected]>