Por coincidência, estou trabalhando em um projeto WinForms padronizado após o MVC. Eu não chamaria isso de uma resposta perfeita, mas explicarei meu design geral e espero que isso possa ajudá-lo a criar o seu próprio.
Com base na leitura que fiz antes de iniciar este projeto, parece não haver uma maneira "certa" de implementar isso. Segui os princípios simples de OOP e MVC, e o restante foi tentativa e erro ao desenvolver um fluxo de trabalho.
O MVC é simplesmente uma arquitetura inadequada para este caso de uso?
Não ..? Não há contexto suficiente em sua pergunta para fornecer uma resposta direta a isso. Por que você está usando o MVC em primeiro lugar? Quais são os requisitos não funcionais do seu projeto? Seu projeto terá muita interface do usuário? Você se preocupa mais com segurança e prefere uma arquitetura em camadas? Quais são os principais componentes do seu projeto? Talvez cada componente precise de um padrão de design diferente. Descubra por que você deseja usar esse padrão de design em primeiro lugar e você pode responder sua própria pergunta;)
Minha razão para usar o MVC: é um padrão de design bastante simples de entender na minha opinião e meu design é fortemente baseado na interação do usuário. A maneira como o MVC permite que o desenvolvedor separe preocupações também é suficiente para o meu aplicativo. Isso torna meu código muito mais sustentável e testável.
Suponho também que estou usando mais um design híbrido. Geralmente, o conceito ideal apresentado na engenharia de software não se efetua na prática. Você pode modificar o design para atender às necessidades do seu projeto. Não há necessidade de se envolver no que é certo ou errado. Existem práticas gerais, mas as regras sempre podem ser dobradas ou quebradas, desde que você não atire no próprio pé.
Minha implementação começou com um design de alto nível que me deu uma idéia de quais componentes serão necessários. É melhor começar dessa maneira e avançar na arquitetura. Aqui está o diagrama do pacote para o projeto (criado no StarUML):
Observe que todas as camadas, exceto a de apresentação, dependem do sistema de mensagens. Essa é uma "linguagem" comum que as camadas inferiores e os subsistemas dessas camadas usam para se comunicar. No meu caso, era uma enumeração simples baseada em operações que podem ser executadas. O que me leva ao próximo ponto...
Pense em operações ou comandos como base para sua implementação. O que você deseja que seu aplicativo faça? Divida-o nas operações mais fundamentais. Por exemplo: CreateProject, WriteNotes, SaveProject, LoadProject etc. A GUI (ou classes de formulário) terá algum evento (como pressionar um botão). Toda operação possui um método de controlador associado. Nesse caso, algo como Exit é muito simples. O aplicativo pode ser simplesmente fechado a partir da classe Form. Mas suponha que eu queira persistir alguns dados do aplicativo em um arquivo primeiro? Vou chamar o método "Salvar" da respectiva classe de controlador no meu método de pressionar o botão.
A partir daí, o controlador chamará o conjunto correto de operações das classes de Serviço. As classes de serviço no meu aplicativo funcionam como uma interface para a camada de domínio. Eles validarão a entrada recebida da chamada do método do controlador (e, portanto, da GUI) e manipularão o modelo de dados.
Após a conclusão da validação e da manipulação do objeto correspondente, o método de serviço retornará um código de mensagem para o controlador. Por exemplo MessageCodes.SaveSuccess
,. O controlador e as classes de serviço foram baseadas nos objetos de domínio e / ou no conjunto geral de operações que podem ser agrupadas.
Por exemplo: FileMenuController
(operações: NewProject, SaveProject, LoadProject) -> ProjectServices
(CreateProject, PersistProjectToFile, LoadProjectFromFile). Onde Project
seria uma classe de domínio no seu modelo de dados. As classes Controller e Service, no meu caso, eram classes não instanciadas com métodos estáticos.
Em seguida, o controlador reconhece a operação como concluída sem êxito. Agora, o controlador possui seu próprio sistema de mensagens que ele usa para interagir com a camada de apresentação, daí a dupla dependência entre as camadas Serviço e Apresentação. Nesse caso, uma classe chamada ViewState
no ViewModels
pacote é sempre retornada à GUI pelo controlador. Esse estado contém informações como: " é o estado em que você tentou colocar o aplicativo válido? ", " Uma mensagem legível por humanos sobre a operação que você tentou executar e por que teve ou não êxito (mensagens de erro) " e uma ViewModel
classe.
A ViewModel
classe contém dados relevantes da camada de domínio que a GUI usará para atualizar a exibição. Esses modelos de exibição se parecem com as classes de domínio, mas no meu caso eu usei objetos muito finos . Basicamente, eles praticamente não têm comportamento, apenas retransmitem informações sobre o estado de nível mais baixo do aplicativo. Em outras palavras, NUNCA vou distribuir minhas classes de domínio para a camada de apresentação. É também por isso que os pacotes Controllers
e Services
dividem a camada de serviço em duas partes. Os controladores nunca manipularão classes de domínio ou validarão seu estado. Eles apenas atuam como um limite, convertendo dados relevantes da GUI em dados relevantes do domínio que os serviços podem usar e vice-versa. Incluir a lógica de serviço no controlador levaria a muito gordura controladores, que são mais difíceis de manter.
Espero que isso lhe dê um ponto de partida.