Database Not More Global Than Classes

It is suggested by some (such as CantEncapsulateLinks and FundamentalFlawsInProceduralDesigns) that RDBMS data is "global", but accessing similar information through classes is not "global". A class interface to an entity is just as global as an entity in a database from a developer's perspective. If there are techniques to limit which file or user can use which class or method, those are specific to your language or system, and not an inherent property of OOP itself. Class interfaces are essentially as global as any database entity through a database interface (such as SQL).

As far as limiting or validating RDBMS-given data by user or other constraints, there are triggers, referential integrity, AccessControlLists, etc. (See RaisingExceptionsToTheUser).

If a class wrapper around entity info has most of the typical access primitives (add, change, delete, etc.), which it probably needs to be useful, then one can do just about anything "wrong" to the entity that they could with SQL.

Untrue, since the object model is a model, it would only provide one way to accomplish an appropriate task, preventing you from doing it wrong. If the only way to add an item to an Order is Order.AddItem?(anItem), there's simply no way to do it wrong.

There might be a "proper" method to do a particular task, but there is nothing inherent in OOP that prevents a developer from using the other more primitive methods to do something the wrong way. OOP cannot read minds.

Yes there is... it's the model itself, there is no more primitive method. When you work with a model, you simply can't do things wrong.

Further, RDBMS integrity, constraint, and validation rules are enforced regardless of which language or tool uses the information. Its enforcement of rules is more "global" than a language-specific solution. In that sense, it is more "global".

You need to quit thinking of add, remove, update, and delete as database operations, they are not, they are simply operations, and these are the fundamental operations that business rules are tied too. These operations are as common in ram, in models, in other datastructures as they are in a database. We build object models because it is the only way to enforce very complex rules globally on those operations. SQL simply isn't capable as a language to accomplish what a real language can. Until you quit thinking of these as database operations, you'll never get it. Databases don't own those four actions, nor are they somehow being reinvented whenever we do them in memory. Databases are great at managing lot's of data, but they aren't good at managing lot's of business rules.

It is not a matter of "owning" the so called DatabaseVerbs, but bringing consistency and discipline to them. Each class can reinvent them in a different way. If there is a huge expressiveness tax to relational rules, as you imply, I have yet to see a solid example of the limits in code. Until I see failures, I see no reason to abandon the consistency card. All else being equal, go with consistency. That is logical.

Sure, but as I said, all else isn't equal. The database doesn't let me add complex business rules to those verbs, code does. That's the failure, that's why code wins. Code let's me "force" rules on those verbs that can't be bypassed within the model, thus if you only use the model, you can't do anything wrong.

Nobody said databases are to handle every action. It is a matter of proper DivideAndConquer, something that OO seems to ignore, mixing nouns and verbs without a discipline (OoLacksConsistencyDiscussion).

I would also like to see a nice demonstration of the model reigning in improper behavior. That is not a common pro-OO claim. Perhaps we can squeaze out a nice new HolyWar battle over such a claim.

Databases are more "global" than simple RAM. If a program, no matter what paradigm it was written in, allocates a block of RAM for its own immediate use that is less "global" than creating a record in a database. It isn't practical to ignore RAM and keep all state in a database. -- EricHodges

So you re-enter all your customer information if somebody trips over the power cord? Note that there are potentially temporary tables and local "result sets" for task-specific usage. See also ProgrammingWithoutRamDiskDichotomy.

He said that it isn't practical to ignore RAM and keep all state in a database, not that it is practical to ignore disk and keep all state in RAM. -- JonathanTang

I can't keep all state in a database, and I don't think you can or do either. If you have some non-toy code that stores all of its state in a database and none of it in RAM, I'd love to see it. -- EricHodges

Actually, many web-based apps are almost like this, out of necessity. I've got one that uses RAM basically for TemplatingInPhp and string manipulation, and practically everything else is in the database.

Not to say that I like doing this. It makes some things - like storing user input before they've completed a form - ridiculously difficult. But PHP is stateless, so there's not much else I can do. -- JonathanTang

