Per coincidenza, sto lavorando a un progetto WinForms che è modellato su MVC. Non la definirei una risposta perfetta, ma spiegherò il mio design generale e spero che questo possa aiutarti a inventare il tuo.
Sulla base della lettura che ho fatto prima di iniziare questo progetto, non sembra esserci un modo "giusto" per implementarlo. Ho seguito semplici principi di progettazione OOP e MVC e il resto è stato tentativi ed errori mentre ho sviluppato un flusso di lavoro.
MVC è semplicemente un'architettura inappropriata per questo caso d'uso?
No ..? Non c'è abbastanza contesto nella tua domanda per fornire una risposta diretta a questo. Perché stai usando MVC in primo luogo? Quali sono i requisiti non funzionali del tuo progetto? Il tuo progetto sarà molto pesante nell'interfaccia utente? Ti interessa di più la sicurezza e preferiresti un'architettura a più livelli? Quali sono i componenti principali del tuo progetto? Forse ogni componente necessita di un modello di progettazione diverso. Scopri perché vuoi utilizzare questo modello di progettazione in primo luogo e potresti rispondere alla tua domanda;)
La mia ragione per usare MVC: è un modello di progettazione abbastanza semplice da capire secondo me e il mio design si basa fortemente sull'interazione dell'utente. Il modo in cui MVC consente allo sviluppatore di separare le preoccupazioni è sufficiente anche per la mia applicazione. Questo rende il mio codice molto più gestibile e testabile.
Suppongo anche che sto usando più di un design ibrido. Di solito, il concetto ideale presentato nell'ingegneria del software non si concretizza nella pratica. Puoi modificare il design in base alle esigenze del tuo progetto. Non c'è bisogno di lasciarsi coinvolgere da ciò che è giusto o sbagliato. Ci sono pratiche generali, ma le regole possono sempre essere piegate o infrante fintanto che non ti spari in un piede.
La mia implementazione è iniziata con un design di alto livello che mi ha dato un'idea di quali componenti avrò bisogno. È meglio iniziare in questo modo e scendere nell'architettura. Ecco il diagramma del pacchetto per il progetto (creato in StarUML):
Notare che ogni singolo livello tranne quello di presentazione dipende dal sistema di messaggistica. Questo è un "linguaggio" comune che gli strati inferiori e i sottosistemi di quegli strati usano per comunicare tra loro. Nel mio caso, era una semplice enumerazione basata su operazioni che possono essere eseguite. Il che mi porta al punto successivo ...
Pensa alle operazioni o ai comandi come base per la tua implementazione. Cosa vuoi che faccia la tua applicazione? Suddividilo in operazioni fondamentali. Ad esempio: CreateProject, WriteNotes, SaveProject, LoadProject ecc. Alla GUI (o alle classi Form) si verificherà un evento (come la pressione di un pulsante). Ad ogni operazione è associato un metodo controller. In questo caso qualcosa come Exit è troppo semplice. L'applicazione può essere semplicemente chiusa dalla classe Form. Ma supponiamo che prima volessi conservare alcuni dati dell'applicazione in un file? Chiamerò il metodo "Salva" dalla rispettiva classe del controller all'interno del mio metodo di pressione del pulsante.
Da lì, il controller chiamerà il set corretto di operazioni dalle classi di servizio. Le classi di servizio nella mia applicazione fungono da interfaccia per il livello di dominio. Convalideranno l'input ricevuto dalla chiamata del metodo del controller (e quindi dalla GUI) e manipoleranno il modello di dati.
Una volta completata la convalida e la manipolazione dell'oggetto corrispondente, il metodo di servizio restituirà un codice messaggio al controller. Ad esempio MessageCodes.SaveSuccess
,. Sia il controller che le classi di servizio erano basati sugli oggetti di dominio e / o sull'insieme generale di operazioni che possono essere raggruppate insieme.
Ad esempio: FileMenuController
(operazioni: NewProject, SaveProject, LoadProject) -> ProjectServices
(CreateProject, PersistProjectToFile, LoadProjectFromFile). Dove Project
sarebbe una classe di dominio nel tuo modello di dati. Le classi Controller e Service nel mio caso erano classi non istanziabili con metodi statici.
Quindi, il controller riconosce l'operazione come completata in modo non riuscito. Ora, il controller ha il proprio sistema di messaggistica che utilizza per interagire con il livello di presentazione, quindi la doppia dipendenza tra il livello di servizio e quello di presentazione. In questo caso, una classe chiamata ViewState
nel ViewModels
pacchetto viene sempre restituita alla GUI dal controller. Questo stato contiene informazioni come: " lo stato in cui hai provato a mettere l'applicazione in corso di validità? ", " Un messaggio leggibile dall'uomo sull'operazione che hai tentato di eseguire e perché ha avuto o meno esito positivo (messaggi di errore) " e una ViewModel
classe.
La ViewModel
classe contiene dati rilevanti dal livello di dominio che la GUI utilizzerà per aggiornare la vista. Questi modelli di vista sembrano le classi di dominio ma nel mio caso ho usato oggetti molto magri . Fondamentalmente non hanno praticamente alcun comportamento, semplicemente trasmettono informazioni sullo stato di livello inferiore dell'applicazione. In altre parole, non regalerò MAI le mie classi di dominio al livello di presentazione. Questo è anche il motivo per cui i pacchetti Controllers
e Services
dividono il livello di servizio in due parti. I controller non gestiranno mai le classi di dominio o convalideranno il loro stato. Agiscono semplicemente come un limite per convertire i dati rilevanti della GUI in dati rilevanti per il dominio che i servizi possono usare e viceversa. Includere la logica di servizio nel controller porterebbe molto grasso controller, che sono più difficili da mantenere.
Spero che questo ti dia un punto di partenza.