This issue is technology agnostic, but I am working with C# and ASP.NET and will use this for the pseudo code. Which is the better approach, and why?
Encapsulate logging, transaction and exception handling:
protected void Page_Load(object sender, EventArgs e) {
SomeBusinessClass.SomeBusinessMethod();
}
public class SomeBusinessClass {
public void SomeBusinessMethod() {
using (TransactionScope ts = new TransactionScope()) {
doStuff();
ts.Complete();
}
catch (Exception ex) {
LogError("An error occured while saving the order", ex);
}
}
}
}
Delegate logging, transaction and exception handling to the caller:
protected void Page_Load(object sender, EventArgs e) {
using (TransactionScope ts = new TransactionScope()) {
try {
SomeBusinessClass.SomeBusinessMethod();
ts.Complete();
}
catch (Exception ex) {
LogError("An error occured while saving the order", ex);
}
}
}
public class SomeBusinessClass {
public void SomeBusinessMethod() {
doStuff();
}
}
I am concerned that by introducing dependencies on logging, transactions, etc in my business logic code, I make it less generic. On the other hand, the UI code looks so much cleaner. I can't make the call. Let me know what other factors I should consider.
Transactions: a central concern of your business layer, so it should absolutely handle this (though you might centralize transaction handling via a unit of work implementation).
Update: I don't agree with this part any more. Often, the controller, presenter, or or another top-level caller is the best place to handle transactions (a la the the onion architecture) - in many cases, that's where the logical unit of work is defined.
Exception Handling: use as needed in every layer - but only use it in the business layer when you can actually do something about it (not just log it). Use a global handler in the UI layer if your infrastructure supports one.
Logging: use trace or informational logging in whatever layers need it, only log exceptions in the top layer.