Search code examples
springvaadinmvpspring-bootvaadin4spring

How to create a MVP architecture with Vaadin + Spring?


I'd like to create the following simple MVP architecture:

  • View classes that are simly the vaadin layout, components, styles. nonfunctional. The views should be tied to the current ViewScope/SessionScope, therefore I use @UIScope of https://github.com/peholmst/vaadin4spring

  • Presenters should have the view injected, register listeners on the view components, handle user input and delegate to the model services

Problem: when I inject the view into the presenter, the view is recreated, thus presenter and view are not in the same scope. So the binding will not work. What can I change to achieve the design described above?

@VaadinComponent
@UIScope
public class LoginView {
    //form fields, buttons
}

@Controller
public class LoginPresenter implements ClickListener {
    @Autowired
    private LoginView view;

    @PostConstruct
    public void bind() {
        view.getLoginButton().addClickListener(this);
    }   

    @Override
    public void buttonClick(ClickEvent event) {
        //validate input and login
    }   
}

Solution

  • Maybe something like

    public class LoginView {
    
        @Autowired
        public void initPresenter(LoginPresenter loginPresenter) {
            loginPresenter.setLoginView(this);
            loginPresenter.bind();
        }
    }
    
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class LoginPresenter {
    
        private LoginView loginView;
    
        public void bind() {
            // ...
        }
    
        public LoginView getLoginView() {
            return loginView;
        }
    
        public void setLoginView(LoginView loginView) {
            this.loginView = loginView;
        }
    
    }
    

    Edit

    You can decouple adding a configuration interface but adds some complexity, for example

        public interface View {
    
        }
    
        public interface Presenter {
    
            void setView(View view);
            void bind();
        }
    
        public interface ViewManager {
    
            void configure(View view);
        }
    
        public class ViewSupport implements View {
    
            @Autowired
            private ViewManager viewManager;
    
            @PostConstruct
            public void init() {
                viewManager.configure(this);
            }
        }
    
    
    
     /**
      * ViewManager that configure Presenters following 
      * the naming convention XXView->XXPresenter
      */  
     public class DefaultViewManager implements ViewManager {
    
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Override
        public void configure(View view) {
            Presenter p = (Presenter) applicationContext.getBean(getPresenterName(view.getClass()));
            p.setView(view);
            p.bind();
    
        }
    
        protected String getPresenterName(Class<?> clazz) {
            return StringUtils.uncapitalize(clazz.getSimpleName()).replace("View", "Presenter");
        }
    
    }