It doesn't keep any state in RAM? It reads a byte from the network and writes it to a database? It doesn't calculate anything, decide anything, etc.? -- EH

I did say "uses RAM basically for TemplatingInPhp and string manipulation". The template library is - guess what - an object. But it's a black box as far as my code is concerned, and about the only local variables I need to declare are templates. -- jt

Local variables are state. -- EH

PageAnchor: interface_relativism

Tables are a giant collection of global data. A table can't be compared to an object because an object can carry their rules with them, objects are behavioral entities. Tables can too in the form of triggers, the difference of course is in the expressibility of the language. An object allows full encapsulation and protection of it's data, a table doesn't.

Can you identify a specific failure scenario? I will agree that in some RDBMS, triggers etc. have limits under conditions. But, even with such vendor-specific limits, the multi-language scope of such rules offsets their short-comings. Besides, like it says below, you cannot force a developer to use the "proper" method. RDBMS tend to take a different approach and enforce rules about data instead of (trying to) regulate by limiting the methods available.

It's not about security, it's about encapsulation and enforcement of business rules. That's why a table is a big global variable while and object isn't. It's quite easy with an object model to ensure invalid items can't be placed into an order no matter how complex the rules, the same cannot be said for and order table and item table unless you try and encode all the business rules into triggers, which is damn near impossible because SQL doesn't do business rules anywhere near as well as a real language.

Again, I would like to see a specific scenario. Note that some of this perhaps belongs under GateKeeper, which is more about protection than globalness.

Again I'll say... anOrder.addItem(anItem), it's that simple, in an object model there could be hundreds of complex rules hidden behind that simple call, calculation of taxes, discounts, all of which may have their own complex rules about when they are valid or when they expire etc. An object model puts a structure into place that makes this all simple to enforce, and impossible to forget. Databases can't do that. "Insert (arg, arg, arg) values(arg, arg, arg) into OrterItems?" simply can't be compared, nor can it hide the complexity of the business rules like the object model can.

SQL forces one to keep all business rules in mind each time you write a statement, view some data, change some data, delete some data. Object models don't, they can simply be used, they won't allow you to do anything wrong, if I view anOrder.Total in a model, I know it's correct, I don't have to worry, but if I "select sum(item.price) from item where orderid = 3", I have no such guarantee, in fact, it probably should be "select sum(item.price * quantity) from item where orderid = 3", of course, it's probably more complex than this, and this is exactly what happens when developers use SQL, suddenly your developer is spending a week trying to figure out why the reports don't match the order screen. Models hide complexity and make programming easier, anOrder.Total is far easier than any other alternative.

Because of the "Widest Method Access Principle" (see toward the bottom of GateKeeper), one can probably find a way to total it "wrong" using an Invoice class or invoiceItem class. Further, in practice different groups may want different totals. For example, maybe sales compensation does not depend on sales tax, and so the sales department wants a sum without taxes in it. You seem to be against globals, yet you are proposing that there is One Right Global Interface, such as one and only one right way to total. Again, I belong to the EverythingIsRelative school of thought.

As an example, I used to strive for the concept that every record should have a generic "description" attribute of some kind. (A polymorphic "toText" method in a way.) However, sometimes people/situations wanted long descriptions, other times short descriptions, other times translated descriptions that were more customer-friendly than the internal people needed, etc. Eventually I realized it was easier to just "calculate" them as needed (per task) rather than strive for the One Right Global Generic Description dream. No single description will ever make every stake-holder happy. Einstein was right. Just breed Einstein and Dr. Codd together, and the result will understand what I am trying to say.

Nope, now you got anOrder.Total and anOrder.TaxLessTotal?, both using the exact same code, with Total simply summing TaxLessTotal? plus taxes. You still fail to see the simplicity of an object model. They allow code reuse in ways sql can't, because sql doesn't provide a real language to work with. There is only one right way to total, there may however be different totals the domain is interested in. Let's put it this way... one first builds an object model representing the domain... this model is now the language we use to solve problems. As a language, the object model works better than sql to solve problems in the domain. The object model language is custom tailored to the problem at hand, sql isn't. Until you start seeing an object model as a language, you just won't get it. SQL is a mini language to represent data manipulation, and it works great at that, and an object model is a language to solve the exact problem at hand and it's great at that, better than SQL, since it's tailored to the problem.

