I heard a couple of times that putting business logic in database models is bad down the road.
I just read this in blog of a Rackspace employee:
The Django ORM also tended to have us accumulate large amounts of business logic on the model objects, which made building strong service contracts even harder.
But it seems logical to me that if you have an Invoice
model to encapsulate a lot of invoicing logic there. Seems very similar to what we’ve been taught in OOP. Could you show me how this is wrong? Please include a practical example if possible.
Benefit of “put logic in models”
What I see as a big benefit of putting logic in models is that users of the models don’t have to worry about how they can use stuff, they just use it and it works, can’t break anything.
Let me elaborate with an example:
So if you don’t put logic in models, then you probably have this code elsewhere, let’s say it’s in api
. If you want to change a state of an Invoice
, what do you do? Do you do invoice.state = new_state
or do you read the api
code to see if there’s some method there that changes the state?
This is confusing and error prone. How do you fix this issue then?
Wrong is a big word.
The reason why you shouldn’t put it in the DB, is because the vocabulary of (most) DB systems is incredibly limited. There is nothing about “SELECT”, “INSERT”, “UPDATE”, “DELETE” that allows you to fluently express why something is happening and to what it is a reaction, in domain language terms.
As to the second part of your question: the problem with ORMs is that they usually force you to organize your domain objects in ways contrary to what you want.
A simple and common example is that you generally want to encapsulate a lot of stuff inside domain classes, but ORMs need access to the fields and properties of classes to be able to persist them, so you wind up exposing everything, potentially breaking encapsulation.
What I generally tend to find a good idea is to separate your ORM model from your domain model and create a way to inflate domain model objects from ORM model objects.
2
The problem with Django models (or rails active record its the same pattern) its that this is not a behavior model of your application, its a data model, with this architectures your application are tightly coupled to the database. Problems of this types of applications:
-
The design of the database dominates the design of your application. Normally you end up with one table -> one class.
-
As a consequence of the previous design, when the application grows the “model” classes tend to grow and grow, the typical “fat model” problem.
-
Your business logic and your persistence technology are totally coupled, you cannot change one thing without changing the another.
-
You view the database as a global data storage, you can access every table/model from every point of the application. When the application grows this type of systems are really difficult to maintain because of the highly coupling. Kent Beck wrote about this a long time ago: http://www.threeriversinstitute.org/blog/?p=338 , he call this type of systems “connected” vs a more moduler approach.
For easy database centric applications with not complex domain models Django ORM (or rails active record) can do a good job. The problem is when your system grows, a lot of people in the rails and django community are looking at things like hexagonal architecture or Domain Driven Design for finding ways to deal with complex domain.
For an intro, this is a great talk from matt wayne (cucumber author): http://www.youtube.com/watch?v=CGN4RFkhH2M, its about rails, but, its exactly the same problem.
2
Its a similar question to “should do you do validation in the presentation layer?”
And the answer is the same – generally business logic needs to go in the middle tier, but there are always exceptions where it is appropriate.
If you’re putting a lot of logic in the data layer then you’re doing it wrong, you’re polluting the data access and retrieval code with a load of other stuff that doesn’t belong there and you’re writing a client-server style program.
In OO terms, your middle tier should be taking a “raw” Customer data object and a set of Product objects and combining them to produce an Invoice object that is sent downstream. Note that there is no Invoice data directly stored in the DB, just customers, products and their orders.
Its a little bit of a poor example, as apart from combining data into new object there’s no business logic being applied (unless you have complicated tax rules maybe) and if all you’re doing is jiggling data then its a great example of what should go into the Data layer. A rule of thumb could be “if you have to modify that data then it has to go in the middle tier”
5