Over the past couple of weeks we have been discussing the most effective way to write software and defining a framework to make that a reality. Yesterday, we discussed best practices at the database level. Today we are going to discuss the lowest procedural level, the middle tier. For a detailed explanation of the terms used go here, though we here at Sentia do NOT agree with the discussion of what does and should happen in the middle tier, but we are discussing that below. Grab a fresh cuppa and enjoy.
Middle Tier
The middle tier should be nothing more than an Object Oriented (OO) wrapper for the database objects and a way to create, read, update, delete and search (CRUDS) them. Advanced OO principles like Dependency Injection or SOLID are basically a way to save state without the use of a database. The database is where state should reside to make is durable, or alternatively as with Inversion of Control (IoC) these truths are self-evident and don’t need to be documented. Below is a discussion of what the middle tier should consist of.
Stored Procedures
While technically part of the middle tier, as they are not a data store and do contain logic, they should be discussed here. Basically, the rule on procedures is "use them." Do not do any data processing, outside of data validation in the middle tier.
BusinessObjectBase
The purpose of the BusinessObjectBase class is to provide a common interface for all Singular Business objects. It is actually an abstract class as it provides some default housekeeping logic for objects that derive from it, like a string called UniqueID that is a string representation of the EntityID so that you can search a collection of the objects by ordinal or by ID simply by casting the ID int to a String.
BusinessObjectCollection
The purpose of the BusinessObjectCollection is to house a one or more objects derived from the BusinessObjectBase class and to Automate CRUDS operations to the database. It has methods like Add, Update and Delete that must be overridden in the derived class.
DataAccessor Class
Many of you will have noticed the reference to the DataAccessor class at the top of the two business objects classes. This is instantiated ‘just in time’ in the various overridden add/update and delete methods in the base classes. It depends on a Static class GlobalConnectionString that doesn’t require instantiation and keeps the connection string to the database in a ‘sticky’ fashion, that is, if you don’t want to look in a config file and set the property every time, this class remembers the string for you and uses the last setting automatically. Notice further that there is no coding for ad hoc SQL. This is by design and it wouldn’t work anyway since the user who should be logging in has no access to the tables in the database.
BusinessObjectEnumerator Class
This is to allow iteration of objects derived from BusinessObjectBase from objects derived from BusinessObjectCollection.
Singular Objects
Note: that is not Singleton objects. We find those usually create more problems than they solve. The purpose of the singular object is simple: it represents a row in a table of a database. It has the ability to prepopulate itself from the database given an ID, but has no methods only properties corresponding to the columns in the database. It derives from the BusinessObjectBase class and an example of the CreditUnion object discussed above can be seen in Appendix A - CreditUnion Singular Object Example. There are several things to note here. First, notice the Constructor designed to return a CreditUnion object based on a CreditUnionID and SessionGUID. Second notice the SessionGUID itself and recall that we can limit the credit unions a user sees based on the user type or on which credit union they belong to.
Collection Objects
The purpose of the collection object is to take care of the housekeeping for rows in the database (represented by the singular objects), and to automate CRUDS (Create, Read, Update, Delete and Search) operations. Further, at the top of every collection object there is an enumerator which describes a way to retrieve a collection of singular objects by the related information in the database. That means that I automatically can get a list of credit unions by providing a BranchID, or a little more appropriately get a collection of Car objects (Cars) who have a particular type if tire installed simply by providing a TireID. This is what we mean by leveraging the power of the database. In the bad old days we might have returned a list of cars which is inefficient across the network, and a list of tires and iterated through both looking for matches. This is the epitome of procedural programming and why it is not optimal. The set based approach used here returns only the objects needed saving both network traffic and processing time. For small data sets, this could be a degree of magnitude faster, and as the two datasets increase in size the performance gap gets wider and wider.
Web Service
The object model described above is perfect for a web service. For a web application, there is no need for a web service however and having an installed desktop application doesn’t make much sense with a remote database, except in certain instances where what is necessary simply can’t be done in a browser.
So yes, the Enumerator class is an artifact of the .NET framework. Yes, you can use a plain old generic list object (List<T>) in place of a collection class, but you are going to have to eventually write some kind of custom code that you don't have access to in the List<T>. Even better, when you dimension a new collection object you make it
Tires tires = new Tires(CarID);
instead of
Tires tires = new List<Tire>.....uh how do we get a list of tires for this particular car?
Tomorrow we will take a look at the (Graphical) User Interface (GUI) and how best to tackle developing the things that should be there.
No comments:
Post a Comment