19

This is a question I asked a while back on SO, but it may get discussed better here...

Where I work, we've gone back and forth on this subject a number of times and are looking for a sanity check. Here's the question: Should Business Objects be data containers (more like DTOs) or should they also contain logic that can perform some functionality on that object.

Example - Take a customer object, it probably contains some common properties (Name, Id, etc), should that customer object also include functions (Save, Calc, etc.)?

One line of reasoning says separate the object from the functionality (single responsibility principal) and put the functionality in a Business Logic layer or object.

The other line of reasoning says, no, if I have a customer object I just want to call Customer.Save and be done with it. Why do I need to know about another class to save a customer if I'm consuming the object?

Our last two projects have had the objects separated from the functionality, but the debate has been raised again on a new project.

Which makes more sense and why??

kmote
  • 3,322
  • 6
  • 24
  • 33
Walter
  • 16,158
  • 8
  • 58
  • 95
  • 1
    Could you tell us more about your project ? The nature of your project will determine which solution is better. –  Oct 06 '10 at 09:56

7 Answers7

5

If you consider that a Customer is a part of the domain model, then it makes sense (especially within the context of DDD but not limited to it) to have have both properties and operations for that object.

With that said, however, I think that the example you've used is a poor one, and is a cause of the argument. If you're talking about persistence, then Customers generally don't 'save' themselves; whatever you're using for persistence will. It makes sense that any kind of persistence should belong to the persistence layer/partition. This is generally the thinking behind the repository pattern.** A method like Customer.UpgradeWarranty() or Customer.PromoteToPreferred() makes the argument clearer.

This also doesn't remove the possibility for having DTOs. Consider the situation where you're going to pass customer information to a remote service for instance. It may not make sense for a customer to create a DTO of itself for transport, that's an architectural concern, but a case could be made for it in the persistence or network layer/partition/code/what-have-you. In that case, such an objectcould have methods that look like this

public static CustomerDTO GetDTO(Customer c) { /* ... */ }

public static Customer GetCustomer(CustomerDTO cdto) { /* ... */ }

So, in summary, it makes perfect sense to have operations on a domain object that are congruent with logical operations in the domain.

Google for 'Persistence Ignorance' for a number of good discussions on the matter (this SO question, and its accepted answer is a good place to start).

** This gets a bit muddied with certain OR/M software where you are forced to inherit from a persistent base that has a 'Save' method.

Steven Evers
  • 28,200
  • 10
  • 75
  • 159
3

I think both ways of doing it can have their benefits, but I would look at it from an object oriented or domain driven view: what are the responsibilities of the different classes and objects.

So, I would ask myself this, in your concrete case: should a customer know how to save itself?

To me, the answer would be no: logically to me it doesn't make sense, and a customer should not have to know anything about any persistence framework at all (separation of responsibilities).

As for SnOrfus' examples with Customer.UpgradeWarranty() or Customer.PromoteToPreferred(), they are obviously more business logic oriented than Customer.Save(). There are different approaches to this, but again if you ask yourself if a customer is supposed to be able to upgrade his warranty, the answer might be both yes or no, depending on how you look at it:

  • yes: a customer can of course upgrade his warranty
  • no: a customer could ask to be upgraded, but the upgrade is done by someone else

Back to your original question; which way makes more sense will probably depend on who you ask and their preferred method, but the most important thing is probably to stick to one way of doing it.

In my experience, though, separating data and (business) logic makes for a simpler architecture, although not as exciting.

Vetle
  • 2,185
  • 15
  • 19
3

I actually just recently reworked some code to separate the objects from the data. The reason is that the data is obtained through a separate service, and it is much easier to just pass the raw data to/from the server instead of passing the entire object back and forth and having to deal with validation errors on the server.

For small projects, having objects contain their business logic and data is probably fine, but for large projects, or projects that might expand over time, I'd definitely separate the layers.

Rachel
  • 23,979
  • 16
  • 91
  • 159
2

