spring-projects-experimental / spring-fu

Configuration DSLs for Spring Boot
Apache License 2.0
1.67k stars 139 forks source link

Proposal : Separate DSLs for blocking and reactive apps #343

Closed pull-vert closed 3 years ago

pull-vert commented 3 years ago

Reactive apps must never block, and blocking apps should not use reactive libraries. They are kind of two incompatible worlds.

This Github issue is a proposal on this subject, and is the continuation of PR-333 discussion

Current implementation

For now spring-fu expose all DSLs with no limitation using a common ConfigurationDsl which all DSLs are based on.

This implies that all configurations are allowed today, for example this one :

val app = webApplication { // <- creates a ServletWebServerApplicationContext
    webFlux {} // should not be possible inside a Servlet context
    jdbc {} // would be correct for a 'webApplication' with 'webMvc'
            // but should not be used from inside webFlux routes
}

This limitation also forces non compatible DSLs (both based on ConfigurationDsl) to have distinct names, for example webFlux and webMvc.

Proposal

The goal of this proposal is to force the user to choose a mode between Blocking and Reactive. No blocking DSL would then be available inside a spring-fu reactive App configuration, and vice-versa.

Reactive example with this proposal

val app = reactiveWebApplication { // <- creates a ReactiveWebServerApplicationContext
    enable(dataConfig)
    enable(securityConfig)
    enable(webConfig)
}

val dataConfig = reactiveConfiguration {
    r2dbc {} // jdbc DSL is not accessible inside 'reactiveConfiguration' block
}

val securityConfig = reactiveConfiguration {
    security {} // The reactive security DSL
}

val webConfig = reactiveConfiguration {
    web {} // The former 'webFlux' DSL
}

Blocking example with this proposal

val app = webApplication { // <- creates a ServletWebServerApplicationContext
    enable(dataConfig)
    enable(securityConfig)
    enable(webConfig)
}

val dataConfig = configuration {
    jdbc {} // r2dbc DSL is not accessible inside 'configuration' block
}

val securityConfig = configuration {
    security {} // The blocking security DSL
}

val webConfig = configuration {
    web {} // The former 'webMvc' DSL
}

Implementation of this proposal

Change the existing reactiveWebApplication DSL to define a new ReactiveApplicationDsl scope, instead of the ApplicationDsl that is currently used by both reactive and blocking apps.

fun reactiveWebApplication(dsl: ReactiveApplicationDsl.() -> Unit)

This ReactiveApplicationDsl would extend a new ReactiveConfigurationDsl. This change would also require to add new reactiveApplication and reactiveConfiguration DSLs for non web apps and sub-configurations :

fun reactiveApplication(dsl: ReactiveApplicationDsl.() -> Unit)

fun reactiveConfiguration(dsl: ReactiveConfigurationDsl.() -> Unit)

With these changes the ReactiveConfigurationDsl.security { } would be the Reactive version, and the ConfigurationDsl.security { } would be the MVC one.

As mentionned before, it would also allow to have just web instead of both webFlux and webMVC, expose r2dbc only for reactive apps, and maybe more but these would be breaking changes for the current API.

Extra implementation note Current ConfigurationDsl would have to be renamed to AbstractConfigurationDsl and would have 2 distinct implementations : ConfigurationDsl and ReactiveConfigurationDsl.

For a smooth transition, existing DSLs could continue to be extensions on AbstractConfigurationDsl to ensure backward compatibility, with a deprecation notice to suggest to use new exclusive DSLs.

sdeleuze commented 3 years ago

I think I would like to keep consistency between regular Spring and Spring Fu concepts, and reactive bits can be used in regular/blocking apps (WebClient for example but there are other use cases), that's why I am not in favor of this proposal to introduce such distinction at configuration API level.

I even don't like the distinction we do at application level but it is due to historical choices made on Spring Framework that forces Spring Boot to have distinct application contexts.

pull-vert commented 3 years ago

That's exactly this distinction in application context that gave me the idea to push it further in this proposal ;)

I understand your opinion, writing this proposal was a fun and interesting exercise for me. You can close it if you want, no regret :)

sdeleuze commented 3 years ago

Thanks for your understanding.