ContextMapper / context-map-generator

A graphical DDD Context Map Generator based on Graphviz
Apache License 2.0
21 stars 2 forks source link

Visualization of relations between one OHS and multiple consumers #13

Open dwu opened 2 years ago

dwu commented 2 years ago

When modeling three contexts ContextA, ContextB, ContextC with ContextA providing an OHS ServiceA to both ContextB and ContextC where the relationships ContextA -> ContextB and ContextA -> ContextC have the same name I would have expected a context map diagram with one OHS on ContextA with edges to both ContextB and ContextC to make it obvious that both consumers consume the same OHS.

Instead, the context map diagram shows ContextA with two OHSs, one for each relation.

I'm not sure whether this is intended behavior and just a misunderstanting of the visual notation on my side or a bug / due to limitations of how graphviz renders edges between nodes when no port is specified.

Here's an example:

ContextMap Demo {
    contains ContextA
    contains ContextB
    contains ContextC

    ContextA [U,OHS]->[D,CF] ContextB : ServiceA
    ContextA [U,OHS]->[D,CF] ContextC : ServiceA
}

BoundedContext ContextA
BoundedContext ContextB
BoundedContext ContextC

Expected result:

Actual result:

A similar behavior can be observed when exporting to PlantUML where two interfaces for ServiceA are generated with one of them being referenced by ContextB and one by ContextC.

Expected result: plantuml_expected

Actual result: plantuml_current

Interestingly, the DDD sample linked from the examples page shows the visualization I would have expected.

Context map in the documentation:

Context map when generated from the stage 5 source of the sample using the contextmapper vscode plugin version v6.5.1 (operation "Generate Graphical Context Map"):

stefan-ka commented 2 years ago

Hi @dwu

Thanks for reporting the issue! I'm aware of this limitation... There are two things we would have to change in order to improve the visualization:

1) The CML language currently does not support expressing that two OHS's are "the same one". If two relationships use the "OHS" role on the same context, we cannot automatically assume that this is the same API. We would have to enhance the grammar so that we can for example give an OHS an identifier that can be reused. Or make an OHS a real object in the language that can be referenced.

2) The Context Map generator is currently based on GraphViz and we use a pretty simple graph for the visualization. As far as I know GraphViz does not support having "one single point" on a vertex where multiple edges can be connected to? Or am I wrong? In that case we would have to consider to make the OHS its own vertex and find out whether we can visualize this accordingly.

Side note: The example on the examples page that shows it as you would have expected it, was created manually ;)

If you want to help us improving the Context Map generator, contributions are always welcome ;) I have to check when I would have the time to work on this one... But I see that this might be an important feature!

Best regards, Stefan

dwu commented 2 years ago

Hi @stefan-ka,

Thanks for the quick and detailed response.

Regarding your second point: as far as I know, you're right. Graphviz does not allow defining the points where edges connect to nodes explicitly apart from specifying a port which can be one of (n | ne | e | se | s | sw | w | nw | c | _) according to the dot language grammar. So this doesn't really help.

After some quick experiments with graphviz to get an idea how this could look like it seems that displaying OHSs as nodes in a way that is as compact and clear as the current representation will be quite tricky. At least if I'm not missing anything in the graphviz documentation.

Here's a naive example with two OHSs ServiceA and ServiceB represented as boxes.

