This should not happen. What I usually do is have a module structure like this:
root
- api (interfaces, enums and custom exceptions only)
- impl (standard implementation of api -> dependency to api)
- server (runtime dependency to impl, compile time to api only)
- client (dependency to api only)
Let's say we have an interface Person
in the api
module. Now in the impl
module, you'd have a class JpaPerson
, because impl
uses JPA. However, those are details that client and server don't need to now, so you should not (and in my design can not) do new JpaPerson()
in either client
or server
. Instead you'd have an interface (also inside api
) called PersonFactory
(sounds awful when you're talking about people, maybe name it more human-friendly), that has methods like this:
public interface PersonFactory{
Person findPersonBySsn(String socialSecurityNumber);
List<Person> findPersonsByName(String firstName, String lastName);
List<Person> findPersonByBirthDate(Date birthDate);
Person createPerson(
String firstName, String lastName,
Date birthDate, String socialSecurityNumber
);
}
Again, implement this interface in the impl
module, but reference it as an interface only in the other modules. Then use dependency injection to wire things together (Spring, SEAM, EJB3, whatever).
The situation you are mentioning is a result of code doing things that are beyond it's responsibility (read about separation of concerns).
The great advantage to this design is that you can refactor all modules (except api
) individually, without changing code in the other modules. So if you want to switch from jpa to classic hibernate, you just need to edit impl
. If you want to switch the server from axis to cxf, just change server
. This makes things a lot easier. And of course you don't get circular dependencies.
EDIT:
a simple (simpler) solution for you would be to introduce a module d that is included as a dependency in a, b and c. Move all common code to d. (Actually rename this d to a and a,b,c to b,c,d).