Entities, Services and what goes between them...
A developer I am working with had added a method Customer.FindOrdersByCity() and we got into a discussion why it wasn't the correct place for the method. This dicussion has made it clear that I have never really sat down and wrote what I though about what guidelines I follow with regard to good application design.
Let us start with the definations:
- Entity - an (usually persistable) object that is part of the application model. Usually the application is based around actions on entities or by entities. Examples: Customer, Order, Employee
- Infrastructure Service - part of the underlying infrastructure of the application, usually handles concerns that are common to many applications . Examples: DatabaseService, LoggingService
- Application Service - part of the core application, handles business concerns that are usually specific for the application. Examples: OrdersService, CustomerService, BillingService
- Controller - responsible for coordinating a cohesive part of the application, usually a single user interaction, but can be larger than that. Examples: NewOrderController, CustomersController
- Views - responsible for converting the data from the controller into something that the consumer can understand. This is not just a user, I have WCF services as views, among other things.
There isn't much to talk about with regards to infrastructure services, I put them in Rhino-Commons and forget about them :-) Controllers/Views I have already talked about at length.
In general, I would like to keep my entities clean of dependencies on services. I like to put business logic only in the entities, and use the surrounding framework to get what is needed (usually this means lazy loading) without the explicit involvement of the entity with the service layer. This means that they are light-weight, independent of the infrastructure, and can be tested independantly.
Application Services are interesting, they generally use both the infrastructure services and the model to do useful stuff. They may contain business logic, if that is the best place to put it, although I would rather have it in the entities. They are usually responsible for setting things up so the entities can do their work.
As usual, the best examples are code, so let us take a look at the Order class:
public class Order
{
public virtual Money CalculateCost()
{
Money cost = Money.Zero;
foreach (OrderLine line in OrderLines)
{
cost = cost.Add(line.Cost);
}
cost = cost.Add(this.ShippingCosts);
return ApplyTaxes(cost);
}
}
The business logic is in the entity, but what does the service does, then?
public class OrderService
{
public virtual Money CalculateCostForOrder(int orderId)
{
Order order = Repository<Order>.FindOne(
(Where.Order.Id == orderId).ToDetachedCriteria()
.SetFetchMode("OrderLines", FetchMode.Eager));
return order.CalculateCost();
}
}
This is a very rough draft, but you get the idea, I hope. The service loads the entity, making sure to load the OrderLines collection with the entity (to save another query later) and delegate the calculation to the entity.
What did I gain here? I could have just put the calculation directly in the service, couldn't I? I could do that, but then I would have lost the OO capabilities that I have with entities (vs. Data Transfer Objects). If I wanted to have a OnSaleOrder, with a special discount, I could do this polymorphically, instead of adding conditions to the service.
There is a strong relation between a service and an entity (but hopefully not the other way around), this means that there usually will be a service per entity, but that is not always the case (OrdersService and NewOrderService, for instance) and there certainly will be cases where there are entities that have no service (those will usually not be the aggerate roots of the system, of course).
The service in general has a very procedural interface, although I am starting to consider fluent interfaces and method objects for services as well.
Comments
The main drawback from seperating the service from the entity is that the user of the entity has to know about both in order to do certain things. For example, where does validation happen? There may be an IsValid on the entity, but it may or may not depend on the service having loaded it up first in order for the IsValid to function correctly. An answer to this is possibly to always talk to the service, but this can lead to all kinds of problems, for example when the interface of the entity changes, the interface of the service would likely also have to change.
That said, I prefer this approach because it makes a lot of things easier when the entity is not dependent on any particular environment service.
Entities, Services and what goes between them... [Via: Ayende Rahien ] BPEL support for WF [Via: Tomas...
What would you do if Money CalculateCost() on the Order entity was significantly more complex and as a result you decided to encapsulate the calculation in a separate class. From your postings I know that you use some form of Inversion of Control (Windsor). How would you deal with the dependency between the Order and the new class?
Great post! I like the way Ayende did here. To me, it is always a big issue in designing applications. Usually I create service classes based on Use Case, and leave the methods on Entity to encapsulate entity business logic, as Ayende showed above, or manage relationship with other entities. But still it is very hard to design a consistent API and put Abstraction onto the appropriate classes.
I think we may extend this topic to discuss how to design with rich domain.
In general I would like to avoid needing IoC just for the entities, so I don't think that I would use that for this, though.
If that was the case, I would probably create a CalculateOrderCost method object, which would do the calculation.
There are now two options here:
a/ Let the Order class instantiate this directly - I like this approach since it means that I can override it in derived class, and it is a class refactoring of Big Method To Method Object.
b/ Let the service handle it, and inject the CalculateOrderCost to the service. This is a valid option, but it means that the selection is more or less static. Without starting to go really crazy, there isn't a good way to say "give me the calculator for this type of order", which means that I would have to inject a factory that would make this selection, and I already have a perfectly valid place to put this, which is in the Order itself.
The choice between the two would be driven by how much changes I expect to have, but I would lean toward to method object created by the entity.
@Mike,
That really depend on the scenarios that you have, to take your validation example, I usually have a separate validation service that I use.
What you describe as Entity is in fact a domain model.
Yup, separating validation from the entity has some advantages.
First of all you can choose a validation model/framework of choice and hence you are more flexible when it comes to the presentation of the validation results.
Second you cannot overrule validation logic in a derived entity class which is good as validation for a base class must also hold for all derived classes.
I also tend to inject the validator for a given kind of entity into the corresponding DAO (infrastructure service) as this is usually the last place an object gets passed to before it is persisted and this way you can ensure that only valid objects get saved (even though this object might have already be validated in the GUI, but the additional cost of duplicate validation is usually neglectible)
I don't really see the need for the OrderService in this example, I would make CalculateCostForOrder(int orderId) a static method on the Order class. What benefits are there of having a non-static OrderService class?
Hammett,
Yes, definitely. I am not fully DDD, but I try to stay close :-)
Adam, you're missing the point. Ayende mentioned that there is domain logic and there is application logic. Those don't mix.
Also, a service layer is the entry point for the presentation layer, it might be use-case driven and last but not least it's the perfect place to define transaction boundaries.
@Adam,
That is a good question.
You can say that there are instance responsibilities and type responsibilities, but I don't like to make this distinction.
It still feels to me like you are putting to much responsibilities in one spot.
In addition to that, there is also the issue of trying to change it. You can't override a static method and you can't mock it.
Using a separate class give me more flexibility to change
@Hammett,
How would you define the difference between services and controllers?
I am starting to get a feeling that some of my controllers are actually services, but I am not sure if it is possible to break them apart cleanly. (Take into account that I now work with web forms, so there is a bit that I need to do in the view).
I am starting to think that I might go with "controllers validate input and then call to the service" model, but considerring that the call is made from an aspx page, this mean that I would get:
View.Submit_Click()
Controller.AddNewItem()
Service.AddNewItem(int id, string name);
Controllers manage the application flow. They coordinate the input and output, just as you have described. For simple cases you can have what you depicted:
View.Submit_Click()
Controller.AddNewItem()
Service.AddNewItem(int id, string name);
But for complex scenarios a controller can have a more sophisticated code, and yet do not overlap with services responsibilities.
View.Submit_Click()
Controller.AddNewItem()
Is in Admin Role?
Is Data OK?
Is Account not expired?
Ok, I think get it.
The Order class shouldn't know about how it is retrieved, updated, deleted, etc or any other domain logic; just about it's own application/business logic e.g. how to calculate it's own cost?
I'm still yet to try mocking but I'm probably going to try it out soon so I'll bear in mind that this approach helps with this also.
@Adam,
Domain logic == business logic
CRUD logic == infrastructure logic
So have you read Eric Evan's Domain-Driven Design? I recall a few posts back (If coding is too hard...) that you listed "Model Driven Architecture" as something you don't do. (Maybe I'm ignorant of a distinction between Model Driven Architecture and Domain-Driven Design?)
What you are talking about with Entities and Services sounds a lot like Evan's DDD. BTW, I highly recommend the book to everyone.
My style is increasingly in this direction also, and I am also having the same feelings regarding Controllers and Services.
I have read DDD, and greatly enjoyed it.
Model Driven Architecture is a key pharse for "don't you DARE show me code, I am an architect, I draw pretty pictures and abstract factories factories", at least in the context I have heard it used.
DDD is about a model that reflect the business domain that you are working at.
LOL - passing that MDA comment on to our architect right now! Too funny. :)
Thanks for the post. I have one question though. It seems that you are making the suggestion that the entities do not speak to Repositories and it is the service that does so. If that is the case then would all the CRUD methods for the entities be on the service?
@Ahmad,
Yes, I don't want my entities directly talking the the repositories.
All the crud methods are on the services, or in the controllers.
I don't have an issue with the controllers using a repository to get the data it needs.
Why shouldn't the entities talk to the repositories? Shouldn't they be able to access the database in order to carry out business rules?
RE: Biz Logic in Entities
I generally prefer to keep Entities as simple as possible (closer to the DTO for a few reasons). In my environment we have several customers on the same base product. Using your example, some of our customers use different business rules to calculate cost or apply taxes, etc. If this logic was in the entity I would have to modify the base code to sub-class the order object and instantiate the correct object base on the customer (or cook up some custom factory). Instead I opt to leverage the capabilities Windsor already provides. Windsor correctly resolves the custom "OrderService" for the current customer and and I don't have to modify any base code.
Yeah, if you aren't considering your entities to be domain objects, they probably shouldn't contain business logic....but in a lot of cases it is very convenient, letting you do things like order.AddOrderItem(item) and knowing the order will take care of the details. This is just naturally easier than OrderService.AddOrderItemToOrder(order, item) because in that case you need to know about 3 objects instead of the 2 you should have to care about.
@Ayende & @Hammet
But doesn't all this entities not talking to repositories go against the Castle project philosophy (ActiveRecord) which you are part of?
Because following the AR model
Order.FindByCustomerAndCity(...) would make sense.
Ayende, In that case for simple CRUD operations that contain no business logic or validation, the service method becomes a pass through to the repository. If these methods were on my entities, atleast I could use code gen for creating them.
Entities and Services
@Mike,
Order would definitely have a method called Order.AddOrderItem(); which would be something like:
public void AddOrderItem(Item item)
{
Items.Add(item);
item.Order =this;
}
Persisting this assoication is the responsibility of NHibernate, not of the entity.
This is part of the fun of working with a Unit Of Work.
Just to clarify, my entities are certainly part of the domain. Although I get the feeling that you and I are talking about different things when we discuss what the domain is.
About business logic that needs talking to the database.
I would strive very hard to avoid doing it. Entities validate business logic by traversing the object graph they have, not by calling to the database.
Calling to the database makes it a lot harder to just start a new entity and run business logic.
some points I can think about:
1) One entity can be (usually) several objects
2) UserProfile (customer/user, etc) - separate thing
3) "Application" (Business Logic) classes should translate application use cases: for example: ProcessBilling.cs, PlaceOrder.cs, etc.
4) Logging = infrastructure, is not a part of the application in any way.
@Gustavo,
Yes, what I am talking about contradict the Active Record model.
In general, I really like the way AR can infer everything for me, and take away much of boring details of working with NHibernate.
That said, my personal belief is that a repository driven approach is a more flexible way to handle the issue.
I keep the power of AR, but I can now operate at much greater flexibility, see my article on MSDN to see some of the things that I can do with repositories.
@Ahmad,
Yes, simple CRUD would generally be forwarded to the repository.
If code gen is an issue, there is no reason that you can't generate the service as well.
Personally, I don't like code gen that much, and would much rather build as needed rather than spent more time building the generator than the app, which was my impression where a lot of the ambitious projects went.
I do generate some stuff, but that is usually stuff that is build from the object model of the application.
@Anatoly,
1/ One entity is one object, the way I am doing it. It may not be usable by itself, but it is a valid entity. To take for example OrderLine, it is an entity, but fairly useless without its Order. Maybe you are refering to root/aggerated entities?
2/ UserPfofile - I don't think that I follow what you meant here.
3/ What you call "application classes" I call services or controllers, depending on the level of sophistication. The semantics are slightly different in this regard, but I believe that we are in agreement here.
4/ How does logging relates here??
Oren, I just see it like so:
1) Order is entity (OrderEntity) that consist of several objects:
Order, OrderItem, etc. OrderItem cannot exist on its own.
2) UserProfile is not Entity. ("UserProfile" may be Customer, whatever - somebody who logins to the application and has preferences - is not Entity)
3) BusinessLogic - Sematincs is all that makes a difference :)
The main rule - names should follow use cases. (It should be in english like ProcessOrder, PaintGrass :) whatever)
4) Loggign - you mentioned LoggingService - I say that Loggign is not a part of application.
I don't understand what views mean. If you need to translate your objects - menas to me that you rather build them incorrectly - I don't translate my classes to user interface :) either in web nor in win apps.
Anyway - good post, interesting discussion.
@Anatoly,
1/ you call it an entity, I would call it a root / aggregate entity, but the meaning is the same. The main difference as far as I can see is that I can ask questions on entities, but not on non-entities. Non entities are always (maybe implicitly) qualified by an entity.
2/ UserProfile is an entity in my book, I may ask it questions and it participate in business logic. Usually with regard to either personalization or security, which are often treated as infrastructure concerns, but I would still regard it as an entity that takes part in the domain.
Just to give an example, the logic for whatever I am allowed to do an operation in an HR application is certainly something that involves business logic, and that is something that the UserProfile (not actually named that, but the same idea) is responsible for.
3/ I think that I agree with you on this, although I call them services or controllers NewOrderController, ProcessBillingService which means that the semantics of using them is different.
4/ I mentioned logging as part of the infrastructure services, not a business concern in most cases. Although since it is a cross cutting concern, it is a bit different than most other things in the application.
I'm curious why you use the term "infrastructure logic" to describe classes that might otherwise be abstracted as Data Access Objects. It seems to confuse the purpose with other types of infrastructure such as transactions, remoting, etc.
@Ayende
order.AddOrderItem(item) may break some business rule, where for example the OrderItem added isn't allowed to be added by the particular user that is trying to add it. I know this is a strange example, but it could require security checks for that user that require a database hit. into some security tables. I suppose you could preload the Order (or OrderItem) with the security data necessary for that particular user, but what if the security rights change while the Order is in memory?
In the framework we have built at our company, we use these disconnected entities, and they generally work fine, except for when business rules depend on data not relationally related to the entities themselves (like security).
@Shawn,
I threat them the same way.
Getting something from the database is something that I never think about, no more than I think about opening a transaction.
"something" handles it, but I don't really care what. It is completely infrastructure issue as far as I am concerned.
@Mike,
There are several ways to handle it. The simplest one would be with:
[Security.OnlyAllowUsersThatHaveXYZ]
public virtual void AddOrderItem() {}
And then either build a proxy that would intercept that call ( AOP ), leaving the entity blissfully unaware of the requirements. Or check this when the user attempts to save to persistent storage.
I found that placing all my security checks inside the "is valid to save" freed quite a bit of my time, and made sure that I would not forget about it.
Another option is to inject a service into the entity, but that is something that I would like to avoid if possible.
I see a lot of confusion arising merely by the choice of words. My understanding for Entity, Service, Unit of Work, et cetera is reflected in this:
http://www.martinfowler.com/bliki/EvansClassification.html
(and related books from the DDD camp).
@Mike,
Ayende's article (http://msdn2.microsoft.com/en-us/library/aa973811.aspx) addresses the question of security context (aside from being a great practical example of IoC/DI).
@Christopher
Thanks, I'll check that out. I know generally about dependency injection, but doesn't that still make it so the entities have a connection to the repository, just that this connection is created by some outside object, and set on the entities by this outside object?
No, the injection usually is not to the entity, but to the repository itself.
I found this article http://ricbrown.blogspot.com/2004/09/transparent-persistence-paradox.html. Looks like problem described there is the same as Mike described. Sometimes there are cases when domain model needs to hit the database, or else solution will have scalability problems. What if your domain object needs to perform some business actions on dependent child collection (say, calculate total amount of all payments for certain period), but collection loading makes big performance impact? This is a business rule and should be placed in domain model, but what about performance in that case? Ayende, how do you handle situations like this?
P.S. Sorry for my English :)
@Andrew,
I don't see any issue with your English.
The way I handle such scenarios is by having the service know what to load, and then calling the entity to do the real business.
@Ayende,
I'm handling it the same way, but i have some doubts about where to put DAO (or Repository) calls? I can do it like you showed above, add service layer and place all calls there, but i also can inject my DAOs directly in domain objects, making service layer is needless (and still keeping domain objects testable). I just can't decide, probably because of lack of experience. I know you prefer services, but what you think about second approach? :)
@Andrew,
The other approach, I feel, gives too much responsibilities to the domain objects. They are still testable, but I feel that this makes the significantly more complex.
I am placing the repository calls in both services and controllers at the moment.
Read my followup here (somehow trackbacks don't work): http://harald.fluffnstuff.org/serendipity/archives/50-The-Right-Architecture-for-Your-Application.html
Oren,
Great post, awsome comment thread.
I've put up a post showing a design that takes out the fetching strategy from the service objects and puts it into the generic repository.
http://udidahan.weblogs.us/archives/389
A while ago I dug into this same debate on the DDD mailing list. What I found is that there are basically two camps under the DDD umbrella.
Camp 1: The domain model shouldn't care about persistence
Camp 2: Clients of the domain model shouldn't care about persistence
Eric Evans leans more toward camp 1 (as do I), but camp 2 appears to be louder. Here are a couple Eric Evans quotes on the subject:
"My opinion is that it is reasonable to choose an architecture that does not allow entities to talk to repositories."
http://tech.groups.yahoo.com/group/domaindrivendesign/message/4749
"Generally, I do not like model objects to reference repositories, so my answer would be, generally, the client. The client might look it up based on a well-known name, or you might use dependency injection.
"In effect, the repositories form a layer just above the model objects. They obviously must be above the model objects since their primary purpose is to obtain references to those objects and pass them to requestors. It would not be obvious that they would be above all model objects (rather than just above their own), but I find it works better. Repositories are edging into application functionality rather than model. You could view them as service access to the model objects."
http://tech.groups.yahoo.com/group/domaindrivendesign/message/2305
And another quote from Colin Jack on the DDD mailing list:
"Personally I don't like accessing repositories from the domain as I find all the setup needed to mock them out during testing is just a pain in the neck and makes maintaining my unit tests very difficult, but a lot of people seem to access them from the domain."
http://tech.groups.yahoo.com/group/domaindrivendesign/message/4761
Ayende’s example calculates the cost for an order. For this it needs the order lines associated with
@Ayende: In the OrderService with the CalculateCostForOrder method, I would have leaned towards the service having a GetOrder(orderid) method which returned the order, and for the client to then call order.CalculateCost() (so client would call money = orderService.GetOrder(orderId).CalculateCost(). Now you can add more responsibilities to the entity without having to make the service aware of them.
I'm interested as to what is gained by making the service aware of the orders responsibilities?
@Stephen,
How would the order service know that it needs to load the order lines as well?
This is related to Paul Gielens thread above as well (where to put the comments?).
If you only have a very few use cases which need to calculate cost then would this effectively be a different 'type' of Order? This is where the aggregates help out as they define your boundary. From the above example Order seems to have an OrderLine property. If this is correct then does this mean that in some cases Order could have OrderLines and in some cases it doesn't? Maybe Order is too vague at this point - if you have an instance of Order, what 'type' of order is it? If you use aggregates then those become your 'types' of Order in the domain and makes them explicit. The repositories are then used to retrieve and store the object graph of the aggregates.
If you have defined aggregates then the CalculatedOrderService would return a CalculatedOrder which has a CalculateCost method. There could then also be a DoesntHaveOrderLinesOrder type of order. As for Order, that could be abstract now as both CalculatedOrder and DoesntHaveOrderLines could inherit from it.
I know the names need work - but if you do have multiple types of Order then maybe order is too ambiguous a term. Having names obviously badly worded names like this does at least create discussion about what terms to use instead which works towards extracting and defining the Ubiquitous Language.
Given that you have the knowledge of the domain itself - are there effectively multiple types of Order - one with OrderLines (and the calculation of those order lines) and once without? What is the case for having one Order type in the domain?
@Stephen
Great addition to the discussion. I think your comment explains what I tried to say with "A tight connection with the problem domain at hand makes giving good examples for explaining Domain-Driven Design so hard". Without the proper context, and thus variations on the language results in a confusing discussion in one where all participants seem to have a different mental model of the problem at hand which results in different solutions.
I was reading an blog entry by Ayende Rahien (aka Oren Eini) again, " Entities, Services and what goes
Interesting post and very simliar to something I've blogged about recently: http://colinjack.blogspot.com/2007/02/domain-coordination-layer.html
On my project we've successfully used a persistence ignorant domain (I'm the same Colin Jack that Oran quotes above) but even with NHibernate there are situations where the domain, or atleast domain logic, needs to access the database.
I've handled this situation in two ways:
In our case we've ot a lot of application/process layer services doing what your OrderService is doing.
For example our Client class has an Accounts collection that we lazy load (NHibernate). We use repositories (Evans) and if we know that to run a particular method we're going to use every Account in the Client collection then we'll use an eager fetching query when getting the Client to ensure we get all its Accounts too.
Up till now that sort of logic has been in the application layer but the more I think about it the more I think our domain coordination layer might be the place for it, if so we'll move our equivalents of your services into it.
On the more general issue of whether our domain should access repositories...in my experience no. The minute your domains getting involved in repositories, dependency injection etc etc your making testing hard. It also makes the domain harder to understand which in my view is the best argument against it.
Anyway good post.
Comment preview