Having one Base for Business objects

Having one Base for Business objects

Old forum URL: forums.lhotka.net/forums/t/1843.aspx


patingamon posted on Monday, November 27, 2006

I am trying to determine the best method for setting up my business objects. I will use a previous post's Pet Store scenario to setup the problem.

Assume we have business objects Store, Cage, Animal where there may be one or more Stores. Each Store has one or more Cages and each Cage one or more types of Animal.

It stands to reason that a Store might have numerous business rules, attributes, methods, etc.

For example, a Store (whether it is read-only or editable) needs to be able to list the set of Cages, display the address of the store, generate an inventory, etc.

Only an editable Store can have Cages added or deleted (and, for that matter, have the contents of Cages modified).

Because BusinessBase and ReadOnlyBase are distinct, it appears that I will need both an EditableStore (derived from BusinessBase) and ReadOnlyStore (derived from ReadOnlyBase).

That seems to imply that the only way I can have a common Store business object between the two is to have both EditableStore and ReadOnlyStore contain the Store. This doesn't seem right as an EditableStore ISA Store and a ReadOnlyStore ISA Store.

Am I missing something obvious? Is there a clever strategy to support this?

Thanks for any insights.

RockfordLhotka replied on Monday, November 27, 2006

In my view you should not have a common Store object, because objects are defined by behavior, not data. And you are describing two very different sets of behaviors. Different behaviors mean different objects.

This is a common theme for the questions I get, and the root of the issue is that most people learn a type of object design that is very data-centric. In my view, and the view of people like Cunningham, Beck and West, this data-centric design approach is fundamentally flawed. Objects should drive off your use cases, and should reflect the responsibilities and behaviors defined by those use cases.

Of course I read sentences like that many times and didn't grok the concept. Until I realized that reuse is NOT the goal of OOD. Reuse, if it happens, is a fortunate side-effect. But the real goal of OOD is to create maintainable and readable code - which reduces the cost of development and maintanence. And THAT is the goal.

Translated, this means that reuse of objects across use cases is not a primary goal. If you happen to find that you can reuse an object that's a bonus. But even then, you have to view such a move with skepticism because such sharing of an object tightly couples two use cases to each other, and that reduces maintainability...

DansDreams replied on Tuesday, November 28, 2006

RockfordLhotka:

Objects should drive off your use cases, and should reflect the responsibilities and behaviors defined by those use cases.

That seems like such an obvious point to me, but it's amazing how experienced high-quality developers can't see it.

Ask yourself this... if you walked into a client to beging scoping and designing a software project, do you start by asking "what data do you want to store" or by asking "what do you want this thing to do?"  If you're starting with use cases, it seems so obviously natural to progress from

use case -> business layer to support use cases -> data to support business layer

than from

use case -> data to support use case (indrectly) -> business layer to support use case

IMO in the first case the flow is so much more natural, as demonstrated in the section of the book in which Rocky designs the object model for PTracker.

RockfordLhotka:

Translated, this means that reuse of objects across use cases is not a primary goal. If you happen to find that you can reuse an object that's a bonus. But even then, you have to view such a move with skepticism because such sharing of an object tightly couples two use cases to each other, and that reduces maintainability...

I think that's clouded somewhat by the principle that a particular piece of responsibility should exist in only one place.  It's easy to see that as the same thing as code reuse.  Or should I say, reuse of objects jumps out as a way to achieve that.

So, if I have a Customer.LastName that concatenates the firstName and lastName fields in the Customer object, and I also have a CustomerSummary.LastName in my read only list picking list... it seems the easy OOD solution is that Customer would just aggregate the CustomerSummary object and therefore a) not have to duplicate the LastName business logic and b) then naturally also achieve code reuse.

One of the problems with that approach is that while you've achieved some code reuse, you've also added a bunch of code to handle the aggregations, particularly in regard to the crud operations since a complex business object is now composed of a half dozen objects that can now operate independently or in this mode.

david.wendelken replied on Tuesday, November 28, 2006

DansDreams:
RockfordLhotka:

Objects should drive off your use cases, and should reflect the responsibilities and behaviors defined by those use cases.

