Simple question: Please show me a case where inheritance and/or full data encapsulation makes sense for business/domain objects on the implementation level. 

I'll steal the low-hanging fruit: Address. Address is a great candidate when you look at an OOA model as you could model yourself to death having BaseAddress(BA) and BA<-StreetAddress(SA) and BA<-PostalAddress(PA) and SA<-US_StreetAddress and SA<-DE_StreetAddress and SA<-UK_StreetAddress and so forth.

When it comes to implementation, you'll end up refactoring the class into on thing: Address. There's probably an AddressType attribute and there's a Country field that indicates the formatting and since implementing a full address validation component is way too much work that feature gets cut anyway and hence we end up with a multiline text field with the properly formatted address and stuff like Street and PostOfficeBox (eventually normalized to AddressField), City, PostalCode, Country and Region is kept separate really just to make searching easier and faster. The stuff that goes onto the letter envelope is really only the preformatted address text.

Maybe I am too much of a data (read: XML, Messages, SQL) guy by now, but I just lost faith that objects are any good on the "business logic" abstraction level. The whole inheritance story is usually refactored away for very pragmatic reasons and the encapsulation story isn't all that useful either. You simply can't pragmatically regard data validation of data on a property get/set level as a useful general design pattern, because a type like Address is one type with interdependency between its elements and not simply a container for types. The rules for Region depend on Country and the rules for AddressField (or Street/PostOfficeBox) depend on AddressType. Since the object can't know your intent of what data you want to supply to it on a property get/set level, it can't do meaningful validation on that level. Hence, you end up calling something like address.Validate() and from there it's really a small step to separate out code and data into a message and a service that deals with it and call Validate(address). And that sort of service is the best way to support polymorphism over a scoped set of "classes" because it can potentially support "any" address schema and can yet concentrate and share all the validation logic (which is largely the same across whatever format you might choose) in a single place and not spread it across an array of specialized classes that's much, much harder to maintain.

What you end up with are elements and attributes (infoset) for the data that flows across, services that deal with the data that flows, and rows and columns that efficiently store data and let you retrieve it flexibly and quickly. Objects lost (except on the abstract and conceptional analysis level where they are useful to understand a problem space) their place in that picture for me.

While objects are fantastic for frameworks, I've absolutely unlearned why I would ever want them on the business logic level in practice. Reeducate me.

Wednesday, July 07, 2004 3:02:36 PM UTC
Hello Clemens,

If you're looking for a good use of OOA in the business logic area how about double dispatch? Using the architypical example of the bank account. A bank account has many transactions, each transaction inherits from BaseTransaction. Without double dispatch the Account object has to know how to handle each Transaction object. When you deploy your application the Account object can only handle those Transaction that it knows about. In future if you want to expand your transactions you have to deploy the new Transaction and an updated Account object.

If you use double dispatch then the Account object can push back on the Transaction object, something like this...

class Account
{
public void HandleTansaction(BaseTransaction aTransaction)
{
aTransaction.DoTransactionWithAccount(this);
}
}

class NewTransaction : BaseTransaction
{
public void DoTransactionWithAccount(Account anAccount)
{
//Transaction work here.
}
}

This is an area where implementation in the business layer is made easier by the use of OOA
Wednesday, July 07, 2004 3:06:02 PM UTC
Hi Clemens,

In my understanding there is a logical consequence of the separation of behaviour and data. Why do you want to bundle the data and the behaviour in the same object (according to OOP principles) in the first place? Because then you can use powerful techniques like encapsulation and polymorphism.
You call a method on an object and it changes it's internal state. You are guaranteed that the internal state can't change unless you call the method and ***all*** the actions inside are executed. That's the encapsulation benefit - of course, if you are working on a one-man project this does not sound too beneficial, but imagine you create domain objects to be used in different use cases - then if the objects are simple structs with public properties everything can happen to them and the chance to put them in an inconsistent state is big, imho.
The second case can be illustrated like this - you call Person.DoSomething() and the object starts doing it, potentially calling other objects and using it's properties (for example pocket money ;)) in order to the Something. Then you inherit RichPerson from Person and override the DoSomething() method - because the RichPerson has more money it can call other (more expensive) helper objects etc. And this is my simplistic view on polymorphism. How do you achieve it if you externalize the behaviour? You have to make a parallel hierarchy of Person Controllers (or service methods or managers or simply - a class with static procedural methods), so that the correct controller in the controller hierarchy is chosen for the corresponding data object in the data object hierarchy. Things get more complicated if the domain model is more complicated.
To conclude, both approaches (OO and procedural(service-like)) are different but can be used for a certain problem space. It's very difficult for me to build my own opinion about which one should be prefered (not to speak of "educating" or convincing someone else) as I (still ;)) don't have the 30 year experience needed for really getting to the roots of this stuff ;))

