I have a friend that needs to convert some old fortran engineering code to a more modern language for maintainability purposes. Any ideas?
The reward of a thing well done is having done it.
Ralph Waldo Emerson
A friend from Atlanta passed this on to me:
"The fantasy element that explains the appeal of dungeon-clearing games to many programmers is neither the fire-breathing monsters nor the milky-skinned, semi-clad sirens; it is the experience of carrying out a task from start to finish without user requirements changing."
:-P
When I composed yesterday's blog, I was focused primarily on the issue of implementing the Interface of the target object in the UI element that edits that object. In my head I was calling it the User Interface Interface pattern, or UI^2. The 7 layers in the system are: UI, FACTORY/FACADE, Security, Data Object, Data Rules, Data Access, and Data Store.
After the quantity and quality of comments that I received on that blog, I have completely shifted focus to the idea of separating Business Rules from Business Objects. I had written no actual code for yesterday's blog--I was simply sandboxing. I took the time tonight to create a VB 2005 solution with all the major working parts. It's still in the rough draft phase, so I haven't fully separated all my tiers yet. I'm going to take some time and do some refactoring of my code based on some of the comments I received, as well as some new ideas I've been thinking about. Then I'm going to get some web space and post my working solution with each successive blog on this subject.
Design patterns are fascinating.
Matt Berther is doing something similar to what I was talking about in my previous post.
Where we differ is where to do the validation of the business rules. He's wrapping his IMyView in a class that knows how to perform the Business Rule validation. I didn't discuss my validation method in my last post, so I'll discuss it here:
Let's say I have a class that knows how to save an IPerson to a DataStore. The Save Method signature looks like:
Public Function Save(p as IPerson) as Boolean
If my Web page or UserControl implements IPerson, I can simply pass the entire page or UserControl to the Save method of my Data Access object. But where does the validation get done? It gets done during the save attempt. The UI layer does not have direct access to the Data Access layer. It has indirect access via a set of FACTORY objects. When the UI attempts to save, it does so through a FACTORY class that first performs validation of the Business Rules, and if everything is okay, continues with the save. If the validation fails, then a list of errors is returned to the UI via an event handler.
This separation of Business Rule Objects (policy) from Business Data Objects (data) and Data Access objects (persistence) allows for easily turning various business rules on and off. As new rules are added, old ones are removed, or existing ones change their optional status, only one layer has to be modified: the validation layer. Additionally, this layer can itself be stored in the Data Store and configured dynamically. The UI can and should perform its own validation, but if it doesn't, no harm is done to the underlying system.
Consider the following example. Assume an interface IPerson defined as follows:
Public
Interface IPerson Property IsACompany() As Boolean Property CompanyName() As String Property FirstName() As String Property MiddleName() As String Property LastName() As String Property Address() As IAddress Property Contact() As IContactInformation End
Interface
Public
Interface IAddress Property Address1() As String Property Address2() As String Property City() As String Property State() As String Property PostalCode() As String End
Interface
Public Interface IContactInformation
Property PhoneNumbers() As System.Collections.Specialized.NameValueCollection
Property EmailAddresses() As System.Collections.Specialized.NameValueCollection
End
Interface
Consider the following Business Rules: In IPerson, the LastName is required if it is, if fact, a person. The CompanyName is required if the IPerson is a company (anybody have a better name for IPerson that supports it being either a human or a company?)
My FACTORY Object might look like this:
Public
Class FactoryObject Public ReadOnly Property SecurityContext() As SystemUser ' snipped for brevity Public Function Save(ByVal p As IPerson) As Boolean Dim V As New IPersonValidator(p) Dim pm As New PersonManager(Me.SecurityContext) Return pm.Save(p, V) End Function End
Class The FACTORY Object simply instantiates the validator as a required parameter to the DataAccess Object's Save() method, hiding this complexity from the client code. The Validator Object might look like this:
Public
Class IPersonValidator Public ReadOnly Property Person() As IPerson ' snipped for brevity Public ReadOnly Property IsValid() As Boolean Get Return Me.ValidateNames End Get End Property Public Function ListErrors() As String() ' snipped for brevity
Protected Function ValidateNames() As Boolean Dim Result As Boolean = True If Me.Person.IsACompany Then Result =
Me.Person.CompanyName.Trim <> String.Empty Else Result =
Me.Person.LastName.Trim <> String.Empty End If Return Result End Function Public Sub New(ByVal p As IPerson) ' snipped for brevity End
Class And finally, the Save() method on the Data Access object might look like:
Public Function Save(ByVal p As IPerson, ByVal V As IPersonValidator) As Boolean Dim Result As Boolean = False If Not V.IsValid() Then 'Raise a ValidationError event 'snipped for brevity Else Result =
Me.SaveToDataStore(p) ' execute SQL to store IPerson in Database End If Return Result End Function I'd be interested in any feedback from the community on this approach. This is something that I've started doing recently and so far it's working out rather nicely. There may be pitfalls that I can't see yet, so if you see any major problems with this, ping me :-). You can ping me if you like it too :-P.