OT: What is a business object?

J

jehugaleahsa

Hello:

We have a fairly small shop where I work. Most of our applications are
database driven, taking data from a database table and displaying it
on a form for editing. We are trying to apply a 3-layer architecture,
but I feel like we are missing something.

For us, business objects have always been classes with properties for
each column in a table. However, from what I have read, it sounds like
this really isn't a very good definition for a business object. The
reason I say this is because I feel as though the business object is a
little too aware of the database and has no real business logic
associated with it.

For instance, a "business object" could simply be a bunch of private
fields for each column with public properties for each. At some point,
the database results must be stored to the business object. The data
access layer may deal with assigning all the properties, but it still
seems like the business object is just a mapping of the database
fields. Many authors make it sound as though business objects are more
than just representations - that they have methods for executing
business logic.

Well, obviously, somewhere the transition from the database to an
object is needed. So, I'm not sure what is bothering me here. The
question is, where is all our domain logic? In our applications, like
I said, we essentially just dump the database information to a form.
In that case, can there be such a thing as a business layer?

Our business objects almost always look like this:

MyBO:
<list of properties>
<static method for select>
<instance methods for insert, update and delete>

A long time ago, we used to keep the methods out in a factory class,
but we found it really didn't buy us anything.

Perhaps our applications just lack the domain logic. Is creating a
mapping of the database table sufficient to be considered a domain
layer?

Another question: Over the time we have been developing, we stopped
using private fields for our values. Instead, we pass a DataRow/
IDataRecord to the business object's ctor. We write our properties so
that all they do is index into the row/record. This has worked great
because it saves us from either passing a large number of values to
the ctor or setting each property individually. While this saves code,
it means our business objects suddenly need to know a lot more about
our databases. Is this a bad thing? Does this move our business
objects more toward the data access layer than the domain layer?

Thanks for any input,
Travis
 
S

sloan

Personally, I don't like mixing business objects with the code that creates
the business object(s).
(As you suggest with what you pass to the constructor (ctor as you put it))
(So I do not like your approach of passing the datarow or IDataRecord to the
constructor).

To me a business object can exist ...and it doesn't have to always have a
backend database Entity necessarily.
Aka, I don't assume a business object always needs a persistance.
Alot of times, yes, but not all the time.



I'm sure alot of others will chime in...

But to show how I seperate the object and the code which creates the object,
you can look here:
http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!139.entry
and there is a 2.0 version of that blog entry as well.

....


Some of this is style and preference. I've found the preference I like,
which has matured over the years.
The blog entry shows what I've come up with.


Good luck.
 
J

jehugaleahsa

Personally, I don't like mixing business objects with the code that creates
the business object(s).
(As you suggest with what you pass to the constructor (ctor as you put it))
(So I do not like your approach of passing the datarow or IDataRecord to the
constructor).

To me a business object can exist ...and it doesn't have to always have a
backend database Entity necessarily.
Aka, I don't assume a business object always needs a persistance.
Alot of times, yes, but not all the time.

I'm sure alot of others will chime in...

But to show how I seperate the object and the code which creates the object,
you can look here:http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!139.entry
and there is a 2.0 version of that blog entry as well.

...

Some of this is style and preference.  I've found the preference I like,
which has matured over the years.
The blog entry shows what I've come up with.

Good luck.
















- Show quoted text -

My questions for you: do you still manually go in and write a long
list of properties and private fields? How well would you say you map
the database type to a .NET type and what is your method for doing so?

Even then I still feel that in a more advanced application, the
business object would be much more powerful.
 
S

sloan

//
My questions for you: do you still manually go in and write a long
list of properties and private fields? How well would you say you map
the database type to a .NET type and what is your method for doing so?
//end quote

Did you download the code (complete working example) from the blog?

The CustomerController class has what you're looking for in it. Or at least
my take on it.

..........

I'll answer the second question, since that isn't obvious from the
downloadable code.

Yes, the business object has the capacity to have alot more than just simple
property/value mappings.
When I say capacity, what I mean is that alot of times they are simply just
persisted instances of the db data.
However, you can add-on and write properties that have little to do with a
simple property/value.

For instance.
Person (object) and "IsHireable" (readonly) boolean property.

Inside the property "IsHireable" you have a business rule which says.

