Now that we have the excellent ASP.NET MVC we might not be visiting the aspx much anymore. One huge benefit of the ASP.NET MVC is the testability. The HTTPContext caused a real problem with its singleton behaviour in ASP.NET. To make TDD much more useful in the aspx world we can use the Model View Presenter technique. There are a couple of dialects to this depending on how strictly you adhere to the separation of the objects, but in general their purpose is described in the diagram below. The yellow blocks are the main players of MVP. For more info about the dialects we can see Martin Fowler's thoughts.
Looking from the bottom-up:
1. I use the Entity Framework to create the OO abstraction of the underlying database.
2. As great as these classes are, they are not always representative of the Domain objects - the Business language and concepts used in the Business - so we need to convert/translate (map) them to Domain Entities.
3. From the Domain Entities we often need simplified entities for the aspx pages. So, I think of the Domain Entities as a Domain Model, which is full and complex and complete. I create simplified Models for the Views and call them the View Model. We are now in the MVP part of the action.
I'm not talking much about MVP at the moment, but I'll get there. Anyway, a quick summary of the evolving Entity from database to screen.
- At the bottom of the system is the Table in database, e.g. Car.
- Entity Framework creates a Car class at design time. This class is hidden from the user of the system in the Repository object.
- The Business Domain entity, Car, this is retrieved from the Repository. A mapper class exists inside the workings of the Repository that takes the Entity Framework's Car and converts it to the Business Domain's Car.
- The car class in the View Model. This is the object to be displayed on the web page. Again a Mapper will convert/map/translate from the Domain's Car object to one suitable for the View (web page).
The aspx code behind class implements the ASPX View. That is, it is a concrete View. The view of the MVP pattern is actually an Interface defining the behaviour of the concrete view. This separation is excellent for testing. We can create a test object that implements the interface that will not be dependent on the HttpContext. A devoted View Model for that page provides data to one View.
There are two practical and high level questions that need to be asked and answered:
1. How do I divide this up into separate Projects in the Solution, and
2. Is this all necessary.
Is this all necessary
Well, for the small project the answer is definitely no. I would probably keep the MVP and Entity Framework, but I would ditch the Domain Entity objects unless the database tables and the subsequent EF classes do not represent the Business well enough.
How do I divide this up into separate Projects
Project for the web-site.
Namespaces:
- Project
Project for the MVP.
These are all related to the views, the web pages
Namespaces:
- Project.MVP.Models,
- Project.MVP.Views,
- Project.MVP.ViewModels
- Project.MVP.ViewModels.Builders
- Project.MVP.ViewModels.Mappers
Project for the Domain
These are all related to the Domain objects, the domain layer. Conceptually they are between the Web page stuff and the Repository stuff. They hold the Business meaning, the model of the Business.
Namespaces:
- Project.Domain.Commands
- Project.Domain.Entities
- Project.Domain.Repositories
- Project.Domain.Repositories.Mapping
Project for the Data
Namespaces:
- Project.Data
This can of course be simplified by using fewer actual projects, and using folders and namespaces to separate the files. It can all change during development. My advice would be to think what you want, do it, change it when you evolve the development and discover possible improvements. Using the refactor feature in Visual Studio to rename classes, and using the context menu's rename option in the Solution explorer to rename files and their constituent classes (right-click on the filename), will make doing big structural changes easier. Building the Solution will find the breaks from any re-engineering.
Practical point to note. If, in a given example, the database table entity is named Cars then Entity Framework will produce a class named Car and the collection will be Cars. If you then translate this using a mapper class to a Domain Entity object, and thence onto an object in the ViewModel for the aspx page, will there be any confusion if give the name Car to the objects in those layers?
How do you want to handle that?
Package diagram indicating how most of the classes are separated. |
Key to becoming familiar with the architecture
People who are new to this stuff can, with difficulty, pick this stuff apart and try to copy it parrot-fashion. But what is key to understanding this and making up your own improvements, is to know why each design block in the diagram exists. If you are writing your unit tests and there is some bug or block to your development, it is crucial to be able to vocalise to yourself what this object is supposed to be doing, what is it responsible for.
There are two levels to understanding the architecture. 1, is understanding the objects, and 2, is understanding why we are using interfaces. The interfaces are probably secondary to the understanding and are pretty much there for testing purposes. To aid in the unit testing.
As an example, lets say there is a Default.aspx page. (This is really a class named _Default.)
_Default - this is the View. The concrete class that is responsible for displaying things to the user and handling the user's button presses etc. No hard-lifting should go on in this object.
This class inherits from two classes. An interface class, IView and an abstract class, Viewbase.
IView, this is very useful for unit testing.
Viewbase, the use of this is debatable and usually doesn't do a lot. It is a good candidate to leave out.
DefaultPresenter - this is the "controller". The View (web page code file) creates the Presenter and the Presenter is aware of who his creator was (the View). The Presenter will create a ViewModelBuilder and tell it to create the ViewModel. The ViewModel is then given over to the View.
The Builder uses the Mapper to translate the fields from the Repository's Entity (e.g. Car) to the Entity use in the ViewModel. Remember that the View Model may well have fewer data fields than the Domain Entity, or perhaps concatenated data. The View Model is designed specifically for this particular View's needs.
Class structure for MVP. (Including Repository class from the Domain project). |
No comments:
Post a Comment