That seems like such an obvious point to me, but it's amazing how experienced high-quality developers can't see it.

Ask yourself this... if you walked into a client to beging scoping and designing a software project, do you start by asking "what data do you want to store" or by asking "what do you want this thing to do?"  If you're starting with use cases, it seems so obviously natural to progress from

use case -> business layer to support use cases -> data to support business layer

than from

use case -> data to support use case (indrectly) -> business layer to support use case

IMO in the first case the flow is so much more natural, as demonstrated in the section of the book in which Rocky designs the object model for PTracker.

I've seen some really brittle software application designs that were driven by use cases instead of the underlying data.  The first cut of the application worked ok.  And the (apparently unexpected but incredibly common) response of the users was, "Great, now that the immediate problem facing me is solved, I need to do X, Y, and Z to the data."   Except the application had to be completely gutted to do that because the data was structured to facilitate the A, B and C use cases instead of being structured to support its true nature.

I think that making behavior definition primary over data definition is just as bad a mistake as making data definition primary.  Data and behaviour are equal in importance, and have to be respected as such.

For that reason, I prefer this flow:

goals & objectives & problems & risks
-> desired capability (at a high level, both immediate and future)
     -> use case design and data design in parallel
          -> business layer to support implemented functionality

That way, both behavior and data get equal attention on their own terms.  The process of reconciling the two with one another (and to the desired capability) in order to design a business layer catches mistakes caused where only one viewpoint on the problem would have missed a critical feature.

ajj3085 replied on Tuesday, November 28, 2006

david.wendelken:
I've seen some really brittle software application designs that were driven by use cases instead of the underlying data.  The first cut of the application worked ok.  And the (apparently unexpected but incredibly common) response of the users was, "Great, now that the immediate problem facing me is solved, I need to do X, Y, and Z to the data."   Except the application had to be completely gutted to do that because the data was structured to facilitate the A, B and C use cases instead of being structured to support its true nature.


It would seem to me that X, Y and Z would then generate completely new, independent objects from the previously built ones... unless X, Y and Z somehow invalidated the previous use cases. 

But your wording implies that is not the case, so it would seem to me that the objects were built off of data, not business requirements, because X, Y and Z should get their own objects, and the previously built ones should remain completely unchanged unless X, Y, or Z modifies the previous use cases.  If you're gutting the previous objects, again, it sounds like the new use cases completely invalidated the previous ones.

That's my take on the scenario you describe, and it doesn't sound like a failing of the 'behavior over data' design philosophy.

david.wendelken replied on Tuesday, November 28, 2006

ajj3085:
david.wendelken:
I've seen some really brittle software application designs that were driven by use cases instead of the underlying data.  The first cut of the application worked ok.  And the (apparently unexpected but incredibly common) response of the users was, "Great, now that the immediate problem facing me is solved, I need to do X, Y, and Z to the data."   Except the application had to be completely gutted to do that because the data was structured to facilitate the A, B and C use cases instead of being structured to support its true nature.


It would seem to me that X, Y and Z would then generate completely new, independent objects from the previously built ones... unless X, Y and Z somehow invalidated the previous use cases. 

But your wording implies that is not the case, so it would seem to me that the objects were built off of data, not business requirements, because X, Y and Z should get their own objects, and the previously built ones should remain completely unchanged unless X, Y, or Z modifies the previous use cases.  If you're gutting the previous objects, again, it sounds like the new use cases completely invalidated the previous ones.

That's my take on the scenario you describe, and it doesn't sound like a failing of the 'behavior over data' design philosophy.

You've misunderstood me.  The data model did not support the other tasks that would soon be required of it.  It only supported the initial use cases.  That's because the data was structured ONLY to suit the original use case's convenience.  It did not properly reflect the true nature of the data.

Here's one of the worst examples of behavior-based data design I ever encountered.

