Before we can add a lot of new features, we need to do some refactoring. Think of refactoring like a chef cleaning their workspace before starting to make a new dish.
In this lesson, we’re only going to make the new (empty) projects we’ll need, but not make any other changes. Most of this lesson will cover how I identify the need for refactoring, and what I think will be a better structure for the future.
Step 1: Review the current structure
Currently, we have two projects: WPFUI and Engine.
The Engine project contains all the logic, but in several namespaces (Models, ViewModels, Services, etc.)
The WPFUI project has a dependency on the Engine project. We can see this by looking at the project references/dependencies.
Because WPFUI has a reference to Engine, it can use the public classes inside the Engine project.
All the public and internal classes inside the Engine project can see, and use, each other.
Step 2: Identify the problem
Because all our logic classes have access to each other, it’s possible that we can create two-way dependencies. The GameSession object (in the ViewModels namespace) can access/modify classes in the Services namespace. However, classes in the Services namespace can also access/modify the GameSession object.
This can lead to a problem where we make a change to the GameSession class and need to also make a change to the Services classes.
If we put the ViewModel classes into their own project, and the Services classes into their own project, we can eliminate two-way dependencies and force one-way dependencies – which will make our code easier to work with.
I use NDepend to identify code problems. One of the many things it identifies is these dependencies. It’s an expensive program, but one I like to use.
However, you don’t need to pay for a program. What I normally do for personal projects is start the solution by building the projects that I’ll show in the next step. This forces you to write code that doesn’t have these two-way dependencies.
Step 3: The new structure
My standard solution structure is to start with these four projects:
UI (WPF, WinForms, Web, etc.)
Then I set the project references/dependencies to enforce the architecture I like.
With this structure, The UI can see models (for binding to the UI) and has to do everything else through the ViewModels – since the UI project only have references to the Models and ViewModels projects.
Also, the Models can’t do anything other than store data in properties and raise events. Model classes can’t call services or modify the ViewModel. This forces us to keep all the program’s logic in the ViewModel or Services classes.
Step 4: Create the new projects
Add three new projects to the solution. These are all “Class Library” projects.
Delete the default Class1.cs files from these new projects.
Step 5: Add the new project references/dependencies
Finally, add the project references for the new projects.
- WPFUI to SOSCSRPG.Models
- WPFUI to SOSCSRPG.ViewModels
- ViewModels to SOSCSRPG.Models
- ViewModels to SOSCSRPG.Services
- Services to SOSCSRPG.Models
Now we’re ready to start moving classes into the new structure.
However, we will need to decouple many existing two-way dependencies between classes in different namespaces. I’m going to do that in small steps, instead of one huge refactoring.
Let me know if you have any questions, although you might want to watch the next lesson or two. That will (hopefully) show how much cleaner the new structure will be.
PREVIOUS LESSON: Lesson 19.1: Converting SOSCSRPG to .NET 5