The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

How do I create/read/update/delete a business object?

I have a business object called Customer and if I want to create a new Customer , should I go:

in c#

1. Customer customer=new Customer();
  customer.Name="Smith";
  customer.Age = 22;
  customer.Create();

Where Create() method is defined as:

    DataAccessLayer.CreateCustomer(this);

Where DataAccessLayer is the helper object for  interfacing with database

OR

2. Customer customer=new Customer();
  customer.Name="Smith";
  customer.Age = 22;
  DataAccessLayer.CreateCustomer(customer);
 
OR neither??
Urumi Send private email
Tuesday, December 19, 2006
 
 
Either one is fine.
johnny
Tuesday, December 19, 2006
 
 
Another alternative, which is I won't say is "better" necessarily, is to bottle up your second example with a special collection class.  Imagine if you made a class that has a reference to a List<Customer>.  Your class could have a method: void Add(Customer c) that adds to the member list as well as calls your DataAccessLayer.CreateCustomer(c);

The benefit here is that your main logic need only care about creating Customers and adding them to an instance of a smart list, further insulating your business logic from your database logic.
Notorius L
Tuesday, December 19, 2006
 
 
I prefer the first one.  I think the purpose of an API is to isolate you from the details.
Almost H. Anonymous Send private email
Tuesday, December 19, 2006
 
 
I like to use a CustomerRepository class. To quote from Domain Driven Design, a repository class "provides the illusion of an in-memory collection, ... decouples application and domain design from persistence technology, ... allows easy substitution of a dummy implementation... "
Mike S Send private email
Tuesday, December 19, 2006
 
 
I always do the second one. I am just used to this way.
Full name
Tuesday, December 19, 2006
 
 
+1/2 to Notorious L and Mike S.

The collection and CustomerRepositories may be overkill, but they've got the right idea. Ideally you do want to be able to "disable" creating the actual data access layer for testing and debugging purposes. However, the best place to do that really depends on how you've structured the entire program (in the sense of which functions are library functions).
TheDavid
Tuesday, December 19, 2006
 
 
Mike S appears to be talking about what I am, only he did it better.

For your example, imagine that DataAccessLayer was no longer a big honking pile static methods, each tied to specific domain objects.  You could instead have generic interface (I'm assuming C# here, but I bet it translates to Java/etc) named DataAccessLayer<T>.  CustomerDAL would implement DataAccessLayer<Customer> and would have a method satisfying the interface such as void Create(Customer c).  In this case, Customer is the "T" that the interface is expecting.

Then you have a class that immidates List<T> (the resizable array class) by wrapping it, and by accepting a DataAccessLayer<T> in the constructor.  FancyList<T> may have an Add(T item) method that adds to the private List<T> and then calls Create against the private reference to the interface.

The nice thing about this is that now you can make stub implementations of DataAccessLayer<Customer> and use those in your unit tests, or even use those while developing your user interface.  With this degree of separation between domain logic and SQL, you could have a UI developer implementing their feature completely before a single line of SQL is written, and then swap in the 'real' implemention of the interface once it's ready.
Notorius L
Tuesday, December 19, 2006
 
 
@Notorius L: Thank you. I like your model.
Urumi Send private email
Tuesday, December 19, 2006
 
 
Or you could just do it in Rails...

customer = Customer.create(:name => 'Smith', :age => 22)
N
Tuesday, December 19, 2006
 
 
One of the creational patterns. For example:

Customer c = Customer.Create("John", 22);

The where Create is a static factory method. It can add the customer to the data layer, or just fire an event to which the data access layer is subscribed also. Of course the constructor of the customer is not public, so the only way to create a customer is this.
foldip
Wednesday, December 20, 2006
 
 
really? ruby is that easy?
Urumi Send private email
Wednesday, December 20, 2006
 
 
Blech.  This fine grain style of ORM mapping is the most boring, useless things ever created.  One-to-one mappings of your database tables to application objects make for a slow, downright useless data access layer.  Every time I try making these stupid "Customer" "Order" things, I throw it away or leave it untouched.

Why not instead create useful objects?  How often are you only going to need "Customer"?  Never.  You really wanted "The orders from customer X placed two weeks ago that had 3 or more pairs of brown socks in them".  Having "Customer" objects would leave you with a million tiny objects floating around with a huge mess of application code.  The "hammer the database" factor would be insane unless you spent years creating some silly "smart ORM" code that recreates the database in your application code.

Why not *use* that sophisticated database you have installed! Nobody buys a hi-def TV and watches VHS!  A couple twenty table joins never hurt anybody!

Come up with objects that give you data you really want.  Nobody wants "Customer."
Cory R. King
Wednesday, December 20, 2006
 
 
Cory, I agree with you....
now the example is a little simple,
but the main reason why you would do such mambo-jambo, is to take data validation rules related to the business, out of your DB. Like:

//-----------------------
Customer c = new Customer("Smith", 22, "M");
Product p = ProductFactory.getProduct("123");
Account a = new Account(c, p);

CheckResult[] r = a.checkCanSave();

if(r!=null)
    throw new UnpleasantCustomerException("Smith cannot ask for an account", r);
else
{
    string connString = "theAccountDB";
    IDataConnector dc = new AccountDataConnector(connString);
    a.save(dc);
}
//-----------------------

the same applies to update/delete actions, other than just "create" .....
this way, if you change or migrate your db, you can reuse your datavalidation rules :)

