javadependenciesjqassistant

How to differentiate between "Direct" and "Indirect" dependencies in jQAssistant


To illustrate my issue, I created a minimal, fictional example project which consists of three classes, Service, Transaction and Product. P

package org.example;

public class Service {
    public Service(Transaction transaction) {
        int buyerId = transaction.getProduct().getId();
    }
}

Type Product is in a separate package domain

package org.example.domain;

public class Product {
    // [...]
    public int getId() {
        return this.id;
    }
}

For the sake of this example, assume that I want to avoid Service to depend on anything in package domain.

I can ensure this using this query:

MATCH
    (c {name:"Service"})-[:DEPENDS_ON]->(d)
WHERE
    d.fqn STARTS WITH "org.example.domain"
RETURN
    c.fqn, d.fqn

This returns a non-empty result, i.e. the constraint is violated - because jQAssistant creates a :DEPENDS_ON relationship between Service and Product in this case, which feels counter-intuitive, because there is neither an import nor a direct reference to org.example.domain.Product in Service.

This leads me to the following questions:


Solution

  • The behavior is by intention on both the technical and conceptual level:

    Technical: While scanning a Java class all encountered dependencies are aggregated. In the example the getProduct() returns a Product and that one is used for invoking a method on it, so it's tracked.

    Conceptual: The code literally depends on Product. If you remove the type from the classpath, the shown code breaks. Even if there's no import or explicit field/variable/parameter/return type declaration the dependency is still there. If you would like to refactor (e.g. split up domain and services into different artifacts) you would need to address this dependency as well. So you could see it the other way around: Even if you don't see the dependency explicitly in the code it becomes visible by jQA.