Layers, MVVM and organizing the Code into Projects
According to the PluralSight Course “Getting Started with Dependency Injection in .NET” by Jeremy Clark, a desktop application can be structured in four layers.
- The View Layer contains the UI elements of the application such as buttons and list boxes.
- The Presentation (Logic) Layer contains the business logic that drives the application and controls the View Layer.
- The Data Access Layer contains code that interacts with the data store. A datastore can be a WSDL-WebService, a REST API, a database or any other data provider.
- The Data Store provides the data.
After defining the four layers, Jeremy goes on to map those four layers to the MVVM pattern. MVVM stands for Model – View – ViewModel.
- The View Layer maps to the View of the MVVM pattern.
- The Presentation Logic Layer maps to the ViewModel of the MVVM pattern.
- The Data Access Layer and the Data Store both map to the Model of the MVVM pattern.
Jeremy uses one separate C# project in the solution per Layer and one project for common code shared amongst the layers. In total the solution contains five projects.
The project of Jeremy’s Data Store Layer contains a ASP.NET Core project that provides a dummy REST-API. I can imagine that there are situations in which the Data Store Layer project does not add any additional benefit to your specific use case and I would go on to say that the Data Store Layer project is optional.
The Data Access Layer
The Data Access Layer has to retrieve or send data to and from the data store. The integration with the data store and the data format used should be of no concern to any of the upper layers. The Data Access Layer shields the upper layers from the details of communication and integration as best as possible.
One way to achieve this separation is that the Data Access Layer talks to the upper layers via domain objects and it has the responsibility to convert to and from those domain objects, when it talks to the datastores.
The Data Access Layer contains ServiceReader interfaces and classes that implement those interfaces. The interfaces define an API that uses domain objects. Internally a ServiceReader implementation uses a WebClient to talk to REST APIs or a SSH- or TelnetClient to speak those protocols.
It also contains converters. Converters are generic interfaces and implementations that have the task to convert from and to domain objects. They contains and isolates the mapping logic to convert between domain objects and the form of serialization that is used to talk to the datastore at hand. A converter can also be unit tested easily. Converters can be nested to deal with complex data structures.
Data is retrieved from the data store via the client, put into the converter to retrieve domain objects and then the domain objects are returned via the ServiceReaders API. Sending data to a data store also goes through the API, a converter and finally a client.
If JSON is the method of serialization, you can use NewtonSoft Json. For WSDL webservices use ???. For Telnet use ??? For SSH use ??? For HTTP you should use a modern asynchronous HTTP client (Which one ???)
The Presentation Layer
Contains ViewModel classes (e.g. EmployeeViewModel, PeopleViewModel, …) that implement INotifyPropertyChanged and other interfaces used to connect them to the View Layer.
The ViewModel classes contain properties (= members) that are databound to the View Layer.
The ViewModel classes finally contain ServiceReader – Interface member variables that get the ServiceReaders Implementations from the Data Access Layer injected.
The View Layer
The view layer contains GUI descriptions in .xaml files. The UI components defined in the .xaml files are backed by window classes. The UI components are bound to properties of their respective windows classes.
The data that the windows classes provide to the GUI components via their bound properties comes from the Presentation Layer’s ViewModel classes. The ViewModel objects are injected into the window classes.