When you say, "You still fail to see the simplicity of an object model", you are right. I wish to see some specific code. I already know you think SQL+procedural is the devil's ass, but I want to see for myself. (Note that I don't think SQL is best possible relational language, but that is another topic.) Further, I see little purpose in clutter up your class with customTotalJustForX methods all over the place. Unless they solve OnceAndOnlyOnce, keep them task-specific IMO. Further, does totalOrdersForCountryX belong under an Order object, or a country object? You will have jealous noun fights. See PrimaryNoun. The relationship between nouns and verbs in the real world is many-to-many. OO ignores this fact of life. And, SQL versus code is perhaps a side issue, but still related. I am suggesting calculating totals where they are needed instead of up in some centralized place. If there is duplication, then create a function to share. We don't need SQL to create a function.

I see no jealous noun fight, it's the orders data, so the order get's the method and country.. etc become arguments. Polymorphism let's us use the same message for any number of input objects... I think most of the problems you seem to find with OO, are in your head, or lack of experience building OO based solutions, which we know you never do.

let's compare languages...

Basically it comes down wanting to work with a language that's custom tailored to the problem at hand. We view the model as a language, you may like select, update, insert, delete for every problem, it may seem simpler to you, it may seem more standard to you, that's fine. But OO'ers like to build languages, then solve the problem in the language, given a schema as input, most of us don't even build the model by hand, it's generated, thus it's not extra work. We also find working with the model far more fun than working with sql, because it requires less work from us, and is easier to reason about because we personify the entities, and find it becomes easier to remember things about them. If your brain doesn't work like this, fine, use sql. But it's been years man, why are you still harping on OO, just let it be, it's clear you don't want to understand OO, you just want to criticize it, and frankly I just don't understand where the motivation for that comes from. OO isn't going anywhere, it is the dominant paradigm in the industry, if anything replaces it (doubtful), it'd be functional, or a nice mix of functional and OO, but c'mon man, you aren't going to bring down OO. Give it up! You seem to have an abundance of energy, why not put it to something more useful like promoting your own style rather than knocking other styles?

- AnonymousDonor

As far as the popularity of OO, see MainstreamOopUsage.

You imply that using SQL results in more total code. It does not. Your list of examples leave out a lot of intermediate steps, such as finding which object to instantiate. Thus, as a comparision tool for code size and effort, I find your examples misleading. And they assume a predictable pattern of variation-on-theme, which is often unrealistic. As far as building a SubLanguage, I do it all the time. It just tends to be local to need rather than application-wide, although I can do that too if needed. SQL is already a sub-language of sorts that provides a standard way to provide CollectionOrientedVerbs. Much of your examples are just collection handling hard-wired into names and not really domain-specific after all. You are just not seeing it for what it really is IMO. You are losing an opportunity for reuse and consistency by mixing domain-specific issues with collection-handling issues.

  class Order < ActiveRecord::Base
    has_many :items
    def total {|i| i.price * i.quantity}.reduce :+

# then you'd retrieve the total for orderid 3 using: Order.find(3).total # equivalent to the SQL query from above: select sum(item.price * quantity) from item where orderid = 3

  def total
    items.sum "price * quantity"

Also, I will indeed wrap *common* SQL idioms behind functions with compact signatures. It is OnceAndOnlyOnce 101. But I don't believe in having a bunch of little methods called only once with names like:

That will create more code, not less, because you are creating a wrapper for stuff that may only be used once. Thus, you incure the cost of both the implementation and the wrapper and reuse won't come into play to justify it.

Plus, I can do things like:

  criteria = "taxState not in ('TX','CA','NY')
              and invcTotal > 500";
  print("Total:", getTotalOrders(criteria));
  function getTotalOrders(criteria) {
    return query(stdDB,
    "select sum(amt) from Invoices,
     lineItems where invoiceID = invoiceRef and (%1)",

A simple sub-language AND flexibility together in one. If you can top that, I will be impressed.

  Criteria aQuery = new Select(Tables.Invoices)
      new And(
        new Not(Invoices.taxState,'TX','CA','NY')
        new Greater(Invoices.invcTotal, 500)));
  print("Total:",aQuery.Execute().Inject(0,function(last, each){return last+each.amt;}));

Not that I would ever do such a thing, but you seem to be under the impression that we don't do queries in OO, that we somehow write a method for each task, you are mistaken. Having an OO query API that simply wraps SQL in a type safe manner allowing us to avoid the evil strings you use, is quite common. You still don't get why we don't like SQL, because it's a string. Yes yes I know you like dynamic languages but so I'll let that one go, I like static languages that are flexible and don't like strings because the compiler can't find them when they're broke.

It's not missing the join, it doesn't need the join, since it's getting the invoice, and asking it for the total.

      criteria = "taxState not in ('TX','CA','NY')
                  and productRef not in (select productRef
                      from recalledProducts)";
      print("Total:", getTotalOrders(criteria)); ....

As far as generating CrudScreen applications from schemas, I have done similar things using DataDictionary's. However, it is more flexible to use the declarative model directly rather than turn it into code (CodeGenerationIsaDesignSmell), although non-OO code generation is still a possibility and I doubt it would be more total code than OO. You may personally prefer OO, but it is not objectively better. It might be "in style", but code even in languages like Java tends to be procedural in practice.

Only to you, to many of us, they are OO in practice.

(Moved comments about VB to ThingsWeHateAboutVbClassic)

A database can basically be viewed as one giant GodClass with instance variables for every property and methods for every operation. Classes don't typically allow queries of internals based on arbitrary expressions, but it's possible to give a class generic methods that allow anyone to create new methods from other kinds of primitives. Collections classes need these. In that context, it behaves as and results in problems similar to a GodClass.

Relational is as much about constraints (rules) as it is about abilities. Sure, one *could* put methods in to make classes resemble RDBMS, but one is relying on the designer and conventions to stick to the rules. I don't see much beyond TuringEquivalency in such a notion.

(Moved some material to FrustrationOverNotGettingOopDiscussion)

Declarative Interface versus Imperative Interface

Aside from the issue of whether access to the table is as "global" as access to a class (above), part of the difference is that databases generally have a "declarative interface" and OOP generally has a behaviorist interface. OO thinking sees behavioral interfaces as superior because one can wrap what they see as "raw" data with behavior so that one can change the underlying source of the information without changing the interface. Technically one can do the same with table views, and even more so with column-views if they were supported more widely, but this is missing the point and probably would not change the landscape much. The data interface approach leaves most of the behavior to relational algebra, which has its own benefits, namely consistency in terms of CollectionOrientedVerbs being a standard. I don't see enough drawbacks to a declarative interface approach to give up the consistency and inter-language share-ability it brings. I am still awaiting some good behavioral interface show-off examples. Relational trusts its operations to be sufficient for most work while OOP thinks that every object needs custom ones.

Generally with a relational-centric design, "actions" are split into standardized CollectionOrientedVerbs (relational algebra), and application-specific tasks. OO does not make a distinction between these, seeing them all as behavior to be tacked on to an entity. CollectionOrientedVerbs perhaps are associated with one and only one PrimaryNoun in some cases, but app-specific tasks are generally not assumed to be associated with any PrimaryNoun. OO sees the association as a good indexing and/or packaging technique, while relational-centric design tends to see it as arbitrary. This is because:


[Hmmm. Maybe some of the above can be moved to PrimaryNoun.]

See also: GateKeeper

CategoryScope, CategoryBusinessDomain, CategoryExample, CategoryOopDiscomfort

EditText of this page (last edited July 20, 2014) or FindPage with title or text search