Deliverable: Business Objects; smart and mobile
CSLA delivers two major contributions towards the use of Business
Objects (BO) within our applications:
the framework makes it easier for us to make an effective BO,
as it provides a very well thought out, and proven, design which
removes most of the heavy lifting, deep level of skill, and drudgery
that would otherwise be involved.
the framework solves the very complex task of how to design
and build BO's which can be used by an application that is, or may
become, deployed across multiple hardware tiers. This is achieved in
a manner that doesn't lose the gains initially sought by adopting an
object-oriented approach.
Firstly I would like to briefly discuss the case for using BO's in
our applications. As you would expect with a title like “Expert
2005 C# Business Objects”, this topic is covered in great depth
by Rocky's book. Much of the case can be summarised by those
foundations of the Object-Oriented paradigm; encapsulation and reuse.
Our applications exist to deliver a union of our business data
and our business rules to our users. We need the business
rules to be available to our UI so that it can provide the users with
a rich experience. The availability of the business rules within the
UI allows us to give immediate validation and authorisation feedback
to the user, without repetitive round-trip delays back to the server.
In a desktop application this means that the business rules are
available within the WinForm, and in an Internet application they
need to be available to the Web Server.
If the application has an Application Server tier, the same
business rules are required at that point also. The App Server is the
central point of control before data makes it way out of, or into,
our database. It needs the business rules to ensure that any UI has
correctly and consistently applied validation and authorisation
rules.
The business rules are also required to manipulate or interpret
the business data within whichever tier is doing the actual work of
servicing the user's request.
Even if our application has no current need for physical tiers it
should be built with logical tiers. The application will be
more easily and safely maintainable,with a longer useful life, if it
is segmented into logical tiers. It also means that if we ever need
to implement hardware tiers at some later point, the application
design is ready for this. The tipping point may an increase in the
number of users, a need to pool database connections, or just a need
to move what was a desktop application onto the Internet.
The BO needs to be a mobile object, which can move between
the various tiers, to meet the needs of each tier . The design and
construction of the BO needs to take into account the laws of physics
regarding the latency and overheads involved when tier computers are
cooperating to complete a job. Careful design is required to avoid
the killer performance penalty that will result if the underlying
mechanism is overly chatty or inefficient.
Business Objects encapsulate our business data within our
business rules. The only way to get or update the data is via this
mobile BO, which we design to apply and enforce the business rules.
This encapsulation and protection would be lost if our system was
having to move data between the tiers in an unprotected container
such as in a DataSet or an xml package.
CSLA BO's not only provide better encapsulation and protection for
our data than these non-BO alternatives, but they are also more
lightweight to move between the tiers. This is due to the fact that
they are binary, and don't require the meta-data overhead of
DataSets. This lightness is achieved by installing our BO class
library's DLL on each of the computers involved, so that only the
data needs to be serialised and transmitted. DLL deployment and
version update issues can be handled by .NET's ClickOnce facility, now made
easier by the Publish Wizard which is built into VS2005.
The reuse angle comes from the fact that the business rules
within the BO are reused between tiers of an application, and can
also be reused between applications. The case study that Rocky builds
during the course of his book show a set of BO's being built then
deployed in three separate application modes. Essentially the same
application is deployed as a desktop application, a web server
application, and then as a set of web services. The same set of BO's
are reused for each application. This reuse achieves savings, and
also ensures that all applications are applying the same consistent
set of business rules.
As I said at the opening of this section, CSLA has two major
contributions to make with regard to BO's. The first is the design
and implementation of the CSLA framework to meet these challenges,
which is a task requiring deep experience and exceptional knowledge
of very complex corners of Microsoft's huge .NET framework. This
skill level was required by CSLA's designer, but is not required of
us to use and deploy the framework.
CSLA's
second contribution in this regard is the depth and breadth of the
CSLA framework itself. We are left with just the task of writing our
business logic. Most of the logic which makes our class into an
effective object comes already built into the CSLA framework.
I can illustrate this via one of the classes that I use within the
demonstration program that comes with the article that you are
reading. It is a Customer Order Header class. This class diagram
shows that I have implemented a few data properties allowing access
to the object's business data. We can see that I have some methods
involved with authorisation and the business rules used by the
property setters. There are also a couple of methods involved with
fetching and updating the order header.
This is all the work that I had to do, but as the following Class
View listing shows there is much more to my Order class. This listing
shows that my Order class has descended from a rich hierarchy of CSLA
classes which enrich my Order class with a lot of other features. Not
all of these methods are used by the UI programmer, but the listing
demonstrates the breadth of what is delivered by the framework,
Examples are the ability of an Order object to clone itself, the
IsValid property, or the collection of broken validation rules.

