Search code examples
javarestdomain-driven-designcircular-dependencyhateoas

Circular Dependency due to usage of HATEOAS in REST


I'm designing my REST application architecture using Domain Driven Design and Adapter patter (there are interfaces, and many implementations in the aggregate root). It's all fine as long as don't add HATEOAS to the puzzle. In HATEOAS my value objects (on the bottom of dependency hierarchy) need to depend on resources (in the top layer). This messes up everything. I'm fairly new to HATEOAS so maybe I'm missing something. I'm planning to use Dropwizard and Jersey Declarative Linking.

Here is a diagram of my architecture:

enter image description here

Little clarification - this "Return and attributes types" between interfaces and value objects should actually be "Return and argument types" - It means, that all the interfaces' methods take objects from Value objects module as an arguments and return those objects to the caller.

I can add a piece of code that will show you what's in what module:

REST - JAX-RS Resources

@Component
@Path("/groups")
@Produces(MediaType.APPLICATION_JSON)
public class GroupsResource {

    @Autowired
    ProcessEngine processEngine; //interface with driver implementation under it

    @GET
    @Timed
    public List<UserGroup> getUserGroups(@Auth BpmUser user) {
        return processEngine.getUserGroups(user.id);
    }

}

Interface ProcessEngine

public interface ProcessEngine {
    void init();
    List<UserGroup> getUserGroups(String username);
}

Implementation in drivers module

public class ActivitiProcessEngine implements ProcessEngine {

    private org.activiti.engine.ProcessEngine processEngine;
    private DataSource dataSource;
    private String databaseType;

    public ActivitiProcessEngine(String databaseType, DataSource dataSource) {
        this.databaseType = databaseType;
        this.dataSource = dataSource;
    }

    @Override
    public void init() {
        if (processEngine != null)
            throw new ProcessEngineAlreadyInitializedException();
        try {
            processEngine = createProcessEngineConfiguration().buildProcessEngine();
            ProcessEngines.registerProcessEngine(processEngine);
        } catch (SQLException e) {
            throw new ProcessEngineDatabaseException(e);
        }
    }

    @Override
    public List<UserGroup> getUserGroups(String username) {
        return processEngine
                .getIdentityService()
                .createGroupQuery()
                .groupMember(username)
                .list()
                .stream()
                .map(Group::getId)
                .map(UserGroup::new)
                .collect(Collectors.toList());
    }

    ...
}

Value object

public class UserGroup {

    @JsonProperty
    public String name;

    //I want to be able add linking to another resources here

    public UserGroup(String name){
        this.name = name;
    }
}

Solution

  • Domain object should never know anything about Controller or any other application logic. So, link controllers to domain object. It will solve your dependency problem.