question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Injection patterns for reactive models?

See original GitHub issue

I have found the injection model to feel strangely backwards when used with reactive programming. I am using RxKotlinFX, which is backed by RxJava and RxJavaFX.

Here’s a little theory that might be driving this, and forgive me if I’m wrong on any assumptions. I’m not an expert on MVC at all.

Traditional imperative models would have the View pull items from the Controller, which would lazily trigger instantiating the Controller. But a reactive model is likely to push items from the Controller to the View. It is an inversion of control.

So it is a little awkward to inject a Controller into a View, when really the View should be injected into the Controller. Otherwise, with the current injection pattern the View will actually be doing all the work and not the Controller.

If I’m not making any sense, try to think about migrating the reactive logic below from the View to the Controller. Unless I’m missing something, you might realize there is no way to accomplish this without a lot of messy initialization and anti-patterns to interop the two.


class MyApp: App() {
    override val primaryView = MyView::class
}

class MyView: View() {

    override val root = VBox()

    val controller: MyController by inject()

    var countLabel: Label by singleAssign()
    var button: Button by singleAssign()
    var itemsList: ListView<String> by singleAssign()

    init {
        with(root) {
            button = button("Increment")
            countLabel = label("")
            itemsList = listview()
        }

        //everything else below needs to move to the Controller

        //increment number on countLabel every time button is pressed
        button.actionEvents()
                .map { 1 }
                .startWith(0)
                .scan { x,y -> x + y }
                .map { it.toString() }
                .subscribe { countLabel.text = it }

        //subscribe items into itemsList
        controller.items.subscribe { itemsList.items.add(it) }
    }
}

class MyController: Controller() {
    val items = Observable.just("Alpha","Beta","Gamma","Delta","Epsilon")
}

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:22 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
edvincommented, Apr 2, 2016

OK! I agree the above code looks clean and doesn’t present any problems that I can see. The View is supposed to contain view logic, so events emitted by the ui should be manipulated there. I’ll revisit this once I get more reactive experience and look for ways to optimize 😃

0reactions
thomasnieldcommented, Apr 2, 2016

I think I’m overthinking this quite a bit. I’ve done a few experiments and I think I’m expecting too much from the Controller in a reactive setting, when really its role has not changed much.

I believe the code I had earlier is acceptable. I think a “good design” might entail this:

  • Cold data-driven Observables reside in the Controller
  • Hot Observables derived from UI events in the View should stay on the View
  • The Controller can hold hot Observables when it acts as an “event bus” of sorts between Views
class MyApp: App() {
    override val primaryView = MyView::class
}

class MyView: View() {

    override val root = VBox()

    val controller: MyController by inject()

    var countLabel: Label by singleAssign()
    var button: Button by singleAssign()
    var itemsList: ListView<String> by singleAssign()

    init {
        with(root) {
            button = button("Increment")
            countLabel = label("")
            itemsList = listview()
        }
        button.actionEvents()
                .map { 1 }
                .startWith(0)
                .scan { x,y -> x + y }
                .map { it.toString() }
                .subscribe { countLabel.text = it }

        //subscribe items into itemsList
        controller.items.subscribe { itemsList.items.add(it) }
    }
}

class MyController: Controller() {
    val items = Observable.just("Alpha","Beta","Gamma","Delta","Epsilon")
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

View Models - ReactiveUI
Note With the ReactiveUI.Fody package, you can implement the described patterns by annotating properties with either the [Reactive] or [ObservableAsProperty] ...
Read more >
Understanding the reactive pattern | Spring 5 Design Patterns
Understanding the reactive pattern. Today, the modern applications must be more robust, more resilient, more flexible, and better positioned to meet the ...
Read more >
Reactive Design Patterns for Microservices - Charly Bechara
A distributed architecture consists of several microservices, where each microservice is implemented using the Actor model such as Akka and ...
Read more >
Flying High With Design Pattern & Dependency Injection ...
In this series, I'll teach you how to create an easy and effective architectural structure from the base when starting a project, as...
Read more >
Angular Forms: reactive design patterns catalog - InDepth.Dev
We call this layer the Form Model pattern which defines a wrapper ... The factory can be injected directly in the component class...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found