Building modules and components is not that hard. You "only" have to encapsulate the internal component implementation and expose a clean and easy to use interface. ...at least on paper. In practice you will be confronted with the following challenges in the early iterations:
- The external interface is too coarse and far less interesting for internal reuse, than you had thought.
- The interesting things are residing inside the component. They are, however, well encapsulated and not accessible from the outside.
address / geo-locationservice. The module
customermgmtexposes CRUD services and the address component extensive search capabilities. So far the world is perfect.
Now: a customer has an address - how to model that? The external, customer contract will have to reference the address somehow. That is often modeled as a direct relation between DTOs (just a getter). The external view of the customer component is now dependent on the address component. The implementation is still independent. The relation between the customer and the address has to be persisted somehow. And now the trouble starts. Now the implementation of the customer component is dependent on the address component - because of direct (JPA) link between both modules. Now the internal implementation *and* the component contract are dependent on each other. You are using JPA 1.0 - and your database experts just don't want to introduce an additional mapping table between the customer and the address. So you have to model a bi-directional relation between the customer entity and the address (introducing a back-link with a mappedBy attribute). Now you get a bidirectional dependency between the implementation of your component - the external dependency of the customer can remain unidirectionally dependent on the address. This is only true if you are using DTOs. So you get two components which should be independent of each others, but are actually tightly coupled. Your modules have to expose everything - if you are using Java EE 6 - the DTOs and JPA-entities are dependent of each other - probably only the very thin boundary may remain independent. In practice you will get e.g. an
invoicemodule in addition, which will be dependent on both the customer and the address.... You can do the following to "improve" the situation:
- Factor out all entities into a common package. Often called "domain", "model" or even "common". Such a common package is not cohesive (it contains multiple business concepts) and also not very good to maintain (the generic names have nothing to do with the actual business). This approach looks great ...on paper.
- Drop JPA-relations and introduce proxy-objects, which contain the ID and can be resolved on demand. This will significantly increase the amount of code and will hit your performance. You will be not able to use joins...
- Allow bidirectional friend-dependencies between modules. In that case it will be hard to introduce a framework like OSGi, jigsaw or something else. But you can still put all "business components" into few modules. Then the real benefit of OSGi, Jigsaw etc is questionable.
- Remove OR-mappers and go with "plain" JDBC. Let the DB handle the dependencies for you. In most cases this is not really a maintainable option.
So, you shouldn't kill any OSGi project - you should implement some typical use cases (PoCs) with modularity solution of your choice before the project really starts. This is actually independent of any framework like OSGi, jigsaw or EMS (esoteric module system) :-).
[See also "Real World Java EE Patterns, Rethinking Best Practices" book, Page 267, (Monolithic or Loosely Coupled?) for more in-depth discussion]
Cloudy Jakarta EE and MicroProfile: Microservices, Clouds and Beyond Jakarta EE / MicroProfile airhacks workshops at MUC airport, Winter Edition
airhacks.fm the podcast:
Stay in touch: airhacks.news.