Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Architecture/fix] Throw explicit error for invalid configuration causing overlapping nodes #6176

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
5 changes: 5 additions & 0 deletions .changeset/bright-goats-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mermaid': patch
---

fix: added documentation & error handling to overlapping nodes of architecture diagrams
17 changes: 17 additions & 0 deletions cypress/integration/rendering/architecture.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,23 @@ describe.skip('architecture diagram', () => {
`
);
});

it('should render an error due to overlapping nodes', () => {
// #6120
imgSnapshotTest(
`architecture-beta
group clients(cloud)[Clients]
group vercel_cloud(cloud)[Vercel cloud]

service mobile_app(internet)[Mobile app] in clients
service web_app(internet)[Web app] in clients
service middleware(middleware)[Middleware] in vercel_cloud

mobile_app:R -- L:middleware
web_app:R -- L:middleware
`
);
});
});

// Skipped as the layout is not deterministic, and causes issues in E2E tests.
Expand Down
58 changes: 58 additions & 0 deletions docs/syntax/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,64 @@ architecture-beta
bottom_gateway:T -- B:junctionRight
```

## Limitations

Due to underlying limitations in the rendering engine, there are a couple things to keep in mind when designing your Architecture diagrams.

### One edge per side

Only one edge may come out of each side of a node. If multiple edges are needed, junctions should be used.

**Invalid** (error logged to console):

```mermaid-example
architecture-beta
service nodeA(internet)[Node A]
service nodeB1(internet)[Node B1]
service nodeB2(middleware)[Node B2]

nodeA:R --> L:nodeB1
nodeA:R --> L:nodeB2
```

```mermaid
architecture-beta
service nodeA(internet)[Node A]
service nodeB1(internet)[Node B1]
service nodeB2(middleware)[Node B2]

nodeA:R --> L:nodeB1
nodeA:R --> L:nodeB2
```

**Valid**:

```mermaid-example
architecture-beta
service nodeA(internet)[Node A]
service nodeB1(server)[Node B1]
service nodeB2(server)[Node B2]

junction aToB

nodeA:R --> L:aToB
aToB:T --> B:nodeB1
aToB:B --> T:nodeB2
```

```mermaid
architecture-beta
service nodeA(internet)[Node A]
service nodeB1(server)[Node B1]
service nodeB2(server)[Node B2]

junction aToB

nodeA:R --> L:aToB
aToB:T --> B:nodeB1
aToB:B --> T:nodeB2
```

## Icons

By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`.
Expand Down
16 changes: 16 additions & 0 deletions packages/mermaid/src/diagrams/architecture/architectureDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const state = new ImperativeState<ArchitectureState>(() => ({
groups: {},
edges: [],
registeredIds: {},
usedEdges: {},
config: DEFAULT_ARCHITECTURE_CONFIG,
dataStructures: undefined,
elements: {},
Expand Down Expand Up @@ -203,6 +204,21 @@ const addEdge = function ({
title,
};

state.records.usedEdges[lhsId] ??= {};
state.records.usedEdges[rhsId] ??= {};
if (state.records.usedEdges[lhsId][lhsDir]) {
throw new Error(
`There is already an existing edge coming out of side [${lhsDir}] for service [${lhsId}] to [${rhsId}]. Please use junctions to connect multiple services to one side.`
);
}
if (state.records.usedEdges[rhsId][rhsDir]) {
throw new Error(
`There is already an existing edge coming out of side [${rhsDir}] for service [${rhsId}] to [${lhsId}]. Please use junctions to connect multiple services to one side.`
);
}
state.records.usedEdges[lhsId][lhsDir] = true;
state.records.usedEdges[rhsId][rhsDir] = true;

state.records.edges.push(edge);
if (state.records.nodes[lhsId] && state.records.nodes[rhsId]) {
state.records.nodes[lhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ export interface ArchitectureState extends Record<string, unknown> {
groups: Record<string, ArchitectureGroup>;
edges: ArchitectureEdge[];
registeredIds: Record<string, 'node' | 'group'>;
usedEdges: Record<string, Record<string, boolean>>;
dataStructures?: ArchitectureDataStructures;
elements: Record<string, D3Element>;
config: ArchitectureDiagramConfig;
Expand Down
35 changes: 35 additions & 0 deletions packages/mermaid/src/docs/syntax/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,41 @@ architecture-beta
bottom_gateway:T -- B:junctionRight
```

## Limitations

Due to underlying limitations in the rendering engine, there are a couple things to keep in mind when designing your Architecture diagrams.

### One edge per side

Only one edge may come out of each side of a node. If multiple edges are needed, junctions should be used.

**Invalid** (error logged to console):

```mermaid-example
architecture-beta
service nodeA(internet)[Node A]
service nodeB1(internet)[Node B1]
service nodeB2(middleware)[Node B2]

nodeA:R --> L:nodeB1
nodeA:R --> L:nodeB2
```

**Valid**:

```mermaid-example
architecture-beta
service nodeA(internet)[Node A]
service nodeB1(server)[Node B1]
service nodeB2(server)[Node B2]

junction aToB

nodeA:R --> L:aToB
aToB:T --> B:nodeB1
aToB:B --> T:nodeB2
```

## Icons

By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`.
Expand Down
Loading