bye!
Johncharles Send private email
Thursday, December 21, 2006
 
 
"Come up with objects that give you data you really want.  Nobody wants "Customer." "

Cory, thanks for answering incorrectly for the rest of the world. Some of us WANT "Customer". If it's in your database and you will never "want it", why put it into the database in the first place?
johnny
Thursday, December 21, 2006
 
 
"this way, if you change or migrate your db, you can reuse your datavalidation rules :)

Who the hell migrates databases?  I like my database platform, thank you very much.

I'm more worried about when my company has another application which needs to hit that data... you know, the one that *isn't* written in the same language or even on the same platform.  How is your spaghetti code ORM going to handle that?  *That* is a real business constraint my friend.  Hell, I'd be more worried about migrating the current application to another language than migrating the database.  Aren't you worried about getting "locked in" to your ORM?

You just lost a ton of *real* performance gains, created significantly more code to maintain, made it a ton harder to add another application, hammered the database server, forced us into writing a bunch of caching systems, and for what?  A hypothetical "migrate the db" scenario.

I'm sorry.  I just don't get this ORM hype.  Database servers are 90% standard.  You choose which one to use based on the other 10%.

All I'm suggesting is a less granular approach to your objects.  Yes, you shouldn't scatter SQL hithertoo - that is a no-duh.  However, I'd expect to see some pretty fat queries in your objects.  Your example is a bunch of "SELECT <stuff> FROM <one table>" x <number of classes>, which is just unacceptable.  That crap is way to close to the database. 

The only way to get the proper amount of abstraction is writing very specific, highly focused queries wrapping them into objects.  In my world, if we are talking MVC, M is just the schema, the C is views and stored procs thinly wrapped up in objects.  Take those nice wrappers and shove them up to the template system for the view.

* This argument does not apply for retail software like FogBugz or any other app that must run on a variety of systems.  Thank god I never have that constraint :-) *
Cory R. King
Thursday, December 21, 2006
 
 
well..... we have an OLTP-like system based on MSSQL, and now volumes are slowly getting on the physical edge of locking perfs....
we already did all the "best practice" stuff: careful locks, stored procedures, early closing connections,.... everthing

so, the main idea is that Oracle could be better. Or we are thinking about refactoring parts of the DB schema for better perf...
In both cases, we hope to reuse that middleware layer. We just "hope" as you say, but still - I think it's no good to play the "lazy programmer"....... we shouldnt be afraid of writing code :)
Johncharles Send private email
Thursday, December 21, 2006
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz