5

I am currently working on a software using C# .NET and WPF with the MVVM pattern.

Though the software is almost done, I have been discussing our architecture for a long time and I would like to get your opinions/advices on the convention/good practices side.

The current architecture is as follows:

  • Project/
    • DataContext/
    • Models/
      • Engine3D/
      • ...
    • Utilities/
      • Converters/
      • Statics/
    • ViewModels/
      • Grids/
      • Menus/
      • Windows/
      • ...
    • Views/
      • Grids/
      • Menus/
      • Windows/
      • ...

Models folder:

Contains the Models from the MVVM pattern.

The Models folder is organized by feature, meaning that it has nested folders named after a feature or context, for example Models/Engine3D for every Model related to 3D.

We never append the "Model" word as a suffix, the files are named [Feature].cs For example, Project.cs, Model3D.cs, Animation.cs, etc...

Views folder:

Contains the Views from the MVVM pattern.

The Views folder is organized by WPF Control, meaning that it has nested folders named after a WPF Control, for example Views/Windows for every View related to a WPF Window, or View/Grids, etc...

The files are named [Feature][ControlType].xaml with:

  • [Feature]: The "feature" implemented/described by the View, for example "CreateProject" for a feature creating a Project, Display3DModel for a Grid displaying a Model3D (using 3D libraries), etc...
  • [ControlType]: The type of "xaml" control represented by the file, being a Window, a UserControl, a Grid, etc... As an example, a Window displaying the System Preferences would be called SystemPreferencesWindow.xaml, and a Grid holding the list of all the Projects would be ProjectsListGrid.xaml. For example, CreateProjectWindow.xaml, SystemPreferencesWindow.xaml, Display3DModelGrid.xaml, ProjectsListGrid.xaml

ViewModels folder:

Contains the ViewModels from the MVVM pattern.

The ViewModels folder copies the Views folder in terms of architecture, meaning that it has nested folders named after a WPF Control, for example ViewModels/Windows for every ViewModel related to a WPF Window, or ViewModels/Grids.

A ViewModel file has the same name as its related View file, with the suffix "ViewModel": the files are named [Feature][ControlType]ViewModel.cs with:

For example, based on View => ViewModel: CreateProjectWindow.xaml => CreateProjectWindowViewModel.cs, OpenProjectWindow.xaml => OpenProjectWindowViewModel.cs, etc...

DataContext folder:

Contains the DataContext files, ie. files related to a certain context in the software; these are classes that are neither ViewModels or Models, are mostly accessible "staticly" and correspond to a very specific context in the software execution flow so don't really belong in the Utilities folder. These classes are for example the Session class holding session values concerning the user like the web technologies' sessions.

Utilities folder:

