Notes
An alternative to n-layer applications architectural style is to use the CQRS pattern. Not too sure how this might be used for building applications instead of the n-layer architecture. With the n-layer architecture, such as a 3 layer application, it is easy to visualize this. There would be a data access layer, domain layer, and UI layer. The data access layer will be responsible for retrieving data from the database. I.E, this can be data models that can be used to retrieve data from the database directly. The domain layer would be responsible for massaging these data models into domain models or composing multiple services together to get information.
An example of a data layer would be a Car
model that would be represented in the database in a Car
table. The domain layer can then be responsible for using the Car
model and linking it up with a Buyer
model to find for example a list of buyers who bought a specific type of car. This will then be presented back up to the UI layer as a view model to be presented in the webpage.
Instead of passing the data model back up to the UI, it is preferable to always create a UI model or a view model. This would allow us to add properties into our data model without worrying what consumers of the existing data model would have to do. Having a UI model would also allow us to add UI only properties without polluting the data model.
Inversion of control
Dependency injection is a subset of inversion of control. What does it mean with inversion of control? The class gives up control of the dependencies and instead uses interfaces. This would allow the developers to inject the services into the classes. In an n-layer application, we can push the burden of registering services all the way to the top of the application or the composition root
. The composition root
for a .NET 5 API project would be the Startup.cs file where the services gets registered.
Presentation layer
The presentation layer can be inserted into an application between the User Interface Layer and the Domain Model layer. This would allow our application to have the following pattern:
UI Layer -> Presentation Layer -> Domain Layser -> Data Layer
Why do we want a presentation layer?
Having a presentation layer separte from the UI layer would allow us to provide a clear separation of concerns between the presentation logic (how a UI behaves) and UI logic (how a UI looks). An example of a presentation logic is to have a readonly field for certain properties such as ID
. UI logic can be responsible for the CSS and any flashy designs. I think this is represented nicely in our MVC controllers. The view itself can be thought of the UI layer that’s responsible for showing the data. The presentation layer might be the controllers where the UI logic resides. Depending on route arguments, we can then for example decide how the UI behaves from the controller.
Code smells
If your UI layer is dependendent on the data layer, there might be something off with the way the application is structured. With a 3 layer architecture or onion layered architecture, the dependencies should be as follows: UI -> Domain -> Data. The UI layer should not depend on the data layer at all and should be able to only use the Domain layer. If the UI layer depends on both the Domain and the Data layer, it makes it more difficult to then change the Data layer. Let’s say data is stored in the database, say SQL. If we need to change the database to be NoSQL instead, the whole application will need to change because the UI layer also depends on the data layer.
In a true 3 layer architecture, the UI layer would not need to change because it only depends on the Domain layer.
Learning reinforcements
Single Responsibility Principle - The single responsibility principle states that a class should only be responsible for one part of the program’s functionality. Essentially this means that we should not be creating a class that tries to do too many things at once. It would make it extremely hard to then debug and maintain if a single class is responsible for everything. An EmailService
should be responsible for e-mail functionality for example and should not also contain a method to Call
someone. Calling someone can be done in the TelephoneService
.
Test driven development - Perhaps when writing interfaces, we should be creating unit test
We should always be creating repository
classes to retrieve data from the data layer and then services
classes that makes use of the aforementioned repository
classes.