If you haven't looked into CSLA yet, you need to. Rocky has done a lot of thinking about the concept of business objects, and his stuff works great. The framework he gives you really helps write clean, efficient, reusable code.
Take his business rules model as an example. Simply override "AddBusinessRules" (it is called when an object is instantiated) and add any combination of predefined rules from the "CommonRules" class or create your own (e.g. "ValidateGroupId" or "ValidateType"). This standardizes a way to get the business logic out of the UI and into the business object where it belongs. The great thing is that the logic is reusable. It can run on any tier - from the UI to the application server (potentially even in the DB - probably NOT recommended.) You can use the same object to show errors to the end user in a Win/WebForm application, an Avalon app, or validate data from a Web Service.
Rocky, of course, does a much better job explaining this than I ever could, so go check him out: http://www.lhotka.net/
Get the full explanation in his latest book: Expert C#/VB 2005 Business Objects
Here's a snippet from one of my business objects to help you see how it all fits together:
Protected Overrides Sub AddBusinessRules()
With Me.ValidationRules
' "To" is required but max of 50
.AddRule(AddressOf CommonRules.StringRequired, "To")
.AddRule(AddressOf CommonRules.StringMaxLength, _
New CommonRules.MaxLengthRuleArgs("To", 50))
' "From" is required, but max of 50
.AddRule(AddressOf CommonRules.StringRequired, "From")
.AddRule(AddressOf CommonRules.StringMaxLength, _
New CommonRules.MaxLengthRuleArgs("From", 50))
' GroupID must be > 0
.AddRule(AddressOf Me.ValidateGroupId, "GroupId")
' Message is not required but max of 500
.AddRule(AddressOf CommonRules.StringMaxLength, _
New CommonRules.MaxLengthRuleArgs("Message", 500))
' Apply custom "Type" rules
.AddRule(AddressOf ValidateType, "Type")
End With
End Sub
Private Function ValidateGroupId( _
ByVal target As Object, ByVal e As RuleArgs) As Boolean
If _groupId < 1 Then
e.Description = "Invalid Group ID"
Return False
End If
Return True
End Function
Private Function ValidateType( _
ByVal target As Object, ByVal e As RuleArgs) As Boolean
If Me._items.Count > 0 _
AndAlso Me.Type <> RequestType.Reviewer Then
e.Description = _
"When group items are listed, type must be ""Reviewer"""
Return False
ElseIf Me._items.Count = 0 _
AndAlso Me.Type = RequestType.Reviewer Then
e.Description = _
"Type cannot be ""Reviewer"" when group items are not listed"
Return False
End If
Return True
End Function