I've just finished working on a WPF, a pretty standard inventory system for widgets & gadgets. A hierachurial structure to the data that lends its self to a tree-view structure - essentially each node has a parent-child relationship, where a node can have multiple child nodes and each node has a reference to it's parent. The app is a simple a CRUD application with some UI tweeks on how the data was to be displayed - visualising the modified nodes in the tree and hows these affected the parent node.
What made this interesting was the fact this wasn't a new app from the ground-up, it was literally only a new UI, the database was already established in a production environment and had been for over 5 years, what made this interesting was the client was providing the DAL & BLL for the application - we were abstracted away from the database. All queries for data are returned via strongly typed data tables and all commands (updates) are done via explicit methods on the BLL. To be honest these didn't really measure up to my idea of a business logic layer, to perform a 'business action' with the BLL required multiple calls - so when trying to hydrate data from the database we had to make multiple calls to the BLL just to get all the data required, none of the BLL calls were designed with any real business need, they were more a need to get data from the database.
Originally when we started the engagement we knew nothing about the DAL & BLL provided by the client. Our presumption was to use an ORM as part of the 2 tier architecture utilising MVVM with the ORM being used directly from the ViewModel to reduce the number of abstractions - I agree with Ayende's stance on the use of a repository abstraction in applications is no longer providing any benefit. We still planned to follow this architecture after we found out using an ORM was not an option, but it became obvious we would have introduce another abstraction into the application because the BLL didn't do exactly what was expected.
We ended up with the following abstraction;
public interface IServiceAssemblies { Assembly GetAssemblyHierarchyForPackage(Package package); IEnumerable<Package> GetUserFleet(User user); IEnumerable<Namespace> GetNamespaces(); IEnumerable<Parameter> GetNamespaceParameters(int assemblyId, int namespaceId); IEnumerable<Country> GetCountries(); IEnumerable<Language> GetLanguages(); IEnumerable<ServiceRegion> GetServiceRegions(); IEnumerable<Type> GetTypes(int parentTypeId); IEnumerable<DataCollectorAssembly> GetStandAloneDataCollectors(); IEnumerable<Type> GetListOfEquipmentTypes(); void Save(Assembly assembly, DeleteClipboard deleteClipboard, User user, ref IList<Assembly> savedAssemblies); void Create(CustomerAssembly assembly, User user, ref IList<Assembly> savedAssemblies); void Create(DataCollectorAssembly assembly, User user, ref IList<Assembly> savedAssemblies); }
Now this really is an abstraction! - it's not some pointlessly introduced service or interface that just does pass-through method calling or to facilitate testing. It's there to allow the abstracting away all the concepts I'd normally expect an ORM to handle. Concerns such as:
- Caching of entities,
- Lazy loading of entities,
- Change tracking of entities,
- Mapping of entities to & from relational model.
The implementation of this interface is over 1000 lines of pure procedural code and you can see from the interface there is a clear division between the queries & the commands, put simply they represent CRUD methods by another name.
I would have preferred not to have written any of this code. Not only did we have to implement these concerns but we had to deal with a hierarchical relational model that used tables to store multiple different business entity types that required re-cursing the hierarchical structure to determine the entity type - WTF!
Comments
Post a Comment