Contains a set of files/classes tools of various goals :

  • Definition classes, giving system mechanisms to the overall software such as ObservableObject.cs, DelegateCommand.cs and RelayCommand.cs which permit MVVM data binding, etc...
  • Utilities/Converters folder: Converters classes...
  • Utilities/Statices folder: Contains static operations classes taking an input and returning an output, for example FileOperations.cs, XWindow.cs (don't bother with the name, it's project's specific) that regroups System.Windows operations like InvokeWindow() and CloseWindow() so that it's easier to manipulate System.Windows in the code, Model3DImporter.cs which imports 3D Models very easily, SerializationManager.cs for serialization operations, etc...

Classes with constants:

Some classes need constants values and since these values must be accessed from pretty much everywhere in the code, we didn't store these constant values in the related classes but in a "sister" class.

These "sister" classes are as follows:

// The sister class
public static class Model3DConstants
{
    private static readonly IList<String> supportedFileExtensions = ...
}

// The original class
public class Model3D
{
}

As I mentioned in the beginning of my message, I would like your ideas/advices on our architecture/naming conventions, because I am sure that we are missing something out but don't know what.

Are we doing the complete opposite of what should be done in a correct C# .NET MVVM software using WPF, what are the flaws and/or stuff that we should modify

Do not hesitate to participate in the debate neither or even ask me questions if I am unclear about the above description.

  • Seems fine to me. Don't know what you feel like you are missing.. – cwap Oct 13 '15 at 13:47
  • You should think about using multiple projects in future rather than including everything in one large project, this allows you to enforce the separation between Model, ViewModel and View based on the project references as well as make use of the internal modifier. – ndonohoe Oct 13 '15 at 13:52
  • Hi @bonyjoe, thanks for the advice ! We are already using multiple projects (three at the moment) and the one I described above is the "main" project, getting the .dll from the two other projects. What do you mean by internal modifier ? – Rashimov Azarov Oct 13 '15 at 13:57
  • 2
    @cwap I've read a few times that it would be better to sort your folders by feature and not MVVM type, ie replacing Models/Views/ViewModels with the feature Projects/Engine3D/etc... That's why I'm a little bit confused from time to time, because it seems there is no concrete convention about an optimal architecture/design for a software. – Rashimov Azarov Oct 13 '15 at 13:57
  • 1
    Generally it would make sense to also split up your model, view and viewmodel objects into separate projects too that way your model and view model don't need to know that wpf exists and your view doesn't need to know that the model exists. Internal is the access modifier that restricts access of classes etc to the current project. Splitting up work based on feature would be beneficial if you were planning to reuse certain features elsewhere, but it really depends on how your team works best. – ndonohoe Oct 13 '15 at 14:03
6

This may not answer everything you are asking, but it's too much for a comment.

Your solution's organization (I use the term organization rather than architecture) should reflect how you use it. It should be efficient for your use. For instance, when you want to make a change to a Menu does a developer have to open files from View/Menus and also from ViewModels/Menus to make the change? (This is inefficient.) Or do you have designers who only ever open Views, and developers who only ever open ViewModels?

This is why single team projects might prefer a feature-oriented approach (with related Views and View Models in the same feature folder) whereas large projects with multiple teams might prefer a component-oriented approach like your current structure. (This may also be the reason for Conway's law.)

I make a distinction (at least mentally) between Utilities and Infrastructure. Infrastructure code is a core part of your application's inner workings that is used by policy (e.g. "We always use RelayCommand on these WPF controls." -- UI Infrastructure). Whereas Utilities are generally useful purpose-built functions like perhaps FileOperations. I'm not suggesting that you create a new Infrastructure project or rename anything. Just make sure that all your "must use" components are in a highly accessible place, which it sounds like they are.

Utility code is at greatest risk for being forgotten or ignored. So make sure that what you put there is truly useful and time-saving. If you pollute it with a lot of minor helpers, other developers will not take the time to familiarize themselves with it and miss the opportunity to save time.

And then there are Helpers which are what I deem reusable code which is helpful across a few specific components. Maybe it's shared code used for related features. I generally keep these close to the calling code if possible and not in Utilities. This way, the code is not far off when a developer needs to work on that feature, and it doesn't add cruft to the more highly-reusable Utilities.

I look at it like: developers must be familiar with Infrastructure, should be familiar with Utilities, and may be familiar with a given feature's Helpers (if they work on that feature).

  • Thanks a lot for your message, it is very interesting ! Why did you edit it ? The second part was also interesting. Anyway, thank you, I always made a difference between Utilities and Infrastructure in my mind but you confirmed it. – Rashimov Azarov Oct 14 '15 at 11:58
  • I figured it was too much diatribe about utilities, but I can roll the edit back (I think). – Kasey Speakman Oct 14 '15 at 13:08
  • Well that's a lot of diatribe as you say, but not too much ; it was really interesting because your were talking about pure naming/usage conventions, something not that much clearly explained on the outer web ! It might help other people reading this post too, so I'd advise you to keep it :) Thanks anyway, that was a very good answer from a good point of view. – Rashimov Azarov Oct 14 '15 at 14:13
  • I added it back. :) – Kasey Speakman Oct 14 '15 at 14:16
2

The structure of a project should reflect the purpose and intend of the project.

From you question, and from your project structure, I have no idea what the purpose and intend of your project is. But if would shed some light on what the project is about I am sure I can give some suggestions for a directory structure.

By having a Views directory, a ViewModels directory and a Models directory, you are organizing your code according to the roles of classes in the MVVM pattern. This structure by design pattern makes your project look like an example project in how to use the MVVM pattern.

A project should be modular.

Unfortunately, the roles-in-a-design-pattern structure prevents modularity, i.e. you can't have AwesomeModel, AwesomeView and AwesomeViewModel together in an Awesome directory (and what if you needed a special AwesomeCollection class too?).

A project structure should scale.

For a project structure to scale it should allow for the project to have more classes without appearing more messy. A ViewModels directory with 20 view-models in it is a mess, so this also speaks against the roles-in-a-design-pattern structure.

A scalable project structure cannot be a static bunch of directories.

However, putting away the generic details into some Shared, Common or Utilities directory greatly helps. Have your home-grown frameworks there. Should you end up having 10 or more subdirectories in the Utilities directory, so be it, at least they don't clutter the root directory. Try not to overdo this, because what goes into the Utilities directory is what becomes the hardest to get rid of. While some utility class matures to be used in more than one application, it might as well move in with it's initial application.

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.