I think you're specifically talking about the difference between the ActiveRecord pattern and the Repository pattern. In the former, the entities know how to persist themselves, and in the latter, the repository knows about persistence. I think the latter offers a better separation of concerns.

In a broader sense, if the entities act more like a data structure, then they shouldn't have behaviors, but if they have behaviors, then they shouldn't be used like a data structure. Example:

Data Structure:

class CustomerController
{
    public int MostRecentOrderLines(Customer customer)
    {
        var o = (from order in customer.Orders
                orderby order.OrderDate desc
                select order).First();
        return o.OrderLines.ToList().Count;
    }
}

Non-Data Structure:

class Customer
{
    public int MostRecentOrderLines()
    {
        // ... same ...
    }
}

In the first case, you can navigate the data structure tree of your model without a problem, from anywhere in your working code, and that's ok, because you're treating it like a data structure. In the latter case, Customer is more like a service for a given customer, so you shouldn't call methods on the result. All the things you want to know about the Customer should be available by calling a method on the Customer object.

So do you want your entities to be data structures or services? For consistency, it seems better to stick with one. In the former case, put your "service"-type logic somewhere else, not in the entity.

Scott Whitlock
  • 21,874
  • 5
  • 60
  • 88
1

The answer is really going to depend on what your architecture/design is - a DDD architecture with a domain model is going to be very different from a CRUD data-centric model when it comes to object design.

In general though, bear in mind that in object-orientated-design you are trying to encapsulate state and expose behaviour. This means that you will usually try to get the state associated with a behaviour as close as possible to that behaviour - when you fail to do this you are forced to expose state in one form or another.

In a domain model you want to separate out the business model from infrastructure concerns - so you absolutely want to avoid methods like '.Save'. I don't remember the last time I "saved" a customer in real life!

In a CRUD application then the data is the first class citizen so a ".Save" is entirely appropriate.

Most sizable real-world applications will have a mixture of these paradigms - domain model where there are rapidly changing or complex business rules, DTOs for transferring data across boundaries, CRUD (or something between CRUD and a domain model, such as activerecord) elsewhere. There is no one-size fits all rule.

FinnNk
  • 5,799
  • 3
  • 28
  • 32
1

I cannot really answer your question but I find it funny we are also having this discussion in one of our projects at school. Myself would like to seperate logic from data. But lots of my classmates say the object has to hold all the logic AND data.

I will try to summerize the facts they bring up:

  • A business class object represents a thing, so all data AND logic should be contained. (eg if you want to open a door, you do it yourself, you don't ask someone else. bad example I know)

  • Easier to understand the code and functionality of a bu object.

  • Less complexity in the design

I am telling them they are lazy and I would make it like this:

  • Yes business classes represents things, so it holds data. But it feels wrong to save yourself, or even copy. You cant do that in rl.

  • Making the object responsible for everything makes it not good for durability and maintibility in the future. If you have a seperate class that is responsible for saving, if you add a new object to the design, youcan easily implement its save funtion. instead of coding it all again in that class.

  • By having one object able to persist data, that object can hold a database connection and all database traffic is guided at that object. (basiccally its the data acces layer) otherwise all the Business object would have to hold a connection. (which adds complexity)

Well one way or another, I am going to ask a teacher. When I have done that, I will post his answer here too if you would like. ;)

edit:

I forgot to mention this project was about a book store.

Emerion
  • 361
  • 1
  • 7
  • Your classmates are right, except that they're confusing business logic with other forms of logic (in this case, architectural or persistence logic). – Steven Evers Oct 06 '10 at 19:31
0

I've been mulling over the same question for a while - I think the data component and the logic component must be seperate. I believe this because it gets you into the right frame of mind as regarding business logic as an interface to the data that provides meaning.

I'd also mod up Scott Whitlock's point from above (except I have no points being a new member), data or business logic classes shouldn't really have to worry about how the object is stored persistantly.

That being said, if you're dealing with an existing product, as long as you have clean, contractual interfaces - that's OK and maintainable too...

bly
  • 606
  • 7
  • 6