Is the person over age 18 (check the Person.DateOfBirth value and calculate
against today's date)?
AND
Is the person a USA citizen OR does the person has their green card?
AND
(etc etc)

So the property a simple boolean, but depends on several checks to see if it
is true or not.

That's a basic example of a derived property, something much easier to
implement in a custom business object rather than a
DataSet,DataTable,DataRow type system.






Personally, I don't like mixing business objects with the code that
creates
the business object(s).
(As you suggest with what you pass to the constructor (ctor as you put
it))
(So I do not like your approach of passing the datarow or IDataRecord to
the
constructor).

To me a business object can exist ...and it doesn't have to always have a
backend database Entity necessarily.
Aka, I don't assume a business object always needs a persistance.
Alot of times, yes, but not all the time.

I'm sure alot of others will chime in...

But to show how I seperate the object and the code which creates the
object,
you can look
here:http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!139.entry
and there is a 2.0 version of that blog entry as well.

...

Some of this is style and preference. I've found the preference I like,
which has matured over the years.
The blog entry shows what I've come up with.

Good luck.
















- Show quoted text -

My questions for you: do you still manually go in and write a long
list of properties and private fields? How well would you say you map
the database type to a .NET type and what is your method for doing so?

Even then I still feel that in a more advanced application, the
business object would be much more powerful.
 
P

Peter Morris

This is how I create business objects.

http://video.codegear.com/RADStudioDevDaysSep2007/ECO/ECODemo.html


Note that I use VS2008 these days as my IDE. I used this technology for a
fairly large system for Imperial Tobacco this year, it was developed
extremely quickly and the customer is very happy with the quality and
flexibility too. The flexibility comes from thinking in objects rather than
database.



Pete
 
J

Jeff Winn

Sounds like you're confusing the model object with the business object. The
business layer contains logic that the application uses and is the main
entry point into the other layers when working with data. The data layer
creates the model layer objects (which you call your business objects). The
data layer is only called by the business layer and your data layer objects
should be kept internal or private while you're business and model objects
are exposed publicly (if you're want them exposed).

Here's a simple example how the layers integrate together. The namespace
determines which layer the object is in:

// Business

namespace MyDll.Business {
public static class MyTableManager {
public static MyObject GetObjectById(int id) {
// Here you may want to handle your caching of the objects,
testing the objects to ensure they pass all requirements to be placed into
the database, or anything else
// you may need to do in the application. This example is simply
calling the data layer telling it to retrieve a row from the table with the
specified id.

return Data.MyTable.GetObjectById(id);
}
}
}

// Data

namespace MyDll.Data {
public static class MyTable {
public static MyObject GetObjectById(int id) {
MyObject obj = null;

// Insert your data access logic here to query the table and
pass the id argument into the query/stored procedure
// as a parameter. Here I've executed the query as a reader
and will use the reader to generate the model object.

using (IDataReader reader = // Execute reader here) {
if (reader.Read()) {
obj = MyTable.CreateNewMyObject(reader);
}
}

return obj;
}

static CreateNewMyObject(IDataReader reader) {
return new MyObject(
(int)reader["id"]);
}
}
}

// Model

namespace MyData.Model {
public class MyObject {
int _id;

public MyObject(int id) {
this.Id = id;
}

public int Id {
get { return this._id; }
set { this._id = value; }
}
}
}

i've found that creating a static method that simply creates the object by
your data access methods in the data class makes it a lot simpler to update
in the future. if you need to add any fields to the database (which i'm sure
you will) you won't have a huge headache trying to find all the places where
you create the object. Simply right click the method name, find all
references, and voila - easy way to find which methods create the object and
which queries/stored procedures you will need to update.

With this setup you'll need at least 1 data object and 1 model object per
table. Depending how you want things setup in your business layer will
determine how many business objects you will need.
 
J

jehugaleahsa

Did you download the code (complete working example) from the blog?

I did look at them, just now. I've gone down that route before.
I'll answer the second question, since that isn't obvious from the
downloadable code.

Yes, the business object has the capacity to have alot more than just simple
property/value mappings.
When I say capacity, what I mean is that alot of times they are simply just
persisted instances of the db data.
However, you can add-on and write properties that have little to do with a
simple property/value.

That was not my second question. That is more of an answer for my
first question: do you just have a list of private fields?

My second question asked how you mapped DB types to .NET types, for
example, such as NUMBER(10, 4). Do you have an automated way of doing
this? or does your database hold binary numbers.
 
S

sloan

The IDataReader has a method called .GetValue( )

Usually I know the db_type/.net type from memory.

When I don't know, I do this.


object o1 = idr.GetValue( 1 );
string s = o1.GetType();
//also "watch" o1 in the debugger.

Where idr is the IDataReader. And 1 is some ordinal number.

Then, after it reports back what it is....lets say it was a boolean...I swap
out the more granular method.

bool b1 = idr.GetBoolean(1);

Something like that.


.....

Every once in a while, you come across a weirdo situation. That debugging
method above usually helps.

I had a weird situation where the resultset was a UNION ALL....

Select Int1 as SomeInt from dbo.MyTable1
UNION ALL
Select Int2 as SomeInt from dbo.MyTable2

If the first table had rows, but the second table didn't, it would come back
as a Int32.
If the second table had rows and the first table didn't, it would come back
as a Int64.
If both had rows, it would be a Int32.

I DID verify the column types for Int1 and Int2 were int and not bigint. It
was a weirdo thing.

I ended up casting them specifically to int to address my issue:


Select convert(Int1,int) as SomeInt from dbo.MyTable1
UNION ALL
Select convert(Int2,int) as SomeInt from dbo.MyTable2

which worked.

Take my report with a grain of salt, its from memory. But sometimes the
GetValue was an Int32 and sometimes it was a Int64, I do remember that.
and I used the "object o1 = idr.GetValue(1);" trick to figure it out.
 
J

Jeff Winn

Typically in situations where you're creating unions between queries it's
the developers responsibility to ensure that the code they write will
interact correctly with the data layer. For example, making sure if the data
layer expects the values to not be null when creating the data all of the
fields need to make sure no null values come into the application
accidentally - same thing when doing joins. A lot of the time you'll see
people writing outer joins and forget to make sure the data on the joining
table doesn't bring nulls into the application if they weren't intended to
be there which will probably throw an exception once it hits your data
layer. As for type mapping, after you work with the data layer long enough
you'll end up memorizing the conversions anyway so that isn't a huge
problem.

Typically when i'm adding new features to our applications, i tend to map
out the database layout in visio (and how it will tie into our existing
schema) and check with the report builders to ensure that the schema will
work for what they'll be doing. Once the schema has been finalized I start
creating the tables, stored procedures, set up the permissions, create the
model layer objects, create the data layer objects, and finally create the
business layer object(s) to tie it all together. Sounds like a lot of work,
and it is, which is why a lot of people are going the ORM (object relational
mapping) route by using NHibernate, LINQ to SQL, or another similar example.
I would advise against using indexes when using readers, yes it's probably a
little faster, but if the tables have been normalized properly and the data
layer has been built correctly finding the fields in the objects will not be
a significant performance increase over the hassle of making sure the fields
never change indexes in all of the queries. Of course this always depends on
what the application is doing, and if there are points in the data layer
where you need the extra speed (ie pulling down hundreds of thousands of
records) i would simply get the index of the fields before reading all of
the data from the reader via the IDataReader.GetOrdinal(string) method.

(int)reader["some_id"] is the basic syntax of our records come from the
database and into the data layer

I haven't used NHibernate, but the amount of overhead that LINQ to SQL
requires versus the time you'll need to invest in building the model and
data layers is well worth the time savings. I'm just not a big fan of them
primarily because you lose the ability to use a truely limited user account
that connects to the server. Granted, you can still use stored procedures,
but it always just looks strange to me. As for the queries, they'll be
stored directly in your data layer rather than being stored on the server -
so some bugs that may slip in due to a query being wrong won't require a
full build to be released to fix it. But that's neither here nor there, it
really all comes down to what you want to do.

var results = from o in context.Addresses
where o.Address.Contains("a")
select o;

roughly translates to:
SELECT *
FROM dbo.Address o
WHERE o.Address LIKE '%a%'

If you do plan to write all of the layers by hand, you might want to
purchase CodeSmith and create some templates for all of the CRUD members
you'll be writing. Saved me quite a bit of time once I got the templates
created, especially since I'm a big fan of writing Intellisense comments on
pretty much everything.
 
A

Arne Vajhøj

We have a fairly small shop where I work. Most of our applications are
database driven, taking data from a database table and displaying it
on a form for editing. We are trying to apply a 3-layer architecture,
but I feel like we are missing something.

For us, business objects have always been classes with properties for
each column in a table. However, from what I have read, it sounds like
this really isn't a very good definition for a business object. The
reason I say this is because I feel as though the business object is a
little too aware of the database and has no real business logic
associated with it.

For instance, a "business object" could simply be a bunch of private
fields for each column with public properties for each. At some point,
the database results must be stored to the business object. The data
access layer may deal with assigning all the properties, but it still
seems like the business object is just a mapping of the database
fields. Many authors make it sound as though business objects are more
than just representations - that they have methods for executing
business logic.

Well, obviously, somewhere the transition from the database to an
object is needed. So, I'm not sure what is bothering me here. The
question is, where is all our domain logic? In our applications, like
I said, we essentially just dump the database information to a form.
In that case, can there be such a thing as a business layer?

Business objects are objects in the domain model/domain layer.

(domain model if being part of business logic layer, domain
layer if it is separate with a service/application layer on
top of it)

To me it sounds as if your problem domain is very thin
in true business logic. This cause the business logic
layer to become very "passthrough" of nature.

Maybe you just need 2 layers. The standard 3 layers
are a good rule of thumb, but it is not the one and only
truth for all apps. Sometimes 2 is sufficient. Sometimes
you need 4 or 5.
Our business objects almost always look like this:

MyBO:
<list of properties>
<static method for select>
<instance methods for insert, update and delete>

That is really data access layer not business logic layer.

And you should split the data carrying from the SQL executing.

And if you switch to an O/R mapper, then you will not even need
to write the SQL executing code.
Perhaps our applications just lack the domain logic. Is creating a
mapping of the database table sufficient to be considered a domain
layer?
No.

Another question: Over the time we have been developing, we stopped
using private fields for our values. Instead, we pass a DataRow/
IDataRecord to the business object's ctor. We write our properties so
that all they do is index into the row/record. This has worked great
because it saves us from either passing a large number of values to
the ctor or setting each property individually. While this saves code,
it means our business objects suddenly need to know a lot more about
our databases. Is this a bad thing? Does this move our business
objects more toward the data access layer than the domain layer?

Yes and yes.

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top