A software development company (now defunct for reasons that will become apparent), built accounting software.  For payroll, they had several use cases for preparing a payroll check:  Timecard Entry, Salary, and Manual (and a fourth I don't remember off-hand).  Each use case not only had its own code (perfectly acceptable!), it had it's own permanent data structures in the database - even though all four methods produced the exact same result - a payroll check.

So, when those were done, and they got around to doing Financial reporting, the reporting use cases had to deal with four entirely different data structures to report on payroll checks when only one was necessary.   The entire data model was like that.

And, of course, I'm sure folks can chime in with examples of data modelers gone haywire, building complex edifices without the slightest clue of how they would be used in the real world.  That's why I advocate treating them equally rather than favoring one over another.

ajj3085 replied on Tuesday, November 28, 2006

david.wendelken:
You've misunderstood me.  The data model did not support the other tasks that would soon be required of it.  It only supported the initial use cases.  That's because the data was structured ONLY to suit the original use case's convenience.  It did not properly reflect the true nature of the data.


Indeed, and that's one of the things the business layer is supposed to hide.  If you data model changes, the public API to your business objects should remain the same.. perhaps some new properties and rules regarding those properties, but you should not have to completely scrape the business objects. 

In building a system, you should only build what you need.  At the same time though, the long term needs to be discussed as well.  So your engineers should have known what was to come down the road, and accounted for that if it is certain that certain functionality was going to be built in the next phase.

david.wendelken:
A software development company (now defunct for reasons that will become apparent), built accounting software.  For payroll, they had several use cases for preparing a payroll check:  Timecard Entry, Salary, and Manual (and a fourth I don't remember off-hand).  Each use case not only had its own code (perfectly acceptable!), it had it's own permanent data structures in the database - even though all four methods produced the exact same result - a payroll check.

So, when those were done, and they got around to doing Financial reporting, the reporting use cases had to deal with four entirely different data structures to report on payroll checks when only one was necessary.   The entire data model was like that.

And, of course, I'm sure folks can chime in with examples of data modelers gone haywire, building complex edifices without the slightest clue of how they would be used in the real world.  That's why I advocate treating them equally rather than favoring one over another.

I see what you're getting at now.  I don't think anyone is advocating 'ignore the data model' at all.  The mantra 'business objects are driven by use cases' applies only to building business objects.  That's not to say that the database design is unimportant and should be modeled on behavior though, and I don't think anyone would advocate basing the db design on use cases.  It of course should be done with a focus on the data.  You then have to have BOs intelligent enough to map its state to the data schema.

Andy

david.wendelken replied on Tuesday, November 28, 2006

ajj3085:
david.wendelken:
You've misunderstood me.  The data model did not support the other tasks that would soon be required of it.  It only supported the initial use cases.  That's because the data was structured ONLY to suit the original use case's convenience.  It did not properly reflect the true nature of the data.


Indeed, and that's one of the things the business layer is supposed to hide.  If you data model changes, the public API to your business objects should remain the same.. perhaps some new properties and rules regarding those properties, but you should not have to completely scrape the business objects. 

The DATA model, not the object model, had to be trashed because it was garbage.

The data model was entirely unable to support the new behavior because it was done wrong.

The data model was garbage because the data in it was defined by behavior instead of in its own terms.  And just because you could keep the business layer doesn't mean that much of it didn't have to be rewritten once the data model was gutted and re-done.

 

DansDreams replied on Tuesday, November 28, 2006

david.wendelken:

The DATA model, not the object model, had to be trashed because it was garbage.

The data model was entirely unable to support the new behavior because it was done wrong.

The data model was garbage because the data in it was defined by behavior instead of in its own terms.  And just because you could keep the business layer doesn't mean that much of it didn't have to be rewritten once the data model was gutted and re-done.

It seems to me that what you've described at length is an inadequacy of the original discovery process, and not at all any measure of one development paradigm vs. another.  How on earth could starting with the data have anticipated the new requirements any better than starting with the business layer if the analyst was too careless to dig deeper or the user was too obscure about their requirements?

ajj3085 replied on Tuesday, November 28, 2006

david.wendelken:
The data model was garbage because the data in it was defined by behavior instead of in its own terms.


From the problem you described, the data should have been coming from a 5th table, PayCheck, whose data could easily be calculated from the other tables.  The BOs then could enter a row in both PayCheck and whatever table it needed.  Alternately, it may have been that the four tables really described the same data entity, and should not have been seperated out.

david.wendelken:
And just because you could keep the business layer doesn't mean that much of it didn't have to be rewritten once the data model was gutted and re-done.


If the data model changes, but the original use cases have not, I would think that the only work would be (in Csla terms) in the DP methods, and other internal methods related to data access.   That may be work, but not as much work as starting from scratch, and your UI layer should remain largely unchanged I would think, also saving a lot of work.

If there was data needed that wasn't captured in the initial use cases, I would think that's a failure in the requirements gathering, not with the methodology used to design the business objects and database.

It sounds like this was the case, that not enough data was being gathered by the inital use cases.  I know there must be more to the story, but I really think that was the problem.  And a report needing four different tables shouldn't totally trash your data model..  at worse you should have a messy view, or need to restructure the data... but it almost certainly sounds like a failure in requirements gathering more than anything else.

Andy

david.wendelken replied on Tuesday, November 28, 2006

ajj3085:
david.wendelken:
The data model was garbage because the data in it was defined by behavior instead of in its own terms.


From the problem you described, the data should have been coming from a 5th table, PayCheck, whose data could easily be calculated from the other tables.  The BOs then could enter a row in both PayCheck and whatever table it needed.  Alternately, it may have been that the four tables really described the same data entity, and should not have been seperated out.

You got it in one. :)

david.wendelken:
And just because you could keep the business layer doesn't mean that much of it didn't have to be rewritten once the data model was gutted and re-done.


ajj3085:

If there was data needed that wasn't captured in the initial use cases, I would think that's a failure in the requirements gathering, not with the methodology used to design the business objects and database.

It sounds like this was the case, that not enough data was being gathered by the inital use cases.  I know there must be more to the story, but I really think that was the problem.  And a report needing four different tables shouldn't totally trash your data model..  at worse you should have a messy view, or need to restructure the data... but it almost certainly sounds like a failure in requirements gathering more than anything else.

Double-entry accounting is a well-documented business practice dating back to the 13th century AD.  So, yes, you could say that there was a failure to model the system correctly.  But they knew they had to produce reports from the get go, it wasn't a surprise. 

To get this thread back on the original topic... :)

