|
From: frebe on 15 Jan 2008 04:31 > We have a disconnect. Your model is typical of how one uses CRUD/USER > layered model infrastructures. That sort of processing is not a good > target for OO techniques. How do we know if an application is CRUD/USER or not? The first version of the application might look like CRUD/USER, but what do we do in the next version when we realize the this is not the case? Start from scratch again? > The developer still needs to provide a mapping, though, between those > Forms and RDB tables and queries. Typically that is done in the business > layer where one mixes classes like [Invoice] and [InvoiceCollection] and > uses relationships to enforce the inherent referential integrity rules. > That works fine for CRUD/USER processing because all the mapping is > essentially 1:1. No harm; no foul. Isn't views a good tool for providing mappings othan than 1:1? > But mixing paradigms is usually a very bad idea from an OOA/D > perspective once one is outside the realm of CRUD/USER processing. > That's because the data structures needed to optimize the customer's > problem solution are usually not the same as those needed to optimize > data storage or display and the 1:1 mapping breaks down. The database schema should be optimized for the customer's problem solution. Optimizing techniques for non-functial requirements, like denormalization for performance, is almost always a bad idea. > I point all this out because it reflects a major disconnect between > OOA/D and the RAD infrastructures. So far your application sounds like > classic CRUD/USER processing. In that case using the RAD paradigm and > infrastructures will probably save you a ton of keystrokes. Indeed. One could also argue 80-90% of all business applications seem to be CRUD/USER according to your definition. > But that > will necessarily result in different models than I would use as an OOA/D > guy when solving non-CRUD/USER problems. Yes, because an OO model would be a network model (with pointers between object/records), and not a relational model. //frebe
From: frebe on 15 Jan 2008 09:04 > invoiceSet = this -> R1 WHERE (date=20080113) > FOREACH invoice IN invoiceSet > // process invoice objects or select invoiceid from invoice where customerid=? and date=20080113 > paymentSet = this -> R1 -> R2 WHERE (date=20080113) > FOREACH payment IN paymentSet > // process payment objects or select paymentid from invoice i join payment p on i.invoiceid=p.invoiceid where customerid=? and p.date=20080113 > when the user wants payments. [Note that the WHERE clause filters the > date attribute in the target set. When a multistage path is navigated it > is assumed that the membership of the intermediate collections already > limits the set adequately. If not, one needs to do the second fragment > in two stages.] Using an SQL database we don't have to bother about limiting the volume of data. In this case we can assume the indexes to do a pretty good job, O(log n). > This is fine so long as the filter is simple, such as a date. Each > collection is optimized for efficient access via a date. As you point > out, it gets somewhat messy if there are multiple filters (e.g., one > also may want payments where the amount was less than some percentage of > the outstanding balance). Using relational algebra (SQL) it wouldn't be messy. select paymentid from invoice i join payment p on i.invoiceid=p.invoiceid join customer c on c.custid=i.custid where customerid=? and p.date=20080113 and p.amount < c.balance*0.10 > Then the collection class needs an interface > for each distinct selection criteria. Each accessor may need a > specialized implementation as well. Yes, the OO (network) solution is indeed a mess. > However, that is pretty much why collection classes exist; they manage > collections. That's pretty much why databases exists, they manage data. > More to the point, that is /all/ they do; they encapsulate > the collection management in one place. IOW, the collection class > isolates and encapsulates complexity of a highly focused nature. In > general that is a Good Thing. Yes, that's why we uses databases. > However, at OOP time one addresses other issues like performance and > maintainability. Suppose Invoices and Payments are very rarely added > compared to how often the various selection criteria are accessed. In > that case it may well be more efficient to have multiple 1:* > relationships in parallel, each with its own optimized collection > mechanics. Thus one has R2A optimized to access by <arbitrary> date and > R2B optimized to access by some amount criteria. For example, one > collection is sorted by date while the other is sorted by amount. The > overhead of adding the same entry to each collection is <hopefully> > small compared to accessing the same elements many times. What about integrity? If a bug causes an exception after adding the payment to the collection sorted by date, but before adding to the collection sorted by amount? What if, you modify the amount and forget to update the collection? What if you add another search cirteria, and forget one of the add/ modify/delete methods? > Maintainability may also drive the use of (1) simply because the > internal mechanisms for optimizing around several disparate criteria are > just making the collection object too complex to modify. Yes, indeed. > Bottom line: in OOA/D one wants to think generically about (2) without > worrying about the details and the OOA/D notations are designed around > that. But at OOP time one is addressing other issues and those may drive > using (1). Bottom line: Use relational algebra for data management. //frebe
From: jimbalo22 on 15 Jan 2008 23:15 Thanks for your input. > Bottom line: Use relational algebra for data management. What I am getting out of this thread is basically, to quote a book title, "There is no silver bullet." I've trying to solve every problem with OOA/D and it has worked well so far, but with this application it has become painfully clear that it is simply not a good fit for the problem domain. Using Datasets & DataViews for the part of the app that deals with the direct user interaction seems to simplify things a great deal. I plan to then pass the Dataset / DataView to appropriate business layer classes for manipulation as needed (for example when the user requests to change paydate for all payments for a certain vendor and day). In other words, a kind of hybrid approach. Jim
From: H. S. Lahman on 16 Jan 2008 14:04 Responding to Jimbalo22... >> We have a disconnect. Your model is typical of how one uses CRUD/USER >> layered model infrastructures. That sort of processing is not a good >> target for OO techniques. The RAD IDEs have already abstracted the >> invariants of the processing in terms of Form/Query/Table and have >> provided a lot of reusable infrastructure that one would have to develop >> from scratch using OO techniques. In addition, such infrastructures are, >> at best, object-based rather than object-oriented. > Interesting. If OO is the wrong tool for this job, it would explain > why I am having a hard time coming up with a smooth design for this > (that and the fact that I still have much to learn in the OOA/D > arena). I don't know that it is the wrong tool. But it /sounds/ like it is from the problem description so far. >> One way the difference is manifested is in separation of concerns for >> different problem spaces. In an OOA/D context the model I provided would >> be relevant in a subsystem that modeled the customer's business problem. >> The rendering of the display would be handled in a different subsystem >> that modeled the particular display paradigm. So for a GUI one might >> have a model like: >> >> [Display] >> | 1 >> | >> | R1 >> | >> | contains >> | * >> [Window] >> | 1 >> | >> | R2 >> | >> | Control >> | * >> [Control] >> >> where [Control] is subclassed into things like [ListBox] and [Grid]. In >> the GUI subsystem the only business semantics exists as labels and >> titles for particular instances that are provided by the business >> subsystem or, more commonly, through a configuration file. > When you say "subsystem" do you mean layers? The app is 3-tiered. Yes. A layer is just a single subsystem encapsulating a single subject matter. The RAD IDEs use 1D layers because the problems they are solving are inherently less complex than general customer problems. However, once one is outside the realm of CRUD/USER the layers become more complicated so that they are no longer semantically homogeneous. So the 1D layer structure becomes a 2D subsystem structure. [The category on Application Partitioning in my blog provides more description.] > >> Your [InvoiceCollection] and [PaymentCollection] entities organize data >> for the display and would map into instances of [Window] and [Control]. >> (In fact, they would both likely be GridControl instances.) > Yes they do organize data for display, but also for operation - the > user can select one or many vendors or invoices or payments and > perform some action on these (like move payment dates or remove > payments, etc.). That's where the RAD and OOA/D paradigms diverge. Display is a different subject matter with it own unique paradigms (e.g., Window/Control for a GUI and Page/Section for a web browser). Those paradigms are abstracted quite differently than the business environment (e.g., objects like Invoice and Payment). That is why I suggested that they might be called something different, like GridControl. More important, the functional semantics is different. In a UI subsystem the functionality is expressed in terms of displaying data and user choices (clicking the Save button). IOW, the semantics is about display rendering through an OS window manager or a web browser. There is no business problem semantics; that only appears in the form of text strings for things like labels and titles that the UI simply renders. So the GridControl objects would have no functional responsibilities for business operations. Those responsibilities would all be in the Invoice and Payment objects in the subsystem that abstracts the business rules and policies. Conversely, the objects like Invoice and Payment would have no functional responsibilities for display. All they would share is data that is passed back and for between them and they each uniquely interpret that data in terms of their subject matter. >> But mixing paradigms is usually a very bad idea from an OOA/D >> perspective once one is outside the realm of CRUD/USER processing. >> That's because the data structures needed to optimize the customer's >> problem solution are usually not the same as those needed to optimize >> data storage or display and the 1:1 mapping breaks down. So in OOA/D one >> would be very careful to put [Invoice] and [Payment] in one subsystem >> and [InvoiceCollection] and [PaymentCollection] in another subsystem. >> That manages the mapping complexity by separating the concerns of >> different problem spaces (display vs. business) so that one can focus on >> one paradigm at a time. One would also encapsulate the subsystems >> strongly so that objects in one subsystem would have no idea that the >> objects in the other subsystem even exist. > Sorry, but I am not sure if I follow the last part: If > [InvoiceCollection] contains [Invoices], how can it be unware of > [Invoice]? [GridControl] (nee [InvoiceCollection]) doesn't contain invoices. It contains a bunch of data values that somebody outside the UI thinks should be displayed. It knows nothing about the semantics of those data values. All it (or someone in the UI) knows is how to map values from those external messages into its internal attributes and, possibly, how to talk to the window manger or browser to get them rendered. [GirdControl] may have relationships to other objects, but they will be other display abstractions like Window. IOW, the relationships describe how the display elements play together, not how the business elements play together. Obviously there must be some sort of semantic mapping between the UI and Business views since ultimately they are both processing the same data (or at least subsets of the same data). So there must be some scheme for identifying messages and the data in their data packets. But each subsystem can map that data into attributes of its own objects in a unique fashion so that the only thing the subsystems share is a view of the message data packets. Using a pure message-based data transfer interface does wonders for decoupling the implementations of subsystems. That is why you InvoiceCollection in the UI doesn't have to know that Invoice even exists in the Business subsystem. The downside is that one needs to encode the data packet on the sender side using its mapping and decode it on the receiver side using the receiver's mapping. That is very general and provides great decoupling, but is gets a bit silly if the mapping is always 1:1, as it invariably is in CRUD/USER processing. So the first thing the RAD infrastructure providers did was to simplify the encode/decode into things like MS' DAOs (i.e., copy This attribute to That attribute). From there is didn't take long to borrow composition from functional programming and use inheritance to provide different views of the same data across the layer boundaries. Now the boundary was barely visible. So it wasn't long before RAD developers eliminated the boundary by mixing display and business abstractions directly and making the display objects "smart" in a business sense. That leads to GridControl -> InvoiceCollection with business operations. > Also, would you mind elaborating on how you would separate the > collections from the elements they contain into different subsystems? Hopefully the above discussion sheds some light on that. > Do you know if C# (VS 2005) lends it self well to this concept? I am a translationist, so I quit writing 3GL code back in '87. B-) The last language I learned was C++ and I can't speak to C#. >> I point all this out because it reflects a major disconnect between >> OOA/D and the RAD infrastructures. So far your application sounds like >> classic CRUD/USER processing. In that case using the RAD paradigm and >> infrastructures will probably save you a ton of keystrokes. But that >> will necessarily result in different models than I would use as an OOA/D >> guy when solving non-CRUD/USER problems. (When I need to solve a >> CRUD/USER problem I forget about OO and use a RAD IDE.) Thus I can't >> really express the points I was trying to make in terms of your model. > That makes sense. I actually had a similar thought earlier: to > simply use a Dataset with DataViews for filtering. I have not used a > DataSet in a long time, but I guess I would have to pass the dataset / > dataview into business layer methods for the execution of operations > on the data (such as the InvoiceCollection.ChangeAllPayDates(DateTime > newDate) method I currently have). What are your thoughts on this? I am not sure exactly what you mean and I don't know very much about the application, so it is difficult to speculate. But I don't see any obvious problems and one might well make use of the Strategy pattern via something like: * extracts from R1 1 [DataSet] ----------------------- [Filter] A | +--------------+-------... | | [ForPaydates] [For...] where [Filter] produces a DataView (which might be a message to the UI) for various contexts. [Apropos of the discussion at the end of this message on performance, I could see an optimization where [DataSet] was actually [Payment]. That is, I could see a different family of filters attached to Invoice and Vendor. Each would extract information from the set in hand and would apply unique selection criteria. But that would not address the fundamental optimization problem because each flavor of [Filter] would have to deal with the same set ordering of [DataSet]. The only way to optimize extraction of subset members is to order the set from which they are extracted in some fashion.] > By the way, what is your RAD IDE of choice? For the last 20 years before retiring I was an R-T/E guy so the only RAD problems we saw were support tools for the software process (e.g., defect tracking). For that mundane stuff I usually used Access. I haven't looked at the market so I wouldn't know what to use for something like a web-based POS where one has both networking and server issues. (There wasn't much around back in the '70s when I was doing IT, but I'm sure we've come a long way since.) >> Assuming one needs a total paid by a vendor for a particular date across >> all vendor invoices, I don't see a need for InvoiceCollection, >> PaymentCollection, and SetFilter. If we had >> Vendor::getTotalPaymentsByDate(date), all that method needs to do is >> "walk" the R1 and R2 relationships in my model. For each Payment >> accessed one then just checks the date and, if a match, accumulates the >> payment amount. If one needs a total for all vendors, we just need to >> iterate over the vendors and sum the results of calling >> Vendor::getTotalPaymentsByDate. > AFAIK, there is no way to use an argument with databinding. Example: > one of the gridcolumns is bound to > VendorCollection.PaymentTotalForDay; I don't think you can bind it to > VendorCollection.getTotalPaymentsByDate(date). But I might be wrong - > if you (or anyone) knows how to do this, please let me know. You're not wrong. This is just another manifestation of the divergence of our design views. The answer is related to the encode/decode I talked about above. I don't have a binding problem when I separate and encapsulate the UI. When the user selects a particular display, I see the UI sending the Business subsystem a message like "The user wants to display the Payment Window. Here's the value the user supplied: <value>". The Business subsystem's interface (think: Facade pattern) would interpret the message ID to mean a certain pile of data needs to be collected and that the value supplied was a payment date. So the Business interface would re-dispatch a message to some object that knows how to collect that data from the Business objects. That object would be a business entity that corresponds to your [VenderCollection]. It just wouldn't be a display object; it would be some business space entity like [Product] that would have a bunch of vendors associated with it. That object would have a 1:* relationship to [Vendor] in my model. The method invoked would proceed to collect all the relevant data by "walking" the relationships in my model with a set of nested iterations that access all the relevant Payment objects. Each Payment would be checked for the date and, if matched, its amount would be accumulated into a <temporary> total value. Once the method collected all the required data, it would package it up in a message data packet to send back to the UI, "Here's the pile of data you requested." The UI interface, in turn, would decode that data packet and assign its values to GridControl and whatnot. Once the data packet was decoded, then the UI interface would invoke the appropriate method on some display object to get those values rendered in the display. So the binding is entirely in the message identity mapping in the respective subsystem interfaces. >>> I know, but I guess I was looking for a way to separate out all the >>> filter-specific responsibilities in such a way as to simplify and make >>> the design cleaner, but it seems that doing that just adds more >>> complexity. >> I am not sure where the filters come in. But I am probably looking at >> the solution quite differently. > By "filter-specific responsiblities" I mean methods for retrieving and > working with a subset of the collections (like the SetFilter method > and the PaymentTotalForMonth). OK, if I understand you correctly, this sounds like the DataSet/Filter application of the Strategy pattern I mentioned above. >> One way to think of it is that there are three pieces of the problem. >> One is to get a set of objects that are of interest. This is just a >> matter of relationship navigation, which is orthogonal to class >> semantics. That is, wherever one needs to construct, say, a Payment >> total, one needs to find a relationship path to the relevant Payment >> objects and navigate it. That is just a matter of "walking" the >> collection classes that implement the relationship path segments. >> >> The second piece of the problem if to extract a subset of the navigable >> objects based on problem constraints (if necessary). This is the >> "filtering". One tests attributes of the collection resulting from >> relationship navigation and prunes those that are not relevant. >> >> The third piece is to actually do something with the objects, like >> accumulating a total, once one has the relevant subset. >> >> My point here is that the "filtering" is just part of the relationship >> navigation. That is, the attribute testing is done as one accesses each >> object. That requires no special objects. (If the testing is complex, it >> may be delegated to another object, but it will always be invoked >> synchronously by whoever is doing the ... > Yes, this is basically what I doing now. One problem I am running > into is that the performance is not that great due to the many > traversals of the various collections. I will profile and see what > can be done (caching is tricky in this scenario). If I cannot resolve > the performance issue, I will probably look into using a DataSet & > DataView instead since they might be better tools for this job. Ah, performance raises its ugly head. It may well be that you need direct relationships or multiple relationships, each optimizing the search through the collections. Each relationship becomes a separate collection class with a unique sort. [Company] | 1 | | R1 | | supplied by | * [Vendor] -----------+ | 1 | | | | R2 | | | | pays | | * | [Invoice] | R4 | 1 | [equal, R2, R3] | | <ordered> | R3 | | | | paid by | | * | [Payment] -----------+ * extracts payment dates for The R4 relationship collection is constrained to have the same elements as the set reached by navigating R2 -> R3. But it is sorted by payment date so that the Payments on the given date are grouped and can be found quickly. This is basically a trade-off between overhead for adding/removing Payments in multiple collections vs. efficient query access of Payment subsets. There are a lot of variations on the theme and which one is best will depend on the specific requirements. However, dealing with performance is orthogonal to the functional requirements. One resolves the functional requirements first and then tinkers with variants of that solution to deal with the nonfunctional requirements. -- There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl(a)pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info(a)pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
From: H. S. Lahman on 16 Jan 2008 14:37
Responding to Frebe... >> We have a disconnect. Your model is typical of how one uses CRUD/USER >> layered model infrastructures. That sort of processing is not a good >> target for OO techniques. > > How do we know if an application is CRUD/USER or not? The first > version of the > application might look like CRUD/USER, but what do we do in the next > version > when we realize the this is not the case? Start from scratch again? The application has some overall problem to solve. Subsequent versions only change aspects of that basic problem. If that problem is essentially conversions between the UI and RDB views, then it is CRUD/USER processing and will likely remain CRUD/USER processing throughout the application's life. If it does, then it is time for a new application because one is solving a different problem. >> The developer still needs to provide a mapping, though, between those >> Forms and RDB tables and queries. Typically that is done in the business >> layer where one mixes classes like [Invoice] and [InvoiceCollection] and >> uses relationships to enforce the inherent referential integrity rules. >> That works fine for CRUD/USER processing because all the mapping is >> essentially 1:1. No harm; no foul. > > Isn't views a good tool for providing mappings othan than 1:1? Depends upon which flavor of views you are talking about. >> But mixing paradigms is usually a very bad idea from an OOA/D >> perspective once one is outside the realm of CRUD/USER processing. >> That's because the data structures needed to optimize the customer's >> problem solution are usually not the same as those needed to optimize >> data storage or display and the 1:1 mapping breaks down. > > The database schema should be optimized for the customer's problem > solution. Such optimization utterly defeats the entire relational database model, which is designed to provide data storage and access that in independent of the way the data is used. (Apropos of the point below, the RDB model is just one form of the more general relational model.) > Optimizing techniques for non-functial requirements, like > denormalization for > performance, is almost always a bad idea. I am talking about optimization for solving a particular problem on the application side, not optimization of RDB schemas. >> I point all this out because it reflects a major disconnect between >> OOA/D and the RAD infrastructures. So far your application sounds like >> classic CRUD/USER processing. In that case using the RAD paradigm and >> infrastructures will probably save you a ton of keystrokes. > > Indeed. One could also argue 80-90% of all business applications seem > to be CRUD/USER according to your definition. That was true in the '60s and CRUD/USER processing is still a major part of IT. But today the proportion has shrunk substantially, probably down to the 20-30% range. >> But that >> will necessarily result in different models than I would use as an OOA/D >> guy when solving non-CRUD/USER problems. > > Yes, because an OO model would be a network model (with pointers > between > object/records), and not a relational model. We've been here before, yet you persist in making statements like this. The reality is that an OOA/D Class Model is normalized using exactly the same relational rules as an RDB schema. The differences between an OO Class Model and a Data Model lie in the way their relational model is constructed, not in whether they are relational models. -- There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl(a)pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info(a)pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH |