SOLID Layering
In the previous article, I introduced the idea that if your organization wants to be AGILE then your code needs to have the same agility that your business directive requires. I used an analogy to a coworker, and I think it fits quite well with what was written last week. Square peg, round hole, where you can make your business process silky smooth but if the code is still a square then you haven't addressed the underlining issue that will most likely lead to failure.
How do you start to make your code more adaptive and maintainable? By having clear separations within your code architecture. Those separations are commonly called layers and are crucial to setting up the foundation of a SOLID system. I will be going over what is commonly called a 3 layered architecture as it is most likely the scenario you are involved in. First you will have the presentation layer, sometimes called application layer, which is typically where all your UI elements are found. The second layer you will have is your business logic layer, this is where all your core business logic is found. I tend to think of this layer as the layer that makes my application different than yours even if we use the same UI system. The third and final layer you have is your domain layer. The domain layer is your persistent storage layer as it is where the majority, if not all, of your persisted state is stored.
The typical flow you will see (I am going to use MAUI/C#/DLL to show this concept) is that you setup your UI Layer as the application. I setup my MAUI project as SolidMaui so you the UI Layer is the MAUI application. The second layer, I will call Business Logic (super original!) and here is where I would like to store all of my logic that makes my application special. Then finally, I bet you can guess it, the domain layer and that's where I store any persisted data. It looks like the following:
The next steps seem arbitrary but are important to note. You will first say, "SolidMAUI, you get everything," so it is dependent on all three, because it's my application layer and all my dependencies will end up there. Next, business logic layer knows nothing about UI layer therefore, it has no dependencies on SolidMAUI but instead knows about Domain only. Domain has zero dependencies on anything and thus is the bottom of the layering.
Domain has no project references other than itself so there is nothing to show for that piece of the puzzle.
This is where a lot of people stop and just start coding things in each layer, which is understandable; however, you have not resolved any real dependency issues. As it is at the moment, Business Logic cannot be independent of any Domain dependencies. Example: if I chose to use MongoDB for my domain layer, then the entire Business Logic is also dependent on MongoDB, by the sheer fact that Business Logic is dependent on the entirety of the Domain project. If I chose to use 3 different databases, the problem is amplified and now my business logic layer is dependent on all three of those libraries. Thus, potentially making it more difficult to make domain changes, which to the naked eye, has no dependencies. How can we break that up better, so we don't have such hard dependencies for each layer?
First thing to keep in mind is UI layer should not have direct access to Business Logic (outside of DI instantiations) implementations, Business Logic should not have direct access to Domain layer implementations, and finally Domain has no access to any of the above layers as it is meant to be completely independent. The way to achieve this is by placing an interface layer in between each set of libraries. Once that layer is established then the only access each layer has is to the interfaces, which protect you from the implementation dependencies of each layer. The new list of files for each is as follows:
As you can see, especially in the Business Logic layer, it has no clue about what you have implemented at the UI level or the domain level, which is ideal since that layer really doesn't want to inherit any dependencies from the Domain or UI layer. The main concept here is each layer has extremely limited access so that each layer can operate independently. The new layer system would look like the following diagram:
The key takeaway from this is the following: by each layer being dependent on the interfaces, I have now enforced dependency rules at a library level. Now when defining interfaces, I am able to keep them agnostic of any specific layer details. The next article will go over the actual ins and outs of how to achieve this in a more code-centric way; however, with this infrastructure intact, we would be able to work on any layer at any time without worrying about dependencies leaking erroneously into layers they were never intended to be in.