Objects, from the discussion on this forum, are about defining behavior.  An object may grab some data, alter it, and deposit it in the appropriate place when done.   The focus is on the immediate behavior - how the data is used by that object at that time. 

Data Entities, from relational modeling theory, are about the data that is stored, i.e., the data that is left behind when an object finishes its work.  What information is contained in that entity?  What is its true definition?  Have I partially confused it with another data entity?  How does it relate to other data entities?  What uniquely identifies it?  What data is essential for properly completing it?  (Yes, the usage of the data does matter, but that is not the primary focus.  I *could* choose to calculate the estimated weight in micro-grams of the ink printed on each invoice, but who would care?)  This gives me a different, unique view on the problem domain.  If I get the definition of the data entities right, they have the latent capability of supporting all behavior expected of them.  In other words, as I add new objects to cover new use cases, the data model is already ready for them. 

I do what is called a CRUD matrix (in the database world) for each object.  For each object, I define which data entities it will Create, Retrieve, Update and/or Delete.  If I do this in a data table, I can easily see whether any data entity does not have a use case to create it, or retrieve it, etc.

For larger systems, I do a similar matrix at the data attribute (field) level, and for the same reasons.

Each way of modeling the system provides different insights into the problem domain.  Each will tend to miss different aspects of the system, so cross-checking them against one another is a great way to reduce mistakes.

ajj3085 replied on Wednesday, November 29, 2006

david.wendelken:
ajj3085:
From the problem you described, the data should have been coming from a 5th table, PayCheck, whose data could easily be calculated from the other tables.  The BOs then could enter a row in both PayCheck and whatever table it needed.  Alternately, it may have been that the four tables really described the same data entity, and should not have been seperated out.
You got it in one. :)


I know we're a bit off topic now, but why did the model have to be scraped?  It seems like you should be able to generate the needed data for this fifth table from the 'single entry' system.