Best regards,
Deyan
Deyan Petrov
Wednesday, July 07, 2004 4:43:50 PM UTC
Deyan; I don't believe that you can create domain objects that really make sense in several orthogonal use-case scenarios and not create a horribly convoluted mess with contradicting requirements and cross-use-case "feature bleeding". Been there, suffered lots. There's a job that needs to be done and that gets data and yields a result. And then there is another job that gets data and yields another result. If those jobs are not related then there's no need to join them at the hip and create coupling and interdependencies just because it's more convenient for the developer.
Clemens Vasters
Wednesday, July 07, 2004 11:19:05 PM UTC
Clemens, I think the Service Layer topic of Fowler's PEAA would answer best your last comment. There Fowler mentions the 2 types of business logic - Application and Domain logic. The former should be placed in controller classes, the latter in the domain classes themselves. The important thing to notice is the advice - start putting the logic first in the domain classes and then if you see they become overburdened with functionality or there is some logic specific only for a certain use case, then move it to the service layer. Of course, if we are talking about notifications of external services etc, then these should be placed directly in the service/controller layer ...
Deyan Petrov
Thursday, July 08, 2004 12:23:40 PM UTC
My comments got a little long, so I promoted them to a post...

http://www.cerkit.com/cerkitBlog/PermaLink,guid,c9f70cd6-feac-4881-9c62-6ef8a49ca95f.aspx
Friday, July 09, 2004 4:37:09 AM UTC
Hi Clemens,

Very interesting post! Here are my comments:
http://www.jnsk.se/weblog/posts/toclemens.htm

Best Regards,
Jimmy
www.jnsk.se/weblog/
###
Sunday, July 11, 2004 11:44:11 AM UTC
It's all about the interpretation.

Yes, I generally find that any data interesting enough to persist in a database or serialize as a message is too interesting in its own right to be encapsulated away; every little piece of your code that touches it even indirectly will want to deal with the fields within the data one by one, so at best your encapsulation ends up being a one-to-one mapping of fields to properties, which seems pointless.

But even with the data exposed, there are still occasions for polymorphism. They happen because the data has to be interpreted. To give an example, say you have a table of addresses and a table of GPS coordinates, and you care about how far one is from another. Then it makes sense to have one procedure for each type of data that can deduce an actual location (in whatever common format you find convenient, say 3-D cartesian coordinates). An abstract operation like measuring distances can then work on unrelated types of data.

The trouble is, this really doesn't happen all that much, because most of the time an operation is always performed on a single type of data, which is shared all over the place. To start another example, say you have various kinds of employees, including programmers and executives. Well, each type includes common employee data such as name and salary, in addition to whichever data is specific to programmers or to executives. If you just want to know the salary of each one, usually you just need a single procedure to extract this from the base employee data. Polymorphism is only necessary if you make it necessary by burying this base data inside an encapsulation of each type of employee.

So at most you have some different types of data and some different procedures that operate on them. Whether and how to put this together into objects is not immediately obvious. You could think of these procedures as services, and you could create a wrapper service that accepts any chunk of data, switches on its type, and delegates to a type-specific service. Then the trouble is, what if looking up an address is very expensive, and you want some way to combine whatever data you have with whichever service can interpret it so that later on... you see where I'm going: that's an interface. So, you could pack each type of data into a class, and make the class implement some interfaces. Trouble is, you can never anticipate all the things that might be done with your data, so the single class that encapsulates your data in a pre-defined set of interfaces will never be adequate. IMHO, the worst thing you can do is set up an inheritance hierarchy. These days, with everyone having interfaces, inheritance is no longer necessary as a vehicle for polymorphism, so using it just wrecks your design for no benefit. You end up glomming together distinct things like base employee data and programmer employee data, until the notion of a CEO who writes code is inconceivable (or rather, inexpressible).

