views:

799

answers:

5

Just finished read this post by Greg Young, where he is talking about Microsoft recommending patterns with dumb data transfer objects. He implied that in the Java community, things are trending the other direction.

My question is how much logic should be in your entity objects? Our philosophy where I work (C# shop) is that if you can't serialize it, don't put it in the entity.

A: 

The main point is how one defines logic. To give some examples:

  1. I wouldn't categorize a function getFullName() in a Person entity, which just concatenates some strings, as logic.
  2. Calculating an order item value would more likely qualify for being logic.
  3. Doing some booking transactions I would definitely say is logic.

Point 1 and maybe 2 would go for me into the entity. Point 3 not. So I define logic as:

  • any operation that does any persistence related thing (read/write)
  • any operation that involves any other (not directly related, e.g. master-detail) entity

IMO, any of these operations don't belong into entities.

Now, why/when I wouldn't put also point 1 and 2 type operations into an entity? It's a rather rare situation, but I wouldn't do it, as soon as the data stored in the entity needs to be interpreted in some way before it can be used by the application (e.g. if depending on current user, content of field X has a different meaning), that means the entity's data itself yields some logic.

MicSim
Master-detail implies the use of an AggregateRoot, which would, in fact, be a great place for logic stored in the domain model.
Ryan Riley
A: 

As far as I understand it, all business logic related to an entity should go into that entity. This consists of any logic that defines the behaviour or internal structure of the entity based on the business rules of the system. This should not include presentation logic or persistance logic (the obvious exception being with the Active Record design pattern) but should include things such as data validation, entity relationships, state machines and other things that define how the entity behaves in terms of the real-world thing it is trying to model.

The way I try to look at it is to try and make my models be as resuable as possible. Always try to think of how the model would be used if it where to be ported to a different system where the client code (or code using the entity) may be different. If the functionality is not part of the entity will it still behave in the same way following the same business rules? If the answer is no then the the functionality should go in the entity.

Tim Wardle
You are correct about Active Record, but like Transaction Script, Active Record is generally seen as a competitor[1] to the Domain Model about which Greg writes.[1] http://martinfowler.com/eaaCatalog/
Ryan Riley
+14  A: 

Matt,

I would say that your shop is writing procedural code. I want to make clear that there is nothing wrong with that many large systems (including many I have worked on) have been written using procedural code. There is a time and place for it.

Now procedural code has no place in a domain model. If you want to use a more procedural style that is fine but use it with something like a Table Module or an Active Record pattern. It is not the lack of OO that I am considering to be so destructive in the guidance but the use of a domain model with procedural logic.

This causes one to spend a large amount of resources building the domain layer (impedance mismatch, thought process time to build aggregates, isolation, ubiquitous language etc) without receiving any of the benefits that the domain layer (generally maintainability) would otherwise provide. In other words while you may meet your functional requirements just fine you end up spending a large amount of your budget with almost no return.

Now to come back to what "is behavior" I would like to focus on the question from an Object Oriented as opposed to a "Domain Driven Design" viewpoint. An object will generally encapsulate some state and will generally expose some behaviors.

quick reiteration: encapsulate state, expose behavior

So what behaviors should an object have? Put simply it should be the behaviors that operate upon the state it is encapsulating. In an ideal behavioral OO world the state would never be exposed from the object only behaviors. Put tactically into code if we start seeing code like:

Customer c = GetCustomerFromRepository();
c.Status = CustomerStatuses.Deleted;
c.LastUpdated = DateTime.Now;
c.UpdatedBy = GetCurrentUser();
CustomerRepository.Save(c);

We have a SRP violation ... This code is code that should be a behavior of the customer object because the "Responsibility" of the customer object is to.

Encapsulate state about a customer and expose behaviors.

As such we can see that we would be better off having a Customer.Delete() method. (yes this is a bad example I know ...)

Now we would also get to this by using TDD. It is much easier for us to deal in tests with the seam that the behavior provides than the seams where all of the state is exposed. The reason for this is that I don't need to duplicate the logic in my tests. The client code doesn't care how a delete works ... it only cares that the customer exposes the behavior. As such in our tests instead of asserting that c.State == CustomerStates.Deleted and c.UpdatedBy==GetCurrentUser() etc etc we would simply assert that the delete method was called on the customer seam by using a mock.

Now to come back to the title. The amount of logic that should be in a business object is the amount of logic that falls under its responsibility of encapsulating its state. Sometimes this is alot, sometimes its not. There are places where you want to use services as well ... a good example would be coordinating the interaction between many domain objects for a given behavior but even here the service should be calling behaviors on the domain objects.

Does this help to clarify things a bit?

Greg

Greg Young
I actually agree 100% with you, but my shop has jumped on the SOA bandwagon, and there is a strong momentum here towards DTOs or message style entities. Would you argue that the repository pattern is inherently flawed? How does unit of work fit in when your Customer objects can save themselves?
Matt Briggs
Oh yeah, and thanks for taking the time to respond in a completely different forum then your blog :-)
Matt Briggs
I think Greg's comment that the Customer.Delete() method was bad means that it's not a method you would generally place on a domain model. (Greg, please correct me if I'm wrong.) That would fit the ActiveRecord model. Repository isn't broken, it's meant for a specific purpose: persisting models.
Ryan Riley
Matt I believe that SOA and DDD are compimentary to each other. Command/query separation will take you a long way towards this. You might also want to look at "Event Sourcing" which goes along with cqs.Eventually you will come to a point that services encapsulate data *and* behavior.
Greg Young
I am not sure I understand your unitofwork question. Unit of work is commonly used in DDD. My bad example about the Delete() is that it wasn't really "Deleting" the customer, it was marking it as being no longer visible ... a Delete operation would tend to be on a repository (jutconfusing thats all)
Greg Young
I think this is a major thing people don't get about DDD: "There are places where you want to use services as well ... a good example would be coordinating the interaction between many domain objects for a given behavior but even here the service should be calling behaviors on the domain objects."
Jonathan Parker
+1  A: 

Lately, I've been toying with the idea of creating domain models that have structure and only those behaviors that are universal for that model (i.e. behaviors that can be used across multiple bounded contexts) with extension methods for the behavior specific to a bounded context. This keeps the domain models close to a DTO (for those that like that) and restricts the use of that domain model to only the allowed behaviors within a bounded context. So that could be an option for a middle-of-the-road response. :)

Ryan Riley
+2  A: 

If you're calling them your "domain model objects" then I will assume that you're refering to Fowler's Domain Model pattern. http://martinfowler.com/eaaCatalog/domainModel.html

Given that assumption, then the answer to your question is "all the business logic" since that is essentially the definition of the pattern.

Unfortunately the term "domain model" seems to have been watered down recently to only mean an object model of your data with no behaviour.

If you haven't already done so, I would encourage you to read PoEAA and decide where you think that domain logic belongs in your situation. If you decide on a domain model, then I would encourage you to read Evan's DDD book and learn about the differences between entities, value objects and services.

Hope that helps!

Stefan Moser