.NET for Visual FoxPro Developers Chapter
8 The VFP community has known about the importance of business
objects for several years now. They continue to be extremely important in all
types of .NET applications including Web Forms, Window Forms, and Web Services.
This chapter explains what business objects are, why you should use them, and
how to implement them in .NET. In most .NET books, documentation, white papers, and
Internet resources available these days, you hear little mention of business
objects. This isn’t because business objects are unimportant—just the opposite;
they are extremely important in building flexible applications that are
scalable and easy to maintain. Rather, I think this omission is because
business objects are perceived as being too advanced when first learning a new
technology. While there may be some truth to that, hopefully this chapter will
help you grasp the concept of business objects and take your software
development to a new level. In this chapter you’ll learn the basic mechanics of creating
and using business objects. In subsequent chapters, you’ll see how they can be
used in Windows Forms, Web Forms, and XML Web Services. What is a business object? A business object is an object that represents a
real-world entity such as a person, place, or business process (Figure 1). For example, in the real world, you have clients. You can
create a business object that represents clients. In the real world, you have
inventory and payments. You can create business objects that represent
inventory and payments. This concept isn’t completely foreign to Visual FoxPro
developers. When you design a data model, you often create data that represents
the characteristics of real-world entities. For example, you create a client
table that represents the characteristics of a client in the real world—a name,
address, phone number, e-mail address, and so on. You can create an inventory
table that represents the characteristics of different inventory items, a
payment table that represents the characteristics of payments, and so on (Figure
2). Modeling the characteristics of a real-world entity by means
of data is only half the story. The other half is modeling its behavior—and you do that by means of code.
This includes data manipulation code as well as any business logic (or
application logic) code associated with the entity. For example, you may have
code that calculates the tax on an invoice, totals the payments made by a
client, or calculates a client’s credit limit. Business objects bring together
both the characteristics and behavior of a particular entity (Figure 3). In an application that uses business objects, all the code
for a particular entity is stored in the corresponding business object. For
example, all the code that has something to do with clients is stored in the
client business object; all the code that has something to do with inventory is
in the inventory business object; and yes, all the code that has something to
do with payments is stored in the payments business object. The simplest aspect
of modeling an entity’s behavior is data manipulation. Just about every
business object needs methods that allow you to add, edit, retrieve, save, and
delete records—these translate to actions that occur in the real world. For
example, when you acquire a new client, this corresponds to the ability of a
business object to add a new record. When you lose a client, this may
correspond to the ability of a business object to delete a record (or possibly
mark it as “inactive”). When some attribute of a client changes (address, phone
number, e-mail address), this corresponds to the ability of a business object
to retrieve, edit, and save a client record. Examining business objects in popular software applications The concept of using business objects in software
applications is very common among popular off-the-shelf software packages.
Examining the business objects in these applications can help you better
understand how to implement business objects in the software you create. A good place to start is by exploring the business object
model of Microsoft Word. The primary business object in this model is the
Document object, which represents a real-world Microsoft Word document. You can
view Word’s object model by using Visual Studio .NET’s Object Browser. To launch
the Object Browser,
select View | Other Windows | Object
Browser from the menu. To open up the Microsoft Word object
model, click the Customize
button at the top of the Object
Browser, which launches the Selected
Components dialog. Click the Add
button, which launches the Component
Selector dialog (Figure 4). Click the COM tab and select
Microsoft Word 9.0 Object Library in the component list (the version number
differs depending on which version of Microsoft Word is on your computer).
Click the Select button to
add Microsoft Word to the Selected
Components list, and then click OK
to close the Component Selector
dialog. Click the OK
button in the Selected
Components dialog to add it to the Object
Browser. This adds a Word
node to the Object Browser
tree view. If you expand the Word
node, you see quite a few items listed. If you select the Document business object
in the left pane, it displays the members of the Document object in the right
pane (Figure 5). You
can determine which items are business objects in the Object Browser because
they have the same icon as shown to the left of the Document business object
shown in Figure 5. You can get quite an education by examining the methods of
the Document business object. For example, in the real world you can perform a
wide variety of actions against a document such as: ·
Check grammar ·
Check spelling ·
Print it ·
Undo changes ·
Close It In the Document business object the methods represent these
different actions: ·
CheckGrammar ·
CheckSpelling ·
PrintOut ·
Undo ·
Close For another example, open up the Microsoft Internet Controls
object library in the VS .NET Object
Browser (Figure 6). This library contains a WebBrowser business object that
represents a real-world browser. It contains methods such as GoBack, GoForward,
GoHome, Navigate, Refresh, and so on. Each one represents real-world actions
you can perform with a web browser. One of the main points to realize from looking at these
object models is when Microsoft created these tools they did not put the
application logic in the user interface of Microsoft Word and Internet
Explorer. Instead, they created business objects possessing events and methods
that contain the business logic. The methods are intuitive and perform a
discrete action that accomplishes a well-defined objective. If they change the
interface of the tool, they don’t have to move or rewrite all of the internal
code that handles operations like SpellCheck and GoHome. Monolithic vs. three-tier applications A monolithic application is a software
application where the user interface, business logic, and data are inextricably
bound to each other. Typically this type of application does not use business
objects. Despite the benefits that business objects can provide, most
developers (Visual FoxPro and otherwise) continue to build monolithic
applications. This should come as no surprise, because most of the software
development tools on the market actually encourage you to build applications
this way. For example, think about the Data Environment builder in
Visual FoxPro. The Form Designer and Report Designer allow you to use the Data
Environment builder (Figure 7) to specify the data to be loaded by a
form or report. Although these tools can help you build applications rapidly,
they don’t provide the most scalable solution. For example, if you load Visual
FoxPro tables into the data environment of a form, what happens when you want
to move to SQL Server or Oracle? You have to spend weeks or months tearing
apart your application and putting it back together again. As mentioned at the beginning of this chapter, most .NET
documentation, books, and periodicals also demonstrate creating monolithic
applications. Not using business objects means the data access code is placed
directly in the user interface, making for a very monolithic application. In contrast, Figure 8 shows a three-tier system
architecture that includes business objects. This is a far more flexible
architecture where any tier can be swapped out (for more information on three-tier
architecture, see Chapter 7, “Data Access with ADO.NET”). For example, you can create a smart client Windows desktop
application for tier 1, and then later you can swap it out with a thin client
Web browser interface, without affecting the rest of the application (Figure
9). You can also change the data tier from Visual FoxPro to SQL Server
without changing your application logic. Additional business object benefits In addition to the scalability gained with business
objects, there are other benefits that come from using them. Normalizing application logic One benefit of using business objects is normalizing
your application logic. As a Visual FoxPro developer, you know all about
normalizing data—eliminating redundancies in the data structure. But how
conscious are you of eliminating redundancies in your application logic? When
you work with a team of developers, the chances of creating duplicate
application logic increases dramatically—especially when creating a monolithic
application. You can have five different developers create the same routine
five different times when the code is stored in the user interface. Each
developer working on a different form has no idea that another developer has
already created the code they need. In contrast, when you use business objects, even when
developers work on different forms, each form uses the same set of business
objects. Each business object acts as a common repository for code that relates
to a particular real-world entity. The chances of adding two or more methods to
a business object that perform the same function are pretty slim—especially if
you give your methods meaningful names! Normalizing your application logic means you write, debug,
and maintain less code. When a change request comes through there’s only one
place in your application that needs to change. Solving the “where’s the code” syndrome Have you ever played “where’s the code?” with your
software applications? When you create a monolithic application, the code can
be located just about anywhere—and Murphy’s law predicts that the code you want
is probably tucked inside the Click method of a button located on page 3 of a
“sub” page frame contained within another page frame. Finding application code is much easier when you use business
objects. For example, Ease of conceiving and creating complex software systems When your code is stuck “in the weeds” of your
application’s user interface, it can be very difficult to step back and see the
big picture of a complex software system. However, when you place your application logic in business
objects, it’s far easier to see the big picture and think “big thoughts”.
Rather than poring through a morass of methods within your user interface code,
you can conceptualize complex processes as high-level business objects
representing real-world entities and interacting with each other. I have the same experience over and over again when I visit
software development companies to help them solve some of their more thorny
issues. As soon as they lay out business objects on a diagram and begin
conceptualizing at a higher level, problems that were previously impossible to
wrap their minds around can be grasped and solved. Once you learn how to use business objects, you’ll never go
back again! A simple example So, how do you transform a monster Click event into a
business object model? Although this isn’t a book about analysis and design,
here is a simple example of how this works. Consider the example of a point-of-sale invoicing application.
If you’re creating a monolithic application, you might have an Invoice form
that has a Save button. Within the Click event of the Save button, you might
have code that does the following: ·
Scans through each invoice item calculating the
tax (if any) on each item. ·
Adds the tax and item cost to the invoice header
total. ·
Subtracts the invoice item quantity from the
“quantity on hand” in inventory. ·
Saves the invoice items. ·
Saves the invoice header How would you handle this using business objects? Typically,
you should create a different business object for each table in your back end
database (this is a guideline, not a “set in stone” rule). In this example, you
might create Invoice, InvoiceItem, Tax, and Inventory business objects. Figure 11 shows a UML sequence diagram demonstrating
how you might implement business objects to handle all of the processes
involved in saving an invoice. For more information on creating and reading UML sequence
diagrams, see my online article “UML Sequence Diagrams” at the following url: http://www.devx.com/codemag/articles/2002/March/umlsequence/umlsequence-1.asp
The stick figure at the top left of diagram represents the
Sales Rep who interacts with the application to save an invoice. To the
immediate right of the SalesRep a box labeled “UI” is a generic representation
of the application’s user interface. The Save() message line pointing from the
SalesRep to the UI represents the SalesRep pressing the Save button. The rest of the boxes to the right of the UI box represent
the application’s business objects. Notice the arrow labeled “Save()” between
the UI object and the Invoice object. This indicates the Invoice object has a Save()
method being called by the user interface. The Invoice object in turn sends a
Save() message to the InvoiceItems object. The InvoiceItems object calls a
method on itself named SaveItem(). The asterisk preceding the SaveItem() method
indicates this method is called multiple times—in this case, once for each
invoice item. From within the SaveItem() method, a call is made to the Tax
object (CalcTax) to calculate tax on the item and to the Inventory object
(RemoveFromStock) to remove each item from stock. Note that I haven’t included any parameters in this diagram.
I did this to make it easier to read. However, in reality most methods in this
diagram would receive parameters. For example, the RemoveFromStock() method
might receive an inventory item primary key and a quantity, so it can determine
which inventory item to adjust and by how much. The main purpose of this example is to show you the big
picture of how to use business objects in applications. You will see more
detailed examples of using business objects in Chapter 9, “Building .NET
Windows Forms Applications”, Chapter 10, “Building Web Applications with
ASP.NET”, and Chapter 12, “XML Web Services”. Making .NET data access easy If nothing I’ve mentioned so far strikes you as a
compelling reason to use business objects, the fact that business objects make
it much easier to use ADO.NET may be the reason you are looking for. As Chapter 7, “Data Access with ADO.NET” explained, ADO.NET
is very flexible, very scalable, and very object-oriented, but it can be
difficult to learn and use. Business objects change all that by creating a
high-level interface to ADO.NET that doesn’t require all developers on your
team to be familiar with creating connections or manipulating and coordinating
data objects. You write your data access logic once, store the code in a
family of data access classes used by your business objects, and never worry
about the specifics of ADO.NET again—until Microsoft changes the ADO.NET object
model. This actually brings up another compelling reason to create a
layer of abstraction between your application and ADO.NET. Microsoft is
notorious for changing its data access model every few years. If you follow the
pattern set by many .NET code samples found in books, magazines, and online
articles, you’ll end up sprinkling lots of data access code throughout your
user interface. This becomes a problem if Microsoft makes changes to ADO.NET.
It will force you to update all of this data access code accordingly. However,
if you use business objects, you have only one place to change your data access
code—within the data access classes of the business object. Enforcing business rules One of the primary jobs of a business object is to
enforce business rules. Business rules fall into two broad categories: 1. Data
integrity rules – This encompasses rules that enforce things such as required
fields, field lengths, ranges, and so on. 2. Domain
rules – This refers to high-level business rules such as “You can’t create an
invoice for a client who is over their credit limit”. Typically, an application checks business rules at two
different points in time. The first is when trying to save a record. After a
user clicks the Save button (Windows Forms application) or the Submit button
(Web Forms application), the system needs to check if any rules pertaining to
the record being saved are broken, and if so, display a message with showing
the broken rules. The second place rules are often checked is when the user
leaves a data entry control. For example, when the user leaves an e-mail text
box, you may want to immediately check if the e-mail is valid. You can call a
business rule method to verify this. For details, check out the section, “The
BusinessRules class”, below. .NET business object architecture To help you grasp the concept of business objects, the
sample code that comes with this book provides a simple business object class
you can use to access either FoxPro or client-server data. Figure 12
shows a UML class diagram documenting the basic architecture of this business
object class. The
architecture of the business object classes in this book is by no means Each of these objects is covered in detail in the following
sections. The BusinessObject class The BusinessObject class, shown in Figure 12, is the
primary class in the business object architecture that the user directly
interfaces with. The sample code in this chapter is set to use the SQL Server
Northwind database by default. Creating subclasses of the BusinessObject class You create subclasses of the BusinessObject class that
represent entities in your application domain. For example, in this book’s
sample code, four business objects have been subclassed from BusinessObject: ·
Employee ·
Customer ·
Orders ·
OrderDetail When you create a subclass of BusinessObject, two important
properties should be set right away—the TableName and FieldList. The TableName
property specifies the primary table in the database with whichwhere the
business object retrieves and manipulates data. The FieldList property
specifies the default list of fields included in the DataSet when you retrieve
data from the back end. This property is set to “*” by default, which specifies
that all fields in the table are returned. You can easily change the value of these properties in your
custom business object’s constructor method. For example, the following code
defines a Customer class derived from BusinessObject and sets the value of
TableName and FieldList. In C#: public class Customer : BusinessObject { /// <summary> /// Customer constructor /// </summary> public Customer() { this.TableName =
"Customer"; this.FieldList =
"CustomerID, CompanyName, " + "Address,
City, PostalCode, Country, Phone"; this.BusinessRuleObject
= new CustomerRules(); } } And in Visual Basic .NET: Public Class Customer Inherits BusinessObject '/ <summary> '/ Customer constructor '/ </summary> Public Sub New() Me.TableName =
"Customer" Me.FieldList =
"CustomerID, CompanyName, " & _ Address, City,
PostalCode, Country, Phone" Me.BusinessRuleObject
= New CustomerRules() End Sub 'New End Class 'Customer Retrieving data with the GetDataSet method The BusinessObject class has a GetDataSet method that
executes a SQL SELECT statement and returns a DataSet containing the result
set. This method has two overloads. In C#: protected DataSet GetDataSet() { string Command = “SELECT “
+ this.FieldList + “ FROM “ + this.TableName; return
this.GetDataSet(Command); } protected DataSet GetDataSet(string command) { return
DataAccessObject.GetDataSet(command, this.TableName); } And in Visual Basic .NET: Protected Function GetDataSet() As DataSet Dim Command As String =
"SELECT " & Me.FieldList & " FROM " &
Me.TableName Return Me.GetDataSet(Command) End Function 'GetDataSet Protected Function GetDataSet(command As String) As DataSet Return
DataAccessObject.GetDataSet(command, Me.TableName) End Function 'GetDataSet The first method signature accepts zero parameters. It simply
uses the FieldList and TableName properties to automatically build a SELECT
command that it passes to the second overload of the GetDataSet method. The
second overload accepts a single “command” parameter that it passes to the data
access object (discussed below) for execution. It also passes the TableName
property, used to specify the name of the main DataTable within the DataSet. The GetDataSet methods are marked as protected, because you
typically don’t want to open your back end database to this sort of carte blanche
querying capability. For example, if the second GetDataSet method was public,
there’s nothing stopping someone from issuing a SELECT * that returns all
fields and records in a table with millions of records. To retrieve data from a custom business object, you typically
create methods that build a SELECT string and pass it to the second overload of
GetDataSet. For example, the following methods of the Customer business object
retrieve a customer by ID and phone number. In C#: public DataSet GetCustomerByID(string customerID) { return
this.GetDataSet("SELECT " + this.FieldList + " FROM " +
this.TableName + " WHERE
customerID='" + customerID + "'"); } public DataSet GetCustomerByPhone(string phone) { return
this.GetDataSet("SELECT " + this.FieldList + " FROM " +
this.TableName + " WHERE Phone =
'" + phone + "'"); } In Visual Basic .NET: Public Function GetCustomerByID(ByVal customerID As
String) As DataSet Return
Me.GetDataSet(("SELECT " & Me.FieldList & " FROM "
& Me.TableName & _ " WHERE
customerID='" & customerID + "'")) End Function 'GetCustomerByID Public Function GetCustomerByPhone(ByVal phone As String) As
DataSet Return
Me.GetDataSet(("SELECT " & Me.FieldList & " FROM "
& Me.TableName & _ " WHERE Phone =
'" & phone & "'")) End Function 'GetCustomerByPhone Here is an example of how you call the GetCustomerByPhone
method from client code. In C#: Customer CustomerObj = new Customer(); DataSet dsCustomers =
CustomerObj.GetCustomerByPhone("555-3425"); And in Visual Basic .NET: Dim CustomerObj As New Customer() Dim dsCustomers As DataSet =
CustomerObj.GetCustomerByPhone("555-3425") Saving data with the SaveDataSet method The SaveDataSet method accepts a single DataSet
parameter and updates the back end database with any changes (updates, inserts,
deletes) found in the DataSet. Here is an example of how you call the SaveDataSet method to
update a DataSet. In C#: Employee EmployeeBizObj = new Employee(); // Retrieve an Employee record DataSet dsEmployee = EmployeeBizObj.GetEmployeeByID(1); DataRow drEmployee = dsEmployee.Tables[0].Rows[0]; // Change a value drEmployee["Title"] = "Vice president"; // Save the change int RowsUpdated = EmployeeBizObj.SaveDataSet(dsEmployee); And in Visual Basic .NET: Dim EmployeeBizObj As New Employee() ' Retrieve an Employee record Dim dsEmployee As DataSet = EmployeeBizObj.GetEmployeeByID(1) Dim drEmployee As DataRow = dsEmployee.Tables(0).Rows(0) ' Change a value drEmployee("Title") = "Vice president" ' Save the change Dim RowsUpdated As Integer = EmployeeBizObj.SaveDataSet(dsEmployee) Before the actual update occurs, this method checks to see if
there is a business rule object attached and, if so, calls that object’s
CheckRules method. If this method is successful, it returns the number of
records containing changes that were persisted to the back end (zero or more).
If any business rules are broken, this method returns a –1. For details on how
to handle broken business rules, see the next section. The BusinessRules class The BusinessRules class enforces business rules and
keeps track of any rules that are broken. Typically, you should create a
subclass of the BusinessRules class for each of your business objects. For
example, in the samples for this book, the Customer business object has a
CustomerRules object, the Employee object has an EmployeeRules object, and so
on. To associate a business rule class with a business object,
you need to instantiate it in the constructor of the business object. For
example, you add the following code to the constructor of the Customer business
object to instantiate and associate the CustomerRule object with it. In C#: public Customer() { this.BusinessRuleObject =
new CustomerRules(); } And in Visual Basic .NET: Public Sub New() Me.BusinessRuleObject =
New CustomerRules() End Sub 'New Typically, you create a separate method in the rules object
for each different business rule. For example, the CustomerRules object has
IsCompanyNameValid, IsPostalCodeValid, and IsPhoneValid methods. Breaking these
methods out allows you to call each method individually (for example, from the
event of a user interface control). In most cases, you also need to check all business rules when
you try to save a record. How can this be done if you have each rule in a
separate method? The answer is to add a call to each business rule method in
the CheckRulesHook method of the BusinessRules class. For example, the
CustomerRules object contains the following code in its CheckRulesHook method. In C#: public override
void CheckRulesHook(DataSet ds, string tableName) { DataRow dr =
ds.Tables[tableName].Rows[0]; // Get
the first DataRow this.IsCompanyNameValid(dr["CompanyName"].ToString()); this.IsPostalCodeValid(dr["PostalCode"].ToString()); this.IsPhoneValid(dr["Phone"].ToString()); } And in Visual Basic .NET: Public Overrides Sub CheckRulesHook(ds As DataSet,
tableName As String) Dim dr As DataRow =
ds.Tables(tableName).Rows(0) ' Get the first DataRow
Me.IsCompanyNameValid(dr("CompanyName").ToString()) Me.IsPostalCodeValid(dr("PostalCode").ToString()) Me.IsPhoneValid(dr("Phone").ToString()) End Sub 'CheckRulesHook The BusinessObject class automatically calls the
CheckRulesHook method before it tries to save a record. It passes the DataSet
to be saved as well as the name of the Table within the DataSet. If any rules
are broken, the business object does not save the data in the DataSet (the next
section shows how you can retrieve and display broken rules). In this particular implementation, the BusinessRules class
only checks the first record in the DataSet. You can enhance this method to
check multiple records in a DataTable. Checking for broken business rules If the BusinessObject’s SetDataSet method returns a –1,
you call the GetBrokenRules method of the BusinessObject class to determine the
business rules that were broken. This list of broken rules can then be
displayed to the user. For example, here’s code that checks for broken rules
when saving an order. In C#: Orders OrderObj = new Orders(); DataSet dsOrder = OrderObj.GetOrderByOrderID(10248); DataRow drOrders = dsOrder.Tables[0].Rows[0]; drOrders["EmployeeID"] = 0; int RowCount = OrderObj.SaveDataSet(dsOrder); if (RowCount == -1) { string BrokenRuleList =
""; foreach (string BrokenRule
in OrderObj.BusinessRuleObject.BrokenRules) { BrokenRuleList +=
BrokenRule + "\n"; } MessageBox.Show("Broken
Rules: \n\n" + BrokenRuleList, "Business Rules"); } else { MessageBox.Show("Order
successfully saved","Business Rules"); } And in Visual Basic .NET: Dim OrderObj As New Orders() Dim dsOrder As DataSet = OrderObj.GetOrderByOrderID(10248) Dim drOrders As DataRow = dsOrder.Tables(0).Rows(0) drOrders("EmployeeID") = 0 Dim RowCount As Integer = OrderObj.SaveDataSet(dsOrder) If RowCount = - 1 Then Dim BrokenRuleList As String
= "" Dim BrokenRule As String For Each BrokenRule
In
OrderObj.BusinessRuleObject.BrokenRules BrokenRuleList +=
BrokenRule + ControlChars.Lf Next BrokenRule
MessageBox.Show("Broken Rules: " + ControlChars.Lf + _ ControlChars.Lf +
BrokenRuleList, "Business Rules") Else
MessageBox.Show("Order successfully saved", "Business
Rules") End If This code instantiates the Orders business object, retrieves
an order, and saves the DataSet with a broken rule (the Employee ID is empty).
It then checks the return value of the SaveDataSet method to see if any rules
are broken (indicated by a return value of –1). If any rules are broken, it
retrieves all broken rules from the BusinessRule object. A reference to the
BusinessRule object is stored in the business object’s BusinessRuleObject
property. BrokenRules is a string collection contained within the BusinessRule
object. The “for each” loop iterates through the string collection building a
string containing all broken rules which it then displays in a message box. Some developers like to display all the broken rules in a
single dialog. You can do this by simply concatenating the broken rules
together and displaying them. Another option is to have your business object
return broken rules as an XML string. You can then display the broken rules in
a list box, DataGrid, etc. Data access classes Figure 12 showed the BusinessObject class has an
associated data access class it uses to retrieve and manipulate data. As shown
in Figure 13, there is actually a family of data access classes used to
access different types of data. The abstract DataAccessBase class defines the interface for
the family of data access classes. The DataAccessSql class allows you to access
SQL Server 7.0 and later, acting as a wrapper around the .NET SQL Server data
provider. The DataAccessOleDb class allows you to access any data with an OleDB
Data Provider, acting as a wrapper around the .NET OleDB data provider. By default, the BusinessObject class uses the DataAccessSql
class to access the Northwind SQL Server database. To change the BusinessObject
class (and all subclasses) to use the DataAccessOleDb class instead, you change
its DataAccessClass property from “DataAccessSql” to “DataAccessOleDb”. Conclusion You don’t have to use business objects in your .NET
applications, but doing so makes your applications far more flexible,
extensible, and maintainable. In this chapter you’ve seen how to design and
implement business objects. For examples showing how business objects can be
used in different types of applications, see Chapter 9, “Building .NET Windows
Forms Applications”, Chapter 10, “Building Web Applications with ASP.NET”, and
Chapter 12, “XML Web Services”.
.NET Business Objects
if you search for code that has something to do with invoicing, chances are very
high it can
be found in the Invoice business object. If your application has a bug in the
logic that
performs calculations on inventory, you can bet that the code is probably in
the Inventory object. Surfacing your application logic (raising it from the
depths of the user interface) and exposing it in high level business objects (Figure
10) makes your application far easier to debug and maintain.
the one “right” way. This architecture is simple enough to show you the
basic mechanics of business objects while still having enough advanced features
to demonstrate the flexibility and scalability business objects
provide. You’ll definitely want to enhance this architecture for more robust
production systems.