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.