Migrate from V3 to V4

Ballast v4.0.0 is a major release, which some breaking changes in its public API and many changes to its internals. Most (but not all) changes are source-compatible with Ballast v3, but these changes are not intended to be binary-compatible.

Below is a list of all changes, broken down by module.

  • All modules
    • APIs that were deprecated since v3.0.0 have been removed.
    • The BallastException base-class has been removed. Each module had already been using its own Exception sub-types where necessary, but these exceptions no longer share a common super-type.
    • Modules which provided their own Inputs and Events classes are now changed to be sealed interfaces instead of sealed classes.
    • Modules which provided their own Inputs and Events classes are now changed to use data objects.
    • Added proper toString() values to Interceptors, for prettier logs
  • ballast-api/ballast-core
    • New API added to BallastInterceptorScope: getInitialState(). This allows an Interceptor to access the initial state passed to the BallastViewModelConfiguration.
    • Began refactoring internals for greater flexibility. This work is only partly done, but some new features are now available for advanced usage:
      • InputStrategies no longer require buffering Inputs through a Channel. You may create your own InputStrategy for special use-cases that do not use a Channel, like immediately dispatching Inputs, or buffering Inputs though another mechanism such as AWS SQS. A ChannelInputStrategy base-class is used to implement the previous functionality of buffering through a channel. The existing Lifo, Fifo, and Parallel InputStrategies all extend this base class, but you are free to extend it yourself if you need to tweak the configuration of the channel.
      • InputFilter has been removed as a property of the BallastViewModelConfiguration, because the basic idea behind it is not compatible with all InputStrategies. Instead, the InputFilter can be passed directly to the InputStrategies which are compatible.
      • A new API, EventStrategy is now available to provide the same kind of pluggable extensibility for handling Events that is used for Inputs. This feature is still a work-in-progress, use at your own discretion. As with the InputStrategies, a ChannelEventStrategy provides the basic functionality of buffering Events through a Channel, and you can extend that base class to customize the channel's parameters.
    • Builder.withViewModel() now returns a strongly-typed variant of the Builder class, allowing for greater type-safety when applying Interceptors. The new convention, which is somewhat enforced by the builder APIs, is that common Interceptors which don't require specifics about the Contract types (like logging, or attaching the debugger client) should be placed before the call to builder.withViewModel(). Interceptors which do need to know specifics about the ViewModel instance and its Contract types (like BootstrapInterceptor) should go after that call, to ensure the values passed to the interceptor match the ViewModel's types.
  • ballast-debugger
    • This artifact's dependency coordinates have been renamed to ballast-debugger-client.
    • The lambdas passed to the BallastDebuggerInterceptor are now encapsulated in the DebuggerAdapter interface. Functions on the BallastDebuggerInterceptor companion object keep the construction of BallastDebuggerInterceptor source-compatible, but you may now create your own instance of DebuggerAdapter for easier setup and maintenance.
    • A new concept has been added to the Debugger UI and client: processing Inputs and restoring States from JSON (or other serialized state). You simply need to mark your Input and State classes as @Serializable with kotlinx.serialization to use the built-in JsonDebuggerAdapter or BallastDebuggerInterceptor() convenience function. Alternatively, if you need to support a different serialization format or use a different serialization library, you can implement a custom DebuggerAdapter to deserialize values based on the incoming ContentType.
  • ballast-navigation
    • New Inputs added to the RouterContract, to handle some common navigational patterns:
      • RouterContract.Inputs.PopAllWithAnnotation allows you to remove the last destinations which have a given RouteAnnotation. For example, this may be used if a sub-flow's destinations all have a common annotation, so you can quickly exit the flow by removing all routes with that annotation.
      • RouterContract.Inputs.PopUntilAnnotation allows you to remove the last destinations until one is encountered with a specific RouteAnnotation. For example, this may be used to annotation the first destination of a sub-flow with a RouteAnnotation, so you can either quickly return to the start of the flow, or return to the destination just before starting the flow.
      • RouterContract.Inputs.PopUntilRoute has similar behavior to PopUntilAnnotation, but uses Route types instead of RouteAnnotation.
  • ballast-saved-state
    • You can now access (and return) the default initial state from the RestoreStateScope for cases where you want to optionally restore the state, rather than recreating the default initial state yourself within the Adapter, or passing the default initial state to multiple locations.
    • New APIs have been added to SaveStateScope to allow you to manually determine if a value has been changed, rather than relying on the default equals (!=) operator. Overrides of saveDiff() and saveAll() have an additional isChanged parameter where you can compare the previous and current values for equality.
  • ballast-test
    • Test: A new API has been added to BallastScenarioScope, customizeConfiguration(). This allows you to fully customize the entire Builder configuration used to run the test.