first pass at a domain driven architecture
We've completed the first pass of the new domain driven architecture, so I wanted to give you an update about how it works and tell you about some of the interesting things we found out along the way.
How It Works
The basic idea of the domain driven architecture is to separate your application's business logic from the underlying nuts and bolts, so that you can use several different applications interchangeably or migrate from one platform to another while preserving the essential core of your application. Another related goal is for you to be able to write business logic code in business terms, so instead of working with tables in a database or their equivalents, you can work with business concepts such as customers, orders, and invoices. Later, when someone else comes in to read your code, it will be easier for them to understand what you've done and help maintain it going forward.
To do this, we have implemented a set of entity class objects representing the data model of opentaps. Most of these classes are automatically generated from the original entitymodel XML's, and you can extend or reuse them to implement more complex business logic. The actual interactions with external resources such as databases or legacy services are defined as Java interfaces, and a central domains directory defines where they are implemented. So, you can define an OrderRepositoryInterface of the methods for obtaining order related information, and your Order domain can interact with this interface. The actual implementation of it is in the OrderRepository, and at run time opentaps will use Spring to look in the domains-directory.xml file to find the order domain that you are actually using and get the order repository from it. If later you decide to switch from the opentaps order management system to another order management system, you can just re-implement all the Order domain interfaces, and the core order processing logic will continue to function.
What We Learned
A couple of the things that we realized as we work on defining the domain driven architecture:
We decided it was necessary to implement a true object layer for the opentaps data model. I originally thought that this was unnecessary, and we could just extend the old ofbiz GenericValue into our domain objects as needed. This turned out not to work, though, because we'd be stuck with chunky GenericValue objects instead of lightweight Java objects. More importantly, it would have introduced logical inconsistencies. For example, imagine if you implemented a method called getTotal(), and later somebody introduced a field called "total" to an entity. If you were extending GenericValues to Java objects, get("total") and getTotal() would give you different results.
My original plan also called for an Infrastructure class which provided the "plumbing" for each platform. Thus, for the ofbiz-based applications there would be the delegator and dispatcher, and for a hibernate-based application there will be a session factory, etc. etc. This actually made the code very messy, because we could not define a common Infrastructure interface which could be used across domains. Each domain with have to use an Infrastructure which is specific to its framework. After further thought, though, I realized that what we really needed was a common Infrastructure that was global to all the frameworks used in opentaps. This did not compromise the reusability of any particular domain implementation, and has a nice benefit of being able to transfer resources from one platform to another.
The Next Step
Now that we have the structure in place, we will be reimplementing some of the existing features with the new domain driven architecture and in the process refine it further. The initial emphasis will be on parts of the application that could really use an object-oriented redesign, either to improve code manageability, like the Party and contact information features, which could benefit from inheritance, or performance, by rewriting some old minilang XML code in Java.