MVC
MVC é um design pattern implementado por vários frameworks, inclusive o NEO. MVC significa Model, View, Controller (Modelo, Visão, Controle). Cada parte do MVC tem uma responsabilidade na aplicação.

- Model: É o modelo de dados da aplicação. Classes que guardam informações.
- View: É a camada de visão. Onde é montada a tela para o usuário. Em J2EE a camada de visão é geralmente representada por JSPs.
- Controller: É o que faz a integração da camada View com a camada Model. Responde as requisições do usuário e busca os dados necessários para montar a visão. Controla o fluxo da requisição.

O NEO fornece um controller padrão e várias tags para ser utilizadas na visão. A camada Model é escrita pelo desenvolvedor da aplicação.
Controller
O controller base do NEO é o MultiActionController, responsável por responder as requisições que vieram do usuário. Um controller responde a determinada URL que deve ser configurada através de annotations. É necessário também configurar um servlet do NEO no web.xml que repassará as requisições para os controllers corretos.
Registrando o servlet no web.xml
Para utilizar o controller do NEO é necessário registrar um servlet específico no web.xml. Esse servlet receberá a requisição do cliente e rapassará ao controller correto. Esse servlet é o DispatcherServlet. Cada DispatcherServlet registrado é reponsável por um módulo.
Importante: Utilize o DispatcherServlet do NEO. br.com.linkcom.neo.controller.DispatcherServlet. O Spring também possui um DispatcherServlet.
Veja um exemplo de web.xml com um módulo configurado:

    [JSP]
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4"
        xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

        <listener>
            <listener-class>br.com.linkcom.neo.core.web.init.ContextLoaderListener</listener-class>
        </listener>

        <servlet>
            <servlet-name>x</servlet-name>
            <servlet-class>br.com.linkcom.neo.controller.DispatcherServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>x</servlet-name>
            <url-pattern>/x/*</url-pattern>
        </servlet-mapping>

        <servlet>
            <servlet-name>resourceServlet</servlet-name>
            <servlet-class>br.com.linkcom.neo.view.ResourceServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>resourceServlet</servlet-name>
            <url-pattern>/resource/*</url-pattern>
        </servlet-mapping>

    </web-app>

Módulo 'x' configurado

- <serlvet>...<servlet> (linhas 12 - 15)
  Declara um br.com.linkcom.neo.controller.DispatcherServlet para o módulo x. O servlet-name deve ser o módulo que esse servlet registra.
- <serlvet-mapping>...<servlet-mapping> (linhas 16 - 19)
  Registra a url /x/* para o servlet x. A URL deve ser sempre /[modulo]/*
  Onde [modulo] é o módulo que esse servlet registra

A configuração é feita uma vez para cada módulo. Após a configuração do web.xml a configuração dos controllers é feita através de annotations.
Criando um MultiActionController
Após configurar o DispatcherServlet no web.xml o Controller já pode ser criado. É recomendável que o controller que utilizar no neo seja uma subclasse de br.com.linkcom.neo.controller.MultiActionController. Fique atento para importar a classe do NEO e não a do Spring. Além de extender o MultiActionController o controller deve informar em qual URL ele responderá através da anotação Controller. Veja o exemplo:

    package teste;

    import br.com.linkcom.neo.controller.Controller;
    import br.com.linkcom.neo.controller.MultiActionController;

    @Controller(path="/x/primeiro")
    public class PrimeiroController extends MultiActionController {

    }

- @Controller(path="/x/primeiro") (linha 6)
  Registra esse controller na URL /x/primeiro
- ... extends MultiActionController { (linha 7)
  Um controller do NEO deve extender br.com.linkcom.neo.controller.MultiActionController

A URL pode ser dividida em duas partes, a primeira é o módulo e a segunda pode ser um nome qualquer que desejamos para o controller. A URL onde o controller deve ser registrado sempre deve começar com o nome de algum módulo. No caso, o módulo escolhido foi o 'x'. Um controller só será capaz de responder a uma requisição se existir um DispatcherServlet cadastrado para o módulo.
A classe está correta mas ainda não possui nenhum código. O MultiActionController possui o conceito de Actions. Actions são funcionalidades que desejamos executar no sistema. Um MultiActionController pode ter quantas Actions forem necessárias, cada Action é representada por um método. Um método para ser uma action deve possuir uma determinada assinatura.
Deve ser enviado na requisição um parâmetro especial indicando qual é a Action desejada. Se o parâmetro não for informado, a Action executada será a Action padrão. Veja como ficaria o controller com apenas uma Action padrão:

    package teste;

    import org.springframework.web.servlet.ModelAndView;

    import br.com.linkcom.neo.controller.Controller;
    import br.com.linkcom.neo.controller.DefaultAction;
    import br.com.linkcom.neo.controller.MultiActionController;
    import br.com.linkcom.neo.core.web.WebRequestContext;

    @Controller(path="/x/primeiro")
    public class PrimeiroController extends MultiActionController {

    	@DefaultAction
    	public ModelAndView actionPadrao(WebRequestContext request){
    		...
    	}
    }

Para definirmos um método como Action padrão basta anotar esse método com @DefaultAction. Um método para ser Action deve ter a seguinte assinatura:

    public ModelAndView [nome](WebRequestContext request)

O método deve ser public retornar um ModelAndView e receber um parâmetro do tipo WebRequestContext. O nome do método pode ser qualquer um. O método pode, opcionalmente, lançar qualquer exceção.
O ModelAndView é uma classe do Spring, ela representa o modelo e a visão que devem ser utilizados. O modelo é simplismente um atributo colocado na requisição equivalente a (request.setAttribute) e a visão é o JSP que desejamos mostrar.
Supondo que desejamos mostrar o seguinte jsp:

    [JSP]
    <html>
    	<body>
    	Olá
    	</body>
    </html>

Devemos retornar um ModelAndView no controller com um nome lógico para esse JSP. Veja o exemplo:

    package teste;

    import org.springframework.web.servlet.ModelAndView;

    import br.com.linkcom.neo.controller.Controller;
    import br.com.linkcom.neo.controller.DefaultAction;
    import br.com.linkcom.neo.controller.MultiActionController;
    import br.com.linkcom.neo.core.web.WebRequestContext;

    @Controller(path="/x/primeiro")
    public class PrimeiroController extends MultiActionController {

    	@DefaultAction
    	public ModelAndView actionPadrao(WebRequestContext request){
    		return new ModelAndView("jspOla");
    	}
    }

O nome lógico do nosso jsp como informado pelo controller é jspOla. Esse nome deve ser transformado para o nome físico do JSP. Essa transformação é apenas a concatenação de algumas informações
/WEB-INF/jsp/[modulo]/[nome informado pelo controller].jsp
Onde [modulo] é o nome do módulo onde o controller foi registrado. E [nome informado pelo controller] é a string utilizada no construtor do ModelAndView.
No exemplo utilizado o nome do JSP seria:
/WEB-INF/jsp/x/jspOla.jsp
Então, se desejarmos que aquele jsp seja mostrado na tela do usuário devemos salvá-lo com o nome jspOla.jsp no diretório /WEB-INF/jsp/x
Quando pedirmos no browser http://[host]:[porta]/[app]/x/primeiro a requisição cairá no DispatcherServlet configurado no web.xml que por sua vez repassará a requisição para o PrimeiroController. Como nenhuma Action foi informada, o método que for a Action padrão será executado, nesse caso actionPadrao. Esse método retorna um ModelAndView com o model jspOla. O NEO receberá esse ModelAndView e fará a tradução para /WEB-INF/jsp/x/jspOla.jsp e executará um forward da requisição para esse JSP.
Várias Actions em um MultiActionController
É possível que um controller possua várias Actions. Isso é útil para fazer com que ações relacionadas fiquem dentro da mesma classe. Poderiamos querer uma action para listar os usuários e outra para salvar um usuário no banco de dados, com o MultiActionController é possível criar dois métodos (um para listar e outro para salvar) e chamá-los de acordo com a ação desejada. Para escolher qual método deve ser chamado no MultiActionController um parâmetro ACAO deve ser enviada na requisição. Por exemplo, suponhamos o seguinte controller:

    package teste;

    import org.springframework.web.servlet.ModelAndView;

    import br.com.linkcom.neo.controller.Controller;
    import br.com.linkcom.neo.controller.DefaultAction;
    import br.com.linkcom.neo.controller.MultiActionController;
    import br.com.linkcom.neo.core.web.WebRequestContext;

    @Controller(path="/modulo/exemplo")
    public class ExemploController extends MultiActionController {

    	@DefaultAction
    	public ModelAndView padrao(WebRequestContext request){
    		return new ModelAndView("pagina1");
    	}
    	public ModelAndView acao1(WebRequestContext request){
    		return new ModelAndView("pagina1");
    	}
    	public ModelAndView acao2(WebRequestContext request){
    		return new ModelAndView("pagina1");
    	}
    }

Se pedirmos no browser: .../modulo/exemplo?ACAO=acao1 a requisição cairá no método acao1.
Se pedirmos no browser: .../modulo/exemplo?ACAO=acao2 a requisição cairá no método acao2.
Se pedirmos no browser: .../modulo/exemplo?ACAO=padrao a requisição cairá no método padrao.
Se pedirmos no browser: .../modulo/exemplo a requisição cairá no método padrao. Quando nenhuma ação é enviada o método escolhido será o anotado com @DefaultAction
É possível também indicar outro nome de ação para o método através da annotation @Action. Veja o exemplo:

    package teste;

    import org.springframework.web.servlet.ModelAndView;

    import br.com.linkcom.neo.controller.Action;
    import br.com.linkcom.neo.controller.Controller;
    import br.com.linkcom.neo.controller.DefaultAction;
    import br.com.linkcom.neo.controller.MultiActionController;
    import br.com.linkcom.neo.core.web.WebRequestContext;

    @Controller(path="/modulo/exemplo")
    public class ExemploController extends MultiActionController {

    	@DefaultAction
    	public ModelAndView padrao(WebRequestContext request){
    		return new ModelAndView("pagina1");
    	}
    	@Action("executar")
    	public ModelAndView acao1(WebRequestContext request){
    		return new ModelAndView("pagina1");
    	}
    	public ModelAndView acao2(WebRequestContext request){
    		return new ModelAndView("pagina1");
    	}
    }

Se pedirmos no browser: .../modulo/exemplo?ACAO=executar a requisição cairá no método acao1.
Se pedirmos no browser: .../modulo/exemplo?ACAO=acao1 a requisição cairá no método acao1.
Se pedirmos no browser: .../modulo/exemplo?ACAO=acao2 a requisição cairá no método acao2.
Se pedirmos no browser: .../modulo/exemplo?ACAO=padrao a requisição cairá no método padrao.
Se pedirmos no browser: .../modulo/exemplo a requisição cairá no método padrao.
Commands
As actions do MultiActionController também podem receber um segundo parâmetro chamado command. O command é um POJO com propriedades que são configuradas através dos parâmetros da requisição. Se o command for de uma classe que possua o atributo nome, e for enviado um parâmetro nome na requisição, o valor desse parâmetro será mapeado no atributo nome do POJO. Exemplo:

    package teste;

    public class MeuBean {

    	String nome;

    	public String getNome() {
    		return nome;
    	}

    	public void setNome(String nome) {
    		this.nome = nome;
    	}
    }

    package teste;

    import org.springframework.web.servlet.ModelAndView;

    import br.com.linkcom.neo.controller.Controller;
    import br.com.linkcom.neo.controller.DefaultAction;
    import br.com.linkcom.neo.controller.MultiActionController;
    import br.com.linkcom.neo.core.web.WebRequestContext;

    @Controller(path="/modulo/verificaNome")
    public class VerificaNomeController extends MultiActionController {

    	@DefaultAction
    	public ModelAndView executar(WebRequestContext request, MeuBean meuBean){
    		String nome = meuBean.getNome();
    		request.setAttribute("nome", nome);
    		return new ModelAndView("mostraNome");
    	}

    }

Se pedirmos no browser: .../modulo/exemplo?ACAO=executar&nome=joao a requisição cairá no método executar e irá configurar o atributo nome da classe MeuBean com o valor joao. A chamada meuBean.getNome() irá retornar joao.
Se não tivesse sido informado o parâmetro ACAO a requisição seria enviada para o método executar do mesmo jeito, já que ele está anotado com @DefaultAction.
Os atributos da classe não precisam ser do tipo String. O NEO irá converter os parâmetros para os tipos necessários. Para converter os parâmetros o NEO utiliza um DataBinder que extende o DataBinder do Spring. Esse dataBinder utiliza PropertyEditors para converter de String para o tipo necessário. O NEO já possui conversores para a maioria dos tipos de dados, mas se desejar informar outro conversor é necessário sobrescrever o método initBinder do MultiActionController. Exemplo:

    package teste;

    import java.beans.PropertyEditor;
    import java.util.Date;

    import javax.servlet.ServletRequest;

    import org.springframework.web.bind.ServletRequestDataBinder;
    import org.springframework.web.servlet.ModelAndView;

    import br.com.linkcom.neo.controller.Controller;
    import br.com.linkcom.neo.controller.DefaultAction;
    import br.com.linkcom.neo.controller.MultiActionController;
    import br.com.linkcom.neo.core.web.WebRequestContext;

    @Controller(path="/modulo/verificaNome")
    public class VerificaNomeController extends MultiActionController {

    	@DefaultAction
    	public ModelAndView executar(WebRequestContext request, MeuBean meuBean){
    		String nome = meuBean.getNome();
    		request.setAttribute("nome", nome);
    		return new ModelAndView("mostraNome");
    	}

    	@Override
    	protected void initBinder(ServletRequest request, ServletRequestDataBinder binder) throws Exception {
    		binder.registerCustomEditor(Date.class, new MyDatePropertyEditor());
    	}
    }

Sempre que for necessário mapear os parâmetro da requisição para um controller um binder é criado. O método initBinder é chamado passando-se uma referência desse binder para uma possível inicialização. Nesse caso estamos registrando um tradutor para o tipo Date.
View
A camada View no neo é na verdade um JSP. Após o controller realizar as tarefas necessárias ele indica para qual JSP deve ser continuada a requisição. Essa indicação é feita através de um objeto da classe ModelAndView.
A classe ModelAndView possui um construtor que recebe uma argumento do tipo String, esse argumento é o nome do JSP que deve ser utilizado. O nome informado é um nome lógico e deve ser transformado pelo NEO para se obter o nome real do JSP. O nome real do JSP é formado da seguinte forma:
/WEB-INF/jsp/ + o módulo onde está registrado o controller + nome informado do jsp + .jsp
Um controller registrado na path /gestao/controllers/AdicionarRecurso que retornasse
new ModelAndView("adicionarRecurso"); iria redirecionar para
/WEB-INF/jsp/gestao/adicionarRecurso.jsp.
JSP Base
Para evitar que o cabeçalho e o rodapé de todos os JSPs sejam copiados em cada página é possível utilizar o JSP base. O JSP base é uma página JSP que incluirá a página requisitada. Ao invés da requisição ir direto para a página indicada pelo ModelAndView ela irá para o base que incluirá no local desejado a página pedida. O JSP base deve ter o nome base.jsp e deve ficar dentro do diretório /WEB-INF/jsp/[módulo] onde [módulo] é o módulo onde o controller está registrado. Exemplo (suponha que o controller está registrado no módulo modulo1 e o retorno do ModelAndView foi hello):

    [JSP]
    <HTML>
    	<BODY>
    			<jsp:include page="${bodyPage}" />
    	</BODY>
    </HTML>

/WEB-INF/jsp/modulo1/base.jsp

    [JSP]
    Hello World!!!

/WEB-INF/jsp/modulo1/hello.jsp
Quando a requisição for redirecionada para o JSP ela será desviada para o base.jsp e o ele incluirá através do código <jsp:include page="${bodyPage}" /> o JSP requisitado. É como se tivéssemos apenas um JSP com o seguinte código:

    [JSP]
    <HTML>
    	<BODY>
    			Hello World!!!
    	</BODY>
    </HTML>

O NEO irá detectar a presença do base.jsp automaticamente. Não é necessária nenhuma configuração.
Resumo
No NEO, utilizamos o padrão MVC para responder às requisições. O controller no NEO é representado pelo MultiActionController e deve ser anotado com @Controller para informar em qual URL o controller responderá. A primeira parte da URL deve ser um módulo que esteja registrado no web.xml através do DispatcherServlet. O MultiActionController permite que várias Actions sejam escritas na mesma classe. Uma Action é um método reponsável por atender determinada requisição. Para escolher qual o método (Action) adequado, o MultiActionController verifica o parâmetro ACAO que veio na requisição. A Action pode ter um command, se tiver o MultiActionController mapeia os parâmetros da requisição aos atributos da classe command. A Action retorna um ModelAndView que representa qual JSP deve ser acessado. O NEO traduz o nome do JSP para o nome físico do JSP e faz o redirecionamento. Se existir um arquivo base.jsp para o módulo em questão será feito o redirecionamento para esse arquivo, e esse arquivo inclui o JSP desejado.