Example source code ``` digraph "ContextMapGraph" { graph ["imagepath"="/tmp/GraphvizJava"] "ContextB" ["margin"="0.3","orientation"="139","shape"="egg","fontsize"="16","style"="bold","label"="ContextB\n","fontname"="sans-serif"] "ContextC" ["margin"="0.3","orientation"="53","shape"="egg","fontsize"="16","style"="bold","label"="ContextC\n","fontname"="sans-serif"] "ContextA" ["margin"="0.3","orientation"="130","shape"="egg","fontsize"="16","style"="bold","label"="ContextA\n","fontname"="sans-serif"] "OHS" ["margin"="0.15","shape"="box","fontsize"="12","style"="bold","label"="OHS\nServiceA","fontname"="sans-serif"] "OHS2" ["margin"="0.15","shape"="box","fontsize"="12","style"="bold","label"="OHS\nServiceB","fontname"="sans-serif"] "ContextA" -> "OHS" ["fontsize"="12","style"="bold","fontname"="sans-serif","dir"="none"] "ContextA" -> "OHS2" ["fontsize"="12","style"="bold","fontname"="sans-serif","dir"="none"] "OHS" -> "ContextB" ["headlabel"=<
DCF
>,"labeldistance"="0","fontsize"="12","taillabel"=<
U
>,"style"="bold","label"=" ","dir"="none","fontname"="sans-serif"] "OHS" -> "ContextC" ["headlabel"=<
DCF
>,"labeldistance"="0","fontsize"="12","taillabel"=<
U
>,"style"="bold","label"=" ","dir"="none","fontname"="sans-serif"] "OHS2" -> "ContextC" ["headlabel"=<
DCF
>,"labeldistance"="0","fontsize"="12","taillabel"=<
U
>,"style"="bold","label"=" ","dir"="none","fontname"="sans-serif"] } ```

In my opinion it is not really obvious that ServiceA and ServiceB are OHSs of ContextA. Unfortunately, there does not seem to be a way to move the OHS nodes closer to node ContextA as the edge len attribute is not supported by dot.

Grouping related nodes as an alternative to moving them closer together does not look too promising either as support for grouping nodes seems to be rather limited. The only way I could find in the documentation is grouping by introducing clusters (subgraphs with a name prefix of cluster_) which results in something like this.

Example source code ``` digraph "ContextMapGraph" { graph ["imagepath"="/tmp/GraphvizJava"] "ContextB" ["margin"="0.3","orientation"="139","shape"="egg","fontsize"="16","style"="bold","label"="ContextB\n","fontname"="sans-serif"] "ContextC" ["margin"="0.3","orientation"="53","shape"="egg","fontsize"="16","style"="bold","label"="ContextC\n","fontname"="sans-serif"] subgraph cluster_ContextA { "style"="rounded,bold" "ContextA" ["margin"="0.3","orientation"="130","shape"="egg","fontsize"="16","style"="bold","label"="ContextA\n","fontname"="sans-serif"] "OHS" ["margin"="0.15","shape"="box","fontsize"="12","style"="bold","label"="OHS: ServiceA","fontname"="sans-serif"] "OHS2" ["margin"="0.15","shape"="box","fontsize"="12","style"="bold","label"="OHS: ServiceB","fontname"="sans-serif"] "ContextA" -> "OHS" ["fontsize"="12","style"="bold","fontname"="sans-serif","dir"="none"] "ContextA" -> "OHS2" ["fontsize"="12","style"="bold","fontname"="sans-serif","dir"="none"] } "OHS" -> "ContextB" ["headlabel"=<
DCF
>,"labeldistance"="0","fontsize"="12","taillabel"=<
U
>,"style"="bold","label"=" ","dir"="none","fontname"="sans-serif"] "OHS" -> "ContextC" ["headlabel"=<
DCF
>,"labeldistance"="0","fontsize"="12","taillabel"=<
U
>,"style"="bold","label"=" ","dir"="none","fontname"="sans-serif"] "OHS2" -> "ContextC" ["headlabel"=<
DCF
>,"labeldistance"="0","fontsize"="12","taillabel"=<
U
>,"style"="bold","label"=" ","dir"="none","fontname"="sans-serif"] } ```

In my opinion this however does not fit the current notation style and as far as I could see, there's no way to influence how groups are displayed apart from setting the style attribute on the cluster subgraph (style=rounded) in the example).

All in all it unfortunately looks like the implementation options for the context map using graphviz are rather limited. :/

Kind regards Daniel