ERP Modules with GWT and Domain Driven Architecture
Since this article was written, we have decided to take a more generalized approach to user interface instead of relying exclusively on the Google Web Toolkit (GWT). The ideas here for the overall organization of the application are still applicable.
If you've had a chance to explore the technical framework of opentaps Open Source ERP + CRM, or indeed any modern ERP system, you would find it probably look something like this:
There is a data model, or entities, which is connected to a relational database. Above that there is a tier of business logic, or services. The combination of entities and services is what is usually called "model" of the model-view-controller architecture. Finally, there is a view layer which creates the screens that the users see, and the view layer interacts with the services through a controller.
When you need to add something to your ERP system, you would need to create new entities, new services, new entries in your controller, and new screens in your view layer:
This, in turn, also means learning the technical framework of the particular ERP system. If later you wanted to add the same features to another ERP system, it would mean learning a different technical framework, then creating adding to its entities, services, and view layer.
Instead of becoming a part of the ERP system, the new feature sits as its own module. It has its own self-contained "model" tier, including the entities and services that it needs. There is a repository interface which defines the actual database interactions that this module would need, such as what kind of data it would need to retrieve and persistent. Its view layer is written with the Google Web Toolkit, and it plugs into the rest of the ERP system. It takes its parameters from HTML embedded in the ERP system's screens and returns its output as a widget.
Finally, to interact with the ERP system, an implementation of the module's repository interface is written for this particular ERP system. This implementation could use either Web services to connect to the business logic or services tier of the ERP system, connect more directly to the entities or persistence layer of the ERP system, or bypass both and simply obtain a JDBC connection from the ERP system and interact with its database directly.
The advantage of this alternative approach is that the module could in turn be adapted to other ERP systems simply by changing the implementation of the repository interface:
For all three ERP systems, the user interface of the module can be integrated into existing screens as widgets. The same module could be adapted to ERP-A and ERP-C in the diagram by implementing different repositories for those two ERP systems. The example for ERP-B shows another configuration: only the user interface is integrated into the ERP system's screens. The module supplies its own persistence, by implementing a repository which talks to an independent database. This approach could be helpful if the module's features do not overlap with the core ERP system, or if interacting with the core ERP system is too difficult.
Each module would be written as standard Java webapps and would have standard GWT base libraries for both client and server-side interaction. It would take parameters from the HTML screens of the ERP application. A loader similar to the opentaps domains directory would be used to bind the implementation of the repository to the repository interface, as well as implementation of commonly used services such as user authentication and view preferences. Transaction management during persistence will be handled by the implementation of the repository.
As an example, consider a Notes module. It could be used to enter freehand notes about customers, orders, cases, sales opportunities, invoices, payments, etc. -- anything.
The Notes module would be its own webapp. It provides a simple user interface: a GWT widget which shows the notes applicable to a particular customer or order, and the ability to add more notes. This widget could be loaded anywhere on the screen using a div tag. It could then find the ID for the main page, such as the party ID or order ID, from the screen's HTML.
When the Notes module needs to retrieve or store notes, it will call a NotesRepositoryInterface and pass it the text of the note, plus the parameters of the IDs to associate the note with, such as the order ID or party ID. For generality, they could be passed as a Map. As part of the core Notes module, how the NotesRepositoryInterface is implemented is not important. We do not need to know where exactly the notes are stored, just that they are stored somewhere, and that we are able to retrieve the notes based on the associated IDs.
Then, to make it compatible with the existing notes in opentaps, we can implement an OpentapsNotesRepositoryImpl which would access the services such as createOrderNote, crmsfaCreateAccountNote, etc.
Others than this implementation to reference the existing services, the Notes module would not need anything else from opentaps worthy ofbiz framework. It would not need to reference the entity engine, service engine, or controller and screen XMLs.