Our current O/RM tool does not really allow for rich domain models, so we are forced to utilize anemic (DTO) entities everywhere. This has worked fine, but I continue to st
This is exactly what the service layer is for - I've also seen applications where it's called the BusinessLogic layer.
These are the routines you'll want to spend most of your time testing, and if they're in their own layer then mocking out the repository layer should be straightforward.
The repository layer should be genericized as much as possible, so it's not an appropriate place for business logic that's individual to particular classes.
If your ORM technology only handles DTO objects well, that doesn't mean you have to throw out rich entity objects. You can still wrap your DTO objects with entity objects:
public class MonkeyData
{
public string Name { get; set; }
public List<Food> FavoriteFood { get; set; }
}
public interface IMonkeyRepository
{
Monkey GetMonkey(string name) // fetches DTO, uses entity constructor
void SaveMonkey(Monkey monkey) // uses entity GetData(), stores DTO
}
public class Monkey
{
private readonly MonkeyData monkeyData;
public Monkey(MonkeyData monkeyData)
{
this.monkeyData = monkeyData;
}
public Name { get { return this.monkeyData.Name; } }
public bool IsYummy(Food food)
{
return this.monkeyData.FavoriteFood.Contains(food);
}
public MonkeyData GetData()
{
// CLONE the DTO here to avoid giving write access to the
// entity innards without business rule enforcement
return CloneData(this.monkeyData);
}
}
I've found Dino Esposito's new book Microsoft® .NET: Architecting Applications for the Enterprise to be a great repository of knowledge for these types of questions and issues.
The service layer.
Let's get back to basics:
Services come in 3 flavours: Domain Services, Application Services, and Infrastructure Services
This is where your data-access and consistency checks go. In pure DDD, your Aggregate Roots would be responsible for checking consistency (before persisting any objects). In your case, you would use checks from your Domain Services layer.
Proposed solution: Split your existing services apart
Use a new Domain Services layer to encapsulate all logic for your DTOs, and your consistency checks too (using Specifications, maybe?).
Use the Application Service to expose the necessary fetch methods (FetchOpenOrdersWithLines
), which forward the requests to your Repository (and use generics, as Jeremy suggested). You might also consider using Query Specifications to wrap your queries.
From your Repository, use Specifications in your Domain Services layer to check object consistency etc before persisting your objects.
You can find supporting info in Evans' book:
From what you say it may be that you’re thinking too rigidly about your Service and Repository layers. It sounds like you don’t want your Presentation layer to have a direct dependency on the Repository layer and to achieve this you are duplicating methods from your Repositories (your pass-through methods) in the Service layer.
I would question that. You could relax that and allow both to be used within your Presentation layer and make your life simpler for a start. Maybe ask your self what your achieving by hiding the Repositories like that. You’re already abstracting persistence and querying IMPLEMENTATION with them. This is great and what they are designed for. It seems as though you’re trying to create a service layer that hides the fact your entities are persisted at all. I’d ask why?
As for calculating Order totals etc. Your Service layer would be the natural home. A SalesOrderCalculator class with LineTotal(LineItem lineItem) and OrderTotal(Order order) methods would be fine. You may also wish to consider creating an appropriate Factory e.g. OrderServices.CreateOrderCalculator() to switch the implementation if required (tax on order discount has country specific rules for instance). This could also form a single entry point to Order services and make finding things easy through IntelliSense.
If all this sounds unworkable it may be you need to think more deeply about what your abstractions are achieving, how they relate to each other and the Single Responsibility Principle. A Repository is an infrastructure abstraction (hiding HOW entities are saved and retrieved). Services abstract away the implementation of business actions or rules and allow a better structure for versioning or variance. They are not generally layered in the way you describe. If you have complex security rules in your Services, your Repositories may be the better home. In a typical DDD style model, Repositories, Entities, Value Objects and Services would all be used along side each other in the same layer and as part of the same model. Layers above (typically presentation) would therefore be insulated by these abstractions. Within the model the implementation of one service may use the abstraction of another. A further refinement adds rules to who holds references to which entities or value objects enforcing a more formal lifecycle context. For more information on this I would recommend studying the Eric Evans book or Domain Driven Design Quickly.