| The following is a high level view of the layers of your application: |
| We'll discuss these four layers in Part III. They are listed here to provide context to some of the basic rules of design mentioned below. |
| |
| Foundation |
| Data Access Layer |
| Business Logic Layer |
| User Interface Layer |
| |
| Here is a short list of 6 basic rules you can follow to help address the assumptions from Part I |
| |
| 1. | Every non-UI rule, no matter how small, should be reusable in another .NET Windows Forms, .NET Compact Framework, or ASP.NET Application In some instances, you should even consider how the rule might be used in a .NET Windows Service or how much effort would be required if this rule eventually needed to be accessible via a .NET Web Service. .NET Web Services should have no business logic in the methods exposed to the caller. .NET Web Service methods should always call exposed business logic methods in the business logic assemblies to enable the same logic to be made accessible over the web as well as for local environment reuse. |
| |
| 2. | Every UI rule, no matter how small, should be reusable across any form or dialog. You should almost always create a class or classes in their own assembly that can be reused across applications in similar environments. It is also suggested that you separate resuable classes that are application generic from those that are application family specific. |
| For example, let's say you have a third party DataGrid component. Rather than place business or conditional logic in the events of the grid, you should have methods in your reusable classes that access objects associated with the DataGrid such as a cell or one of the DataGrid properties via input parameters to your methods. There should be next to nothing for source code in your form or dialog other than a few lines in events to pass various form objects to reusable class methods, properties, and events. I know this seems like adding an unnecessary layer of complexity and more work for you. Trust me, you'll be glad you did. |
| |
| 3. | Try to avoid creating methods in a class that are dependent upon module/class level objects. Note that I never said never do this. I said, try to avoid it. What I mean by this is try not to declare a class level variable such as a string and then populate that same string from inside one of your methods. Thus, you've tied your method directly to residing in that class forever. I've seen this create real problems when business layer or database layer methods in ASP.NET applications work directly with Session or Application variables. How well do you think that is going to work when that layer is implemented on the desktop or on a mobile device? |
| As your application grows in complexity, you may find the need to refactor certain class methods. In this case, you couldn't easily move a method to a new class without first untieing the dependency on that class level variable. What you'll want to try and do instead is declare your class level variable but always pass it in as a parameter to your methods. You get the same effect but now you are free to refactor your code to an improved more flexible design later on down the road. Plus, it will be much easier to diagnose problems during the development phase. |
|
|
| 4 | No matter which layer you yourself are working on, try to write your classes, methods, properties, and events assuming that you are not permitted to speak to the developers utilizing your layer. Thus, a user interface developer should be able to instantly see and understand which methods you've created for a specific task and what is required to use them. |
| As a small example, let's say that you have a method to delete a record from table A. Use an obvious name for the method (no acronyms or abbreviations). Provide overrides for your methods instead of optional parameters. If you opt to have an entire data input class as a parameter, you need to provide a way (documentation comments are one way) to communicate which properties are required. |
| Code comments and documentation are great. Code that clearly communicates how it should be accessed or used without the needed for commentary or explanation is even better. After writing each method, event, or property ask yourself "Would I know exactly what this means, how to utilize it, or what I should get as a return value and why?" using Visual Studio .NET intellisense. If the answer isn't a resounding yes, then you've done something wrong and now is the time to make modifications. |
| |
| 5. | For the same reasons mentioned above regarding code that describes itself, use classes/structs to transfer data between layers of your application whenever transferring a DataSet, DataTable, or XmlDocument isn't absolutely necessary. I know that Microsoft gives you all sorts of neat tools and examples of how to use DataSets and DataTables in your UI but they are often unnecessary and can lead to poor coding practices. That, and data binding is often much, much slower that manually adding the items to the control yourself. Data binding is highly overrated. |
| If you opt to send DataSets or DataTables back and forth, the UI and business layer developers have no way of knowing which columns, column data types, or tables are included at design time without first reviewing the schema or asking the database layer developer. If we use classes, Visual Studio .NET intellisense shows them exactly what they'll get in return, what properties and their corresponding data types are available, and provide intellisense which helps keep their own code that utilizes the class much, much cleaner. |
| Let's not forget about schema changes. If you change the name of a column or a table name, can you easily find all of the places that need this changed as well? If you use classes, you'll instantly get compilation errors and the specific lines of code that need to be changed as soon as you try to compile your assembly. |
| |
| 6. | Separate the classes that use a specific database provider to manage your data from the classes that utilize the return result sets or send data to be updated to the database. As an example, if the term SqlCommand exists in the same class method as your code to populate a specific data class, you are asking for trouble. When you are asked to enable your application to use Oracle as a backend database instead of SQL Server, your architecture breaks down and you've got to create duplicate code for both databases to populate your data class. The same applies to insert, update, and delete logic that may need to be processed prior to making the database call. This logic should be in another class and method which subsequently calls the method for direct database interaction. |
| Keeping the two separate pretty much requires that your low level, provider specific layer must return its results in a disconnected state which means you have to take on the small overhead of returning DataTables/DataSets (DataSet having a higher overhead) instead of a SqlDataReader. You could also look into some of the generic data access layer providers but most of the implementations I've seen add on just as much if not more overhead than just passing back a DataTable. |
| Next Page > |
| |
| Part I: Basic assumptions you should make with every application. |
| Part III: How do I set up the business layers as separate projects in my Visual Studio .NET solution? |
| |
| |