3

Our organization relies heavily on Public Site Visual Force pages for data collection from our user community. These sites contain about a dozen or so VF Pages. The entire site controlled by a single controller.

As a result, our code base contains some huge monolithic controllers - exceeding 3,000 lines of code. Managing and maintaining these controllers is a huge PITA. They become a spiderweb of action methods that becomes increasingly difficult to debug and modify. I want to kill these controllers with fire.

Most of the pages contain a Back, Next, and Exit button with the occasional page specific button. The actions behind each Back, Next, and Exit button can differ depending on the current page and state of the progress through the site.

I contemplated using a State Pattern to facilitate the functionality of page navigation (e.g. on the Welcome Page the Next action only returns a Page Reference to the Instructions Page, on the Edit Record Page, the Next runs validation on the record and adds it to a list.)

However, I am getting stuck trying to figure out how to handle the dependency injection, various action method signatures, and handling return value assignments without creating separate action methods for each action (i.e. some Next Action methods would need to act on a list of records, modify them, then return the modified result back to the controller while others would simply advance to the next Page Reference).

Does anyone have any advice on how to construct better Controllers that don't turn into monolithic monstrosities?

Thanks, Rob

  • In our scenario, we'd used a Base class for having all common functionality along with templates – Raul Feb 6 '17 at 15:48
1

The kind of controller you describe as currently having would normally only be used in a single page application or perhaps in some kind of flow pattern where state needs to be maintained in several variables; assuming you're building a form. If your Site has a Menu, perhaps much of this isn't needed at all to access many of the pages.

Even if that's the case, common functionality needs to be moved out into "helper" or what you'd call "service" classes that perform specific functions which can be called from your primary class.

You want your navigation to be dynamic and independent of what's coded in your controller by passing arguments through it. That way, when you decide to change a page or some kind of content, you won't need to change the code in your controller. This would allow you to use the same variables to hold your temporary data (if you have enough of them). You could presumably do this either using custom metadata or custom settings that your controller reads when you make these changes to add new pages or make edits.

EDIT:

From what you've described, I would recommend you write the service to use an interface based on the strategy pattern. From the inputs to the interface the class would determine whether to modify, validate or advance the state when called.

An interface can have more than one signature. Each signature can be used to help determine which operation/method should be performed. Using the inputs from the State Interface, methods from the DoNext Service class (I think you should have named them using the reverse as in DoNext Interface and State Service class) is called with parameters that tell the class which methods to call.

Depending on what it calculates for it's output and whether you want to store it, the State Service can either calculate the page reference itself, return a parameter, or call a PageReference Service class. All of this combined effectively creates either an Abstract Factory pattern or a combination of a Strategy pattern used in conjunction with a Chain-of-responsibility pattern.

I should add that I'd be inclined to return a parameter and use an inner class in the original calling Class to set the new page reference using the parameter that's returned. Using the interface and service class chaining, you're maintaining a separation of concerns.

  • Thanks @crmprogdev, this is the issue we are having. As you surmised, the site is a multi-page form. And what I am trying to do is move my functionality into service classes. My issue is that the controller has a method doNext() which, depending on which page I am on, has different functionality. This seems like a classic use case for State Pattern, but the issue is how to have my doNext() method modify the data in the controller with specific functionality for the given page. Currently, we have ten pages so we have 10 doNext_<page>() methods. Do you have an example of your recommendation? – Rob Davis Feb 6 '17 at 19:08
  • What would you pass to the DoNext method as parameters if you were to modify it to send it to a service class, I presume to return a pageReference and perhaps populate a variable? In essence, what would distinguish one call from the next? Do they all use the same type of variables with different values? The pattern is important to suggest how to do this because we're talking about an interface of sorts. – crmprogdev Feb 7 '17 at 17:42
  • The controller's DoNext method has three required actions, modify or validate the data, advance the state, and return the next page reference. Those three actions are three different methods in the State object (defined in an interface). The modify or validate step needs to take in certain data, depending on the context. The issue is how to keep the method signature the same across all States. I could create a Service to store all of the relevant data structures for the form. That Service class could be passed as a param to each of the state methods. Does that seem sound? – Rob Davis Feb 8 '17 at 16:04
  • Thanks for your help. I was able to implement a strategy based solution, but it turned out to be far more trouble than it was worth. Back to monolithic controllers for the time being and back to the drawing board for a better solution. – Rob Davis Feb 27 '17 at 16:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.