Another benefit coming from the use of BO's is their suitability
for unit testing. I don't know how to apply unit testing to a UI, but
if we extract our business rules into BO classes it becomes very easy
to write comprehensive unit test suites for these rules.
I
don't have the up-market version of VS2005, so here is a
demonstration using the free utility called NUnit (see links section
of this document). The following image shows that I have run the
suite of unit tests which have been defined for our BO's, and that
one of these tests has failed. The listbox at the middle right shows
an identifying error message, and informs us that a value of 88.88
was received, but a value of 36.68 was expected. The list box at the
bottom right is showing that failure happened at statement 93 of our
testing procedure.
The test suite project is part of our VS2005 solution. We can
exhaustively test and re-test our business rules at any time, with a
simple press of the “Run” button. If our business rules
were scattered through the UI, rather than being centralised in BO's,
this would not be possible. NUnit allows us to run all tests by
clicking on the root node on the left hand side of the screen as I
have done here. Alternatively we can elect to run a a single test, or
a sub-set of tests by selecting nodes within the tree itself.
Now I will examine part of the test suite project itself. Firstly
this will demonstrate how easy it is to create unit tests for our
BO's. NUnit will automatically create a tree node for any method that
is marked with the [Test] attribute. This is a facility to run test
set up and clean up procedures before each test, or each group of
tests. It has a selection of Assert statements to handle a variety of
equality, identity, comparison and type tests. Tests can also be
easily set-up that will fail unless a specific exception is
raised by the code being tested.
The second point of including the following source code is to
illustrate how easy the BO are to use. These test procedures deal
with the BO's at a simple level to fetch, clone and update data. The
programmer has not needed to be concerned with database table
structures, ADO, nor SQL.
using System;
using CslaWIIFM2.library;
using Csla;
using NUnit.Framework;
namespace NUnitTests
{
[TestFixture]
public class OrderUpdateTests
{
// fields
Customer _customerFromUSA;
Customer _customerFromUSAClone;
Customer _customerFromElsewhere;
Customer _customerFromElsewhereClone;
private readonly string _updateDate = "27/09/2006";
private readonly float _updateFreight = 36.68f;
/// <summary>
/// This setup routine runs before each test (methods marked with [Test] attribute.
/// </summary>
[SetUp]
public void Init()
{
_customerFromUSA = Customer.GetCustomer("HUNGC");
_customerFromUSAClone = _customerFromUSA.Clone();
_customerFromElsewhere = Customer.GetCustomer("BLONP");
_customerFromElsewhereClone = _customerFromElsewhere.Clone();
CslaWIIFM2.library.Security.SecurityPrincipal.Logout();
CslaWIIFM2.library.Security.SecurityPrincipal.Login("Doris in Despatch", "");
System.Security.Principal.IPrincipal user =
Csla.ApplicationContext.User;
Assert.IsTrue(user.Identity.IsAuthenticated, "Login failure durinit Init");
}
/// <summary>
/// These methods runs after each test.
/// </summary>
[TearDown]
public void CleanUp()
{
CslaWIIFM2.library.Security.SecurityPrincipal.Logout();
CslaWIIFM2.library.Security.SecurityPrincipal.Login("Doris in Despatch", "");
/* Clone is the object value before the test. Save this object to restore the db
* to it's initial state. Need to make the clone appear changed, so update, then
* reset a property to it's initial value. */
float xxx = _customerFromUSAClone.Orders[1].Freight;
_customerFromUSAClone.Orders[1].Freight = 0f;
_customerFromUSAClone.Orders[1].Freight = xxx;
_customerFromUSAClone.Save();
xxx = _customerFromElsewhereClone.Orders[1].Freight;
_customerFromElsewhereClone.Orders[1].Freight = 0f;
_customerFromElsewhereClone.Orders[1].Freight = xxx;
_customerFromElsewhereClone.Save();
CslaWIIFM2.library.Security.SecurityPrincipal.Logout();
}
/// <summary>
/// One of my business rules is that freight is free within the USA.
/// This test ensure that the USA based Customer object becomes
/// invalid if a freight value is applied. It also tests that
/// non-USA customer can be charged freight.
/// </summary>
[Test]
public void TestFreeUsaFreightRule()
{
_customerFromUSA.Orders[1].Freight = 0.01f;
Assert.IsFalse(_customerFromUSA.IsValid,
"Ensure freight invalid for USA");
_customerFromUSA.Orders[1].Freight = 0.00f;
Assert.IsTrue(_customerFromUSA.IsValid,
"Ensure reset USA freight to 0 makes object valid again");
_customerFromElsewhere.Orders[1].Freight = 0.01f;
Assert.IsTrue(_customerFromElsewhere.IsValid,
"Ensure invalid freight rule only applicable within the USA");
}
[Test]
public void TestOrderUpdates()
{
Order order = _customerFromElsewhere.Orders[1];
Assert.AreNotEqual(order.Freight, _updateFreight,
"test needs these to start out as being unequal");
Assert.AreNotEqual(order.ShippedDate, _updateDate);
order.Freight = _updateFreight;
order.ShippedDate = _updateDate;
_customerFromElsewhere.Save();
Customer cu = Customer.GetCustomer(_customerFromElsewhere.Id);
Assert.AreEqual(cu.Orders[1].Freight, 88.88m, "Update error"); // <== cause error
//Assert.AreEqual(cu.Orders[1].Freight, _updateFreight, "Update error");
Assert.AreEqual(cu.Orders[1].ShippedDate, _updateDate);
}
Our BO's can have much more function than the simple ones that I have
developed for this article. My BO's do little more than provide an
simple editable view of our data. A real-world set of BO's would
contain all of the functions that could be performed with our
application. An example could be “amalgamate all unshipped
orders for this customer”. |