0

I'm building a JavaFX + Spring Boot application using the MVVM pattern.

Problem

I have two ViewModels:

  • SharedLoginViewModel: Manages the workflow state (e.g., login process, step transitions).

  • TokenCreationViewModel: Manages token creation logic (e.g., form validation, token generation).

The SharedLoginViewModel advances the workflow based on state changes. However, TokenCreationViewModel needs to notify SharedLoginViewModel to advance the workflow after successful token creation.

This creates a circular dependency:

SharedLoginViewModel injects TokenCreationViewModel (or vice versa).

This causes Spring Boot to throw:

creating bean with name 'sharedLoginViewModel': Requested bean is currently in creation: Is there an unresolvable circular reference?

What I’ve Tried

1. Direct injection

  • Causes the circular dependency.

2. Spring Events

  • TokenCreationViewModel publishes an event.

  • SharedLoginViewModel listens and advances the workflow.

This decouples them but feels overkill for simple workflow steps.

3. Custom Interface (LoginNavigationService)

Defined:

public interface LoginNavigationService { void advanceStep(); void cancel(); } 
  • SharedLoginViewModel implements this interface.

  • TokenCreationViewModel injects LoginNavigationService instead of directly referencing SharedLoginViewModel.

This works but feels awkward. I'm unsure if this is good practice.

What I Want

A clean way to decouple these ViewModels.

Avoid circular dependencies while allowing TokenCreationViewModel to advance the workflow after successful token creation.

Code Snippets

SharedLoginViewModel:

@Component public class SharedLoginViewModel implements LoginNavigationService { @Override public void advanceStep() { // Advance the workflow } @Override public void cancel() { // Cancel the workflow } } 

TokenCreationViewModel:

@Component public class TokenCreationViewModel { private final LoginNavigationService navigationService; @Autowired public TokenCreationViewModel(LoginNavigationService navigationService) { this.navigationService = navigationService; } public void handleTokenCreated() { navigationService.advanceStep(); } } 

Question

Is the LoginNavigationService approach a good practice for decoupling ViewModels?

Is there a better way to avoid circular dependencies between ViewModels in JavaFX + Spring Boot MVVM?

Any guidance appreciated!

New contributor
Billie is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
8
  • 1
    not a java expert but, don't you use events for this? ie MainVM.SubVM.Clicked += runWhenClicked : stackoverflow.com/questions/27416758/…
    – Ewan
    CommentedApr 24 at 8:21
  • One reason I’ve been hesitant to use Spring Events for UI flow is that it breaks the straightforward, downward communication chain. In MVVM, I prefer a clear path (Controller → ViewModel → Service) that’s easy to trace and debug. With Spring Events, communication becomes more asynchronous and less predictable, which is great for backend concerns but overkill (and potentially risky) for UI workflows that need thread safety and determinism.
    – Billie
    CommentedApr 24 at 9:04
  • 1
    what you are doing now though is akin to coupling button to your VM. you need the equivalent to onClick for your sub VMs
    – Ewan
    CommentedApr 24 at 10:53
  • Thanks for the insight! That makes a lot of sense and aligns well with MVVM principles. Out of curiosity, from your experience: What are the downsides of this listener-based (onClick-like) communication between ViewModels? Are there scenarios where you'd avoid it, or does it scale well even with more complex workflows? Would love to hear your thoughts before jumping into refactoring!
    – Billie
    CommentedApr 24 at 11:15
  • 1
    I normally take the approach of having a "top level view model" which hold all the logic and treats the sub vms as UI controls : softwareengineering.stackexchange.com/questions/446737/…
    – Ewan
    CommentedApr 24 at 12:33

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.