I have run into the following situation a couple of times and I cannot find a fully satisfying solution:
I am developing a Java application using OSGi and, following OSGi's best practices, it is highly modularized. Here's an extract of some of my plugins and classes in them:
- com.example.core.db.manager (plugin)
|- com.example.core.db.manager (package)
|- DatabaseManager (interface)
- com.example.core.business.objectmanager (plugin)
|- com.example.core.business.objectmanager (package)
|- BusinessObjectManager (interface)
- com.example.some.businessobjectmanager.consumer (plugin)
|- com.example.some.businessobjectmanager.consumer (package)
|- SomeBusinessObjectManagerConsumer (interface)
(obviously this is not the real name, but the name is irrelevant)
Where
DatabaseManager
is a low-level construct that interacts directly with the database.BusinessObjectManager
is a high-level construct that acts (among
other things) as a sort of adapter for the DatabaseManager
.SomeBusinessObjectManagerConsumer
consumes BusinessObjectManager
and should not know about the underlying database, not even that there is a database. It should not know about the DatabaseManager
; rather, it should interact only with the BusinessObjectManager
.So far so good. But now SomeBusinessObjectManagerConsumer
needs to update some edges between entities in the database (I use a graph database, meaning my entities (what would normally be rows in a table) are nodes, and the relationships between them are edges). As explained before SomeBusinessObjectManagerConsumer
doesn't know anything about the database, but it knows there are some "business objects" (nodes) and that there are links between some of them (edges).
In BusinessObjectManager
, I create a method replaceLinks
as follows...
UpdatedLinks replaceLinks(BusinessObjectUID from, Set<BusinessObjectUID> to);
...which is supposed to make sure that, by the time it returns, the from
business object will only be linked to the to
objects, possibly removing previous links and adding new ones. I would like to know about these removals and additions. I create an interface UpdatedLinks
in plugin com.example.core.business.objectmanager
:
public interface UpdatedLinks {
Set<BusinessObjectUID> getRemovedLinks();
Set<BusinessObjectUID> getAddedLinks();
}
But the BusinessObjectManager
is not really the one who is going to perform this link replacement and put together the UpdatedLinks
return object. Instead, it delegates this to DatabaseManager
. So I create an equivalent method in DatabaseManager
, and BusinessObjectManager
will simply invoke this method.
The problem now is where to place the interface UpdatedLinks
: it is needed by DatabaseManager
, BusinessObjectManager
and SomeBusinessObjectManagerConsumer
, but the dependencies between these classes (and their corresponding plugins) goes in this direction:
SomeBusinessObjectManagerConsumer ---depends-on---> BusinessObjectManager ---depends-on---> DatabaseManager
So:
UpdatedLinks
in BusinessObjectManager
's plugin because then it is not visible for DatabaseManager
.DatabaseManager
's plugin because then it is not visible for SomeBusinessObjectManagerConsumer
(remember that SomeBusinessObjectManagerConsumer
doesn't know anything about DatabaseManager
; because of modularization in OSGi, it can only have access to DatabaseManager
if I declare an explicit dependency on it, which I don't want to do).Basically, I would have to create a new plugin just for this interface and I cannot even find any meaningful name for that plugin, considering who is going to depend on it. (But mainly, I resist the idea of creating a plugin just for this interface, which exists just as a means to deliver the results of the replaceLinks
method).
I have encountered this situation several times (especially lately using OSGi, because of the modularization) and I can never find a fully satisfying solution. What would/do you do in such a case?
"Meta" disclaimers:
I propose to simply create two interfaces for it. One one the DataBaseManager level that is used by BusinessObjectManager and one on the BusinessObjectManager level that is used by SomeBusinessObjectManagerConsumer.
As you want the bottom and top layers not to be connected you should also not share interfaces between them. Of course you could also create a special api bundle just for this but I think it would not be worth it and hurting the cohesion in api of each layer.