Prior to arriving at my previous consulting assignment, the company had already completed their first WPF project. The architecture they used on that earlier project included a rather unusual, in my opinion, hybrid object/relational in-memory representation of business objects. Some of the business objects were wrapped in View Models, while the View was directly referencing ADO.NET data rows of other business objects. I initially kept an open mind, programming their new app to that earlier architecture. But I eventually realized that it provided me no way to abstract away from the underlying relational structures. I then spent the weekend refactoring the new app, and building its Data Access Layer. Having broken away from the status quo established by their first WPF project, however, my efforts were not greeted warmly. I had to spend a subsequent weekend producing the below “Advantages of the Domain Model”, as well as the Microsoft Embraces the Domain Model article I posted earlier, for convincing them to go with a purely object-oriented in-memory representation of business objects.
Advantages of the Domain Model
Confining object/relational mapping logic to a clearly demarcated Data Access Layer (DAL) within your application facilitates the creation of a single, coherent, object-oriented Domain Model to represent your application’s business objects. Adherence to this practice leads to more robust, scalable, and maintainable software.
Separation of concerns
Separation of concerns is a fundamental principle within computer science. As applied to an application’s overall architecture, it provides for organizing a complex software system into separate, but interdependent, parts. The new app’s codebase is easier to understand and maintain than the old app, thanks to the separated Data Access, Domain Model, and View Model layers. The responsibilities of all three layers are lumped into one layer within the old app.
- Conceptual model - The Domain Model provides a concise representation of the domain of interest. This can aid in communication with experts in the problem domain who are not necessarily developers.
- Division of labor and organization of code - Need to retrieve or persist data? Then call into the
DataAccess singleton. Need to execute some business logic when a condition’s selected field changes? Then go to the Domain Model and put the logic into the
Condition.SelectedField setter. Need to tell the View how to display itself differently? Then go to the View Model.
- Code Reuse - Both the old and new apps contain object-relational mapping logic. The difference is that the new app’s Data Access Layer, by way of generalized functions, eliminates the needless repetition found within the old app’s numerous Load and Save methods.
- Consistent interface to in-memory data - Hiding the relational model specifics, as is accomplished by the new app’s Data Access Layer, provides a consistent representation of business objects. The View layer in the old app, on the other hand, contains bindings that reference both .NET properties as well as database fields.
- Maintainability - Because of the separation of responsibility, the new app can be modified more easily when business needs change. Changing a database field’s name, for example, requires a limited maintenance effort in the new app. Since data access is coded concisely in the Data Access Layer, it involves altering just one string.
Coding directly to ADO.NET objects, as the old app does, means that you cannot easily navigate the relationships between business objects. The new app’s Data Access Layer, on the other hand, shields upper layers from database specifics. This allows for more natural modeling of data, and simpler handling of complex relationships between entities.
- The business objects are strongly typed, so we benefit from compile-time type-checking.
- Business objects’ members expose themselves through Intellisense, eliminating typos and problems with remembering property names.
- We can navigate the object-oriented Domain Model by using the OO-dot syntax, such as
condition.SelectedField.Type.AvailableOperators. That is, we just choose an object instance to be our point-of-entry, then navigate from there. The ADO.NET objects in the old app, on the other hand, don’t allow you to easily navigate the data model.