Interfacing with Insanity
More and more we are becoming mutual support groups for the insanity around us, which also happen to write business apps on the side.
I set out a few weeks ago to intertwine a tall strong intelligent business object with a confident and independent business object. Seemed the perfect pairing.
While I can’t buy POCO and other things being responsible for common behaviors of business objects, I do have a deep appreciation for provider and injection patterns. It seems fine that the business object is self servicing – meaning it has a “save yourself” method, as long as it is passing the work off to someone else who can be switched out to match development, deployment and evolutionary needs.
This was a rite of passage for the common pattern I fall back on in business objects. I’ve had insight seeing people struggle to keep their UI and business layers clean. When we mound heaps of fabulous features into base classes it’s exceedingly difficult to keep the logical intent we start with – keeping the business logic and UI isolated. I’ve just lost faith that we can avoid using features that are sitting in the Intellisense drop down. I want the compiler in control of controlling me.
That means every layer has to talk to every other layer via an interface and providers. That means the business object starts with nothing but it’s own properties and three or four self-servicing methods. It has a base class that defines any further bases, and nothing in the base classes is protected. All, private.
That’s the theory. It was way more work than I expected to implement this against CSLA business objects, but it worked. The “way more work” part was partially because I accomplished two other things along the way. Please don’t get distracted about the fact I was using CSLA as a base. If you understand where I’m going here, it makes absolutely no difference. The interesting thing to talk about later is the interfaces. I’m quite happy with that outcome.
Then I dived into what I thought would be easy – the business collections. Items for budget, line items for invoices, etc. I spent an entire day thinking, ditzing, tinkering and got nowhere. Hmmm. What’s the root cause? It’s the fact I was trained as a scientist. When there seems to be something more going on – most likely there is more going on.
CSLA – and again, it makes no differences what implementation layer you have – I just want to use a good one and not write it from scratch – does a good job setting up bindable collections. But at what cost! I scribed out all the interfaces in play (many are in play multiple times so the order is a bit random. I’m including them below, but the punch line is Rocky implements 13 custom interfaces and 12 BCL interfaces with a total of 92 members. There are a few things in Rocky’s implementation having to do with n-tier deployment that you could potentially argue, but this is just what needs to be implemented to accomplish data binding.
That’s 92 things that are either directly available to the business collection or are available via interfaces. The interfaces are the killer – everything in any interface – all 92 members are potentially public. At the moment I’m primarily interested in isolating the programmer writing business logic – but all 92 interface members are available to the UI.
This isn’t a problem if we abandon the idea of independent business objects. Wow – NO WONDER WE DON’T DO A GOOD JOB OF INDEPENDENT BUSINESS OBJECTS.
Today, I’m just screaming. And walking. And maybe later drinking. Some place in this I’ll decide a solution for this client today. I’m still hoping to do a simple list, with a “AsBindingList” method for binding and a CSLA provider for persistence. But man does that look hard at the moment.
CSLA Interfaces
IEditableBusinessObject
int EditLevelAdded { get; set;}
void DeleteChild();
void SetParent(IParent parent)
void Delete();
IBusinessObject
ISupportUndo
void BeginEdit();
void CancelEdit();
void ApplyEdit();
IUndoableObject
int EditLevel { get; }
void CopyState(int parentEditLevel, bool parentBindingEdit);
void UndoChanges(int parentEditLevel, bool parentBindingEdit);
void AcceptChanges(int parentEditLevel, bool parentBindingEdit);
ITrackStatus
bool IsValid { get; }
bool IsSelfValid { get; }
bool IsDirty { get; }
bool IsSelfDirty { get; }
bool IsDeleted { get; }
bool IsNew { get; }
bool IsSavable { get; }
IUndoableObject
int EditLevel { get; }
void CopyState(int parentEditLevel, bool parentBindingEdit);
void UndoChanges(int parentEditLevel, bool parentBindingEdit);
void AcceptChanges(int parentEditLevel, bool parentBindingEdit);
ISavable
object Save();
void SaveComplete(object newObject);
event EventHandler<SavedEventArgs> Saved;
IParent
void RemoveChild(Core.IEditableBusinessObject child);
void ApplyEditChild(Core.IEditableBusinessObject child);
IDataPortalTarget
void MarkAsChild();
void MarkNew();
void MarkOld();
void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e);
void DataPortal_OnDataPortalInvokeComplete(DataPortalEventArgs e);
void DataPortal_OnDataPortalException(DataPortalEventArgs e, Exception ex);
void Child_OnDataPortalInvoke(DataPortalEventArgs e);
void Child_OnDataPortalInvokeComplete(DataPortalEventArgs e);
void Child_OnDataPortalException(DataPortalEventArgs e, Exception ex);
IIndexSearchable<T>
IEnumerable<T>
SearchByBLOCKED EXPRESSION;
System.ComponentModel.IRaiseItemChangedEvents
bool RaisesItemChangedEvents { get; }
12 interfaces
49 members