So, I do see a small role for objects. For one thing, objects (or in this case structs, as I prefer to think of them) let you deal with strongly typed data. With reflection and a little preparation you can pretty easily move between the worlds of XML, relational data, and objects according to which is handiest. But what we are discussing is the other use of objects. Here, I see objects as a bridge between data and its interpretation. You have an Address struct and a GPS struct, you need an ILocation interface for your nifty UI, so you create an AddressLocation class that implements ILocation through a call to some service and contains an instance of Address that it can pass to/from the service, and likewise for GPS. But again, you might not find this sort of thing worth the trouble except in the cases where you have such wildly different kinds of data for such closely related concepts, and even then it's a pretty thin use of OO.

But you probably already thought of all this, since you started out asking about just two of the three pillars of OO. Encapsulation? Hardly. Inheritance? Don't make me laugh. Polymorphism? From time to time.
Luke Stevens
Tuesday, July 13, 2004 2:41:34 AM UTC
Using your example, I have an address domain object in one of my projects. It has methods that can present an address as a single line, and methods that can be used to break down a full line address into its constituent parts. Considering that I can use this functionality from multiple pieces of application logic, I consider that to be fairly useful.

For me, domain objects should contain very little logic. They are primarily for moving data around in a simple, structured way. I sometimes extend that with some additional calculated properties, but that is about it. Mostly, the domain objects are very cheap to create, because I generate them using a tool, based on the database design (which in turn is based on my domain object design).

I have completely different application/controller objects that deal with processing of the data, and these objects contain a lot of code. That code would be far more complex and ugly if it had to deal with the data mapping directly.
Tuesday, July 13, 2004 10:30:45 PM UTC
I wasn't going to comment here, because i have pretty strong opinions about the whole story and would prefer to organize them into a proper post on my own blog; BUT, I feel compelled to address one quick thing.

Steven; you have an address object with embedded "Domain logic" for presenting the address information in different ways. sounds to me like your embedding presentation logic into your data representation object.

an Address 'domain object' shouldn't include methods for 'presenting' the address info; the presentation logic should be refactored into re-usuable 'views'.

Saturday, July 17, 2004 12:57:52 PM UTC
Hi Clemens ,
Very true. Domain objects does not make sense in several orthogonal use-case scenarios. They should usually be used for only abstraction/encapsulation purposes represting real world and/or database entites for data movement. Whats interesting here is that these entities are primaly used by their adapters/managers and secondarily by the services. These Adapters/Managers are responsible for providing data base (create/update/delete/fetch) logic. These adopters/managers are usually invoked by application/controllers. Now any thing other than create/update/delete/fetch logic like validations, transactions, and workflows go to Services.
Sunday, July 18, 2004 4:51:54 AM UTC
Hi Clemens,

Since I think this is such an interesting discussion, I wrote another blog post here:
http://www.jnsk.se/weblog/posts/toclemens2.htm

Best Regards,
Jimmy
www.jnsk.se/weblog/
###
Friday, July 23, 2004 10:39:46 PM UTC
For a different view of the representation of buisness domain objects (data and behaviour) that suppports encapsulation and reuse, try ModelScope:

http://www.metamaxim.com

Rgds
Ashley
Tuesday, August 03, 2004 4:36:25 PM UTC
I’ve seen quite a few contributions to the debate of «Domain Model» vs. a more traditional data driven approach (as a MS developer – read DataSets + «Table Module», or similar, with the ERDs in focus opposite to UML class diagrams for the static structure).

Why is it that so many practicers of the traditional approach have moved over to «Domain Model», while I can’t seem to find one example of the inverse?
Martin Rosén-Lidholm
Comments are closed.