david.wendelken:
Double-entry accounting is a well-documented business practice dating back to the 13th century AD.  So, yes, you could say that there was a failure to model the system correctly.  But they knew they had to produce reports from the get go, it wasn't a surprise.


It seems like the reports should have been part of the initial batch of use cases.  Its odd that no one thought of the reporting implications before settling on a model.

david.wendelken:
Objects, from the discussion on this forum, are about defining behavior.  An object may grab some data, alter it, and deposit it in the appropriate place when done.   The focus is on the immediate behavior - how the data is used by that object at that time.


This is a fine way to approach things as well, because all your use cases together should give you a picture of how the data will be used at a later point as well. 

david.wendelken:
Data Entities, from relational modeling theory, are about the data that is stored, i.e., the data that is left behind when an object finishes its work.  What information is contained in that entity?  What is its true definition?  Have I partially confused it with another data entity?  How does it relate to other data entities?  What uniquely identifies it?  What data is essential for properly completing it?  (Yes, the usage of the data does matter, but that is not the primary focus.  I *could* choose to calculate the estimated weight in micro-grams of the ink printed on each invoice, but who would care?)  This gives me a different, unique view on the problem domain.  If I get the definition of the data entities right, they have the latent capability of supporting all behavior expected of them.  In other words, as I add new objects to cover new use cases, the data model is already ready for them.


I agree with you here. 

david.wendelken:
I do what is called a CRUD matrix (in the database world) for each object.  For each object, I define which data entities it will Create, Retrieve, Update and/or Delete.  If I do this in a data table, I can easily see whether any data entity does not have a use case to create it, or retrieve it, etc.

For larger systems, I do a similar matrix at the data attribute (field) level, and for the same reasons.

Each way of modeling the system provides different insights into the problem domain.  Each will tend to miss different aspects of the system, so cross-checking them against one another is a great way to reduce mistakes.

This sounds like a great method.  I don't think the back and forth when designing the business layer and data layer should cause changes to the public API and behavior of the business layer, but its perfectly reasonable that a BO may need to track some extra data so that it can properly update the database.   I think that's all that we mean by the 'BOs are defined by behavior only' saying.  That's all my point was; you don't stop doing data modeling like you always have, you just don't design your BOs according to the data model.

patingamon replied on Tuesday, November 28, 2006

I agree, David. Object oriented design does need to combine attributes and behaviors. Business cases ought to bring to the surface both of these aspects. Unfortunately, they don’t in every case, because it relies on the customer being completely aware of how they want their system to work, or how the current environment functions. Even interviewing a plethora of subject matter experts rarely brings the business model more homogenous – instead it seems to have the opposite effect, requiring the architect to draw dissimilar desires together.

 

All that aside, I didn’t mean to imply in the first post that this was entirely data driven. In fact, just the opposite was the intention. Whether an “object” is editable or not is only one behavior – in fact, I would look at the editablity (ok – that is not a real word) as a current state of the object, not necessary driving its behavior. Granted, an object that is editable does have different behavior than a read only one, but that is external behavior – behavior that is shared by any editable object (perhaps editable/read-only is an interface supported by an object – though I haven’t thought that one through yet).

 

Anyway (sorry for the digression), that is why I list behaviors of the Store instead of data that I wanted in common whether or not the object was editable. I completely agree that the “data” portion may be different. To an extent, it is a view on the object, i.e., what data I want exposed or carried about with me.

 

Very good discussion. Thank you for all your input.

david.wendelken replied on Tuesday, November 28, 2006

However, redundant code also reduces maintainability and reliability...

One professor of mine said "Computer Science is the science of giving away some feature you want for another feature you want more." 

With 2.1+, we can save objects that violate the rules.  This implies that a read-only object, if it is to report on its rule status, also has to access the rule code.  The odds of a read-only object getting updated when the rules change isn't good...  Perhaps a compromise on the two positions would be to put common functions in a static StoreHelper object?    That would at least get any custom code (except for rule initialization) out of the two objects and into a shared object. 

Ideally, each of the Store objects would call a shared rule setup function that would initialize the rules, but I'm not sure that's workable given the Csla architecture.  The right hooks might be too private...

 

Copyright (c) Marimer LLC