Who says that a service layer should contain no logic of its own, and instead just delegate to domain objects? That sounds like much too strict a rule to me. it's quite clear that in this case the logic belongs in the service layer, as it does not relate to any object that is persisted as part of the model. Instead it looks to me like you've discovered a utility abstraction, which you may want to consider piling out as a separate service to anything that persists the reports.
How large is your application? There's a good chance you're overthinking this. Unless the application is a large, enterprise-grade application, the high degree of loose coupling you are advocating is probably unnecessary.
If you decide that this level of loose coupling is still necessary, create a service layer that returns "service" objects. This fulfills a similar function to View Models in MVC. You will then write code in your service layer that maps objects from your domain model to your service model.
Note that part of your struggle may be due to the fact that, while your Data Repository is returning CRUD objects, your Service Layer should be returning the result of actions. For example:
public InvoiceView GetInvoice(int invoiceID);
returns a InvoiceView
object containing whatever data you wish to expose to the public, including Name, Address, Line Items and so forth, from several different tables/objects in your domain.
public class InvoiceView
{
public int InvoiceID;
public Address ShippingAddress;
public Address BillingAddress;
public List<LineItem> LineItems;
...
}
Similarly, there will be Service Layer methods that simply performs an action and returns an object indicating the result of the action:
public TransactionResult Transfer(
int sourceAccountID, int targetAccountID, Money amount, ValidationToken token);
IMPORTANT: You'll never be able to completely decouple from your data. Your consumer will always have to have some knowledge of the data, even if it's just an object ID or UUID.
Best Answer
There are several architectural patterns for your choice:
Basically, combining your DTO with domain logic looks pretty much like an active record. It's a viable solution if your domain model is simple and if the domain object is very close to the DTO.
However, be aware of its major drawbacks :
And attention: use it only if the domain model is simple. It's not a good idea if one object is simple but the whole model would be rather complex, because active records raise a lot of issues if you'd need to combine it with unit of work, an identity map or other patterns of enterprise application architecture.