Skip to content

Commit 9fd9660

Browse files
Fixes an issue where containers could be rendered outside of their software system boundary on container scoped dynamic views (collaboration style).
1 parent 83f33e9 commit 9fd9660

File tree

4 files changed

+89
-132
lines changed

4 files changed

+89
-132
lines changed

structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,6 @@ public Diagram export(ComponentView view, Integer animationStep) {
308308
}
309309

310310
List<Container> boundaryContainers = getBoundaryContainers(view);
311-
List<Container> containers = getContainers(view);
312311
Set<SoftwareSystem> boundarySoftwareSystems = boundaryContainers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new));
313312
for (SoftwareSystem softwareSystem : boundarySoftwareSystems) {
314313

@@ -325,7 +324,7 @@ public Diagram export(ComponentView view, Integer animationStep) {
325324
}
326325
}
327326

328-
for (Container container : containers) {
327+
for (Container container : getContainers(view)) {
329328
if (container.getSoftwareSystem() == softwareSystem) {
330329
writeElement(view, container, writer);
331330
}
@@ -418,18 +417,37 @@ public Diagram export(DynamicView view, String order) {
418417
}
419418
} else if (element instanceof Container) {
420419
// dynamic view with container scope
421-
boolean includeSoftwareSystemBoundaries = "true".equals(view.getProperties().getOrDefault("structurizr.softwareSystemBoundaries", "false"));
420+
List<CustomElement> customElements = getCustomElements(view);
421+
for (CustomElement customElement : customElements) {
422+
writeElement(view, customElement, writer);
423+
}
424+
if (!customElements.isEmpty()) {
425+
writer.writeLine();
426+
}
427+
428+
List<Person> people = getPeople(view);
429+
for (Person person : people) {
430+
writeElement(view, person, writer);
431+
}
432+
if (!people.isEmpty()) {
433+
writer.writeLine();
434+
}
422435

423-
List<Container> containers = getBoundaryContainers(view);
424-
Set<SoftwareSystem> softwareSystems = containers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new));
436+
List<SoftwareSystem> softwareSystems = getSoftwareSystems(view);
425437
for (SoftwareSystem softwareSystem : softwareSystems) {
438+
writeElement(view, softwareSystem, writer);
439+
}
440+
if (!softwareSystems.isEmpty()) {
441+
writer.writeLine();
442+
}
426443

427-
if (includeSoftwareSystemBoundaries) {
428-
startSoftwareSystemBoundary(view, softwareSystem, writer);
429-
writer.indent();
430-
}
444+
List<Container> boundaryContainers = getBoundaryContainers(view);
445+
Set<SoftwareSystem> boundarySoftwareSystems = boundaryContainers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new));
446+
for (SoftwareSystem softwareSystem : boundarySoftwareSystems) {
447+
448+
startSoftwareSystemBoundary(view, softwareSystem, writer);
431449

432-
for (Container container : containers) {
450+
for (Container container : boundaryContainers) {
433451
if (container.getSoftwareSystem() == softwareSystem) {
434452
startContainerBoundary(view, container, writer);
435453

@@ -440,10 +458,13 @@ public Diagram export(DynamicView view, String order) {
440458
}
441459
}
442460

443-
if (includeSoftwareSystemBoundaries) {
444-
endSoftwareSystemBoundary(view, writer);
445-
writer.outdent();
461+
for (Container container : getContainers(view)) {
462+
if (container.getSoftwareSystem() == softwareSystem) {
463+
writeElement(view, container, writer);
464+
}
446465
}
466+
467+
endSoftwareSystemBoundary(view, writer);
447468
}
448469

449470
for (ElementView elementView : view.getElements()) {

structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,28 @@ public void test_BigBankPlcExample() throws Exception {
203203
edge [fontname="Arial"]
204204
label=<<br /><font point-size="34">Dynamic View: Internet Banking System - API Application</font><br /><font point-size="24">Summarises how the sign in feature works in the single-page application.</font>>
205205
206-
subgraph cluster_11 {
206+
subgraph cluster_7 {
207207
margin=25
208-
label=<<font point-size="24"><br />API Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font>>
208+
label=<<font point-size="24"><br />Internet Banking System</font><br /><font point-size="19">[Software System]</font>>
209209
labelloc=b
210-
color="#444444"
211-
fontcolor="#444444"
212-
fillcolor="#444444"
210+
color="#cccccc"
211+
fontcolor="#cccccc"
212+
fillcolor="#cccccc"
213+
214+
subgraph cluster_11 {
215+
margin=25
216+
label=<<font point-size="24"><br />API Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font>>
217+
labelloc=b
218+
color="#444444"
219+
fontcolor="#444444"
220+
fillcolor="#444444"
213221
214-
12 [id=12,shape=rect, label=<<font point-size="34">Sign In Controller</font><br /><font point-size="19">[Component: Spring MVC Rest Controller]</font><br /><br /><font point-size="24">Allows users to sign in to the<br />Internet Banking System.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"]
215-
15 [id=15,shape=rect, label=<<font point-size="34">Security Component</font><br /><font point-size="19">[Component: Spring Bean]</font><br /><br /><font point-size="24">Provides functionality related<br />to signing in, changing<br />passwords, etc.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"]
222+
12 [id=12,shape=rect, label=<<font point-size="34">Sign In Controller</font><br /><font point-size="19">[Component: Spring MVC Rest Controller]</font><br /><br /><font point-size="24">Allows users to sign in to the<br />Internet Banking System.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"]
223+
15 [id=15,shape=rect, label=<<font point-size="34">Security Component</font><br /><font point-size="19">[Component: Spring Bean]</font><br /><br /><font point-size="24">Provides functionality related<br />to signing in, changing<br />passwords, etc.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"]
224+
}
225+
226+
18 [id=18,shape=cylinder, label=<<font point-size="34">Database</font><br /><font point-size="19">[Container: Oracle Database Schema]</font><br /><br /><font point-size="24">Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"]
227+
8 [id=8,shape=rect, label=<<font point-size="34">Single-Page<br />Application</font><br /><font point-size="19">[Container: JavaScript and Angular]</font><br /><br /><font point-size="24">Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"]
216228
}
217229
218230
8 [id=8,shape=rect, label=<<font point-size="34">Single-Page<br />Application</font><br /><font point-size="19">[Container: JavaScript and Angular]</font><br /><br /><font point-size="24">Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"]

structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,14 @@ public void test_BigBankPlcExample() throws Exception {
270270
271271
AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid")
272272
273-
Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") {
274-
Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="")
275-
Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="")
273+
System_Boundary("InternetBankingSystem_boundary", "Internet Banking System", $tags="Software System") {
274+
Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") {
275+
Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="")
276+
Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="")
277+
}
278+
279+
ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="")
280+
Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="")
276281
}
277282
278283
Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="")

structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java

Lines changed: 28 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,107 +1555,16 @@ public void dynamicView_ExternalComponents() {
15551555
SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2");
15561556
Container container2 = softwareSystem2.addContainer("Container 2");
15571557
Component component3 = container2.addComponent("Component 3");
1558+
Container container4 = softwareSystem2.addContainer("Container 4");
15581559

15591560
component1.uses(component2, "Uses");
15601561
component2.uses(component3, "Uses");
1562+
component3.uses(container4, "Uses");
15611563

15621564
DynamicView dynamicView = workspace.getViews().createDynamicView(container1, "Dynamic", "");
15631565
dynamicView.add(component1, component2);
15641566
dynamicView.add(component2, component3);
1565-
1566-
Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView);
1567-
assertEquals("""
1568-
@startuml
1569-
title <size:24>Dynamic View: Software System 1 - Container 1</size>
1570-
1571-
set separator none
1572-
top to bottom direction
1573-
hide stereotype
1574-
1575-
<style>
1576-
root {
1577-
BackgroundColor: #ffffff;
1578-
FontColor: #444444;
1579-
}
1580-
// Element
1581-
.Element-RWxlbWVudA== {
1582-
BackgroundColor: #ffffff;
1583-
LineColor: #444444;
1584-
LineStyle: 0;
1585-
LineThickness: 2;
1586-
FontColor: #444444;
1587-
FontSize: 24;
1588-
HorizontalAlignment: center;
1589-
Shadowing: 0;
1590-
MaximumWidth: 450;
1591-
}
1592-
// Relationship
1593-
.Relationship-UmVsYXRpb25zaGlw {
1594-
LineThickness: 2;
1595-
LineStyle: 10-10;
1596-
LineColor: #444444;
1597-
FontColor: #444444;
1598-
FontSize: 24;
1599-
}
1600-
// Container 1
1601-
.Boundary-Q29udGFpbmVyIDE= {
1602-
BackgroundColor: #ffffff;
1603-
LineColor: #444444;
1604-
LineStyle: 0;
1605-
LineThickness: 2;
1606-
FontColor: #444444;
1607-
FontSize: 24;
1608-
HorizontalAlignment: center;
1609-
Shadowing: 0;
1610-
}
1611-
// Container 2
1612-
.Boundary-Q29udGFpbmVyIDI= {
1613-
BackgroundColor: #ffffff;
1614-
LineColor: #444444;
1615-
LineStyle: 0;
1616-
LineThickness: 2;
1617-
FontColor: #444444;
1618-
FontSize: 24;
1619-
HorizontalAlignment: center;
1620-
Shadowing: 0;
1621-
}
1622-
</style>
1623-
1624-
rectangle "Container 1\\n<size:16>[Container]</size>" <<Boundary-Q29udGFpbmVyIDE=>> {
1625-
rectangle "==Component 1\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem1.Container1.Component1
1626-
rectangle "==Component 2\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem1.Container1.Component2
1627-
}
1628-
1629-
rectangle "Container 2\\n<size:16>[Container]</size>" <<Boundary-Q29udGFpbmVyIDI=>> {
1630-
rectangle "==Component 3\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem2.Container2.Component3
1631-
}
1632-
1633-
SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <<Relationship-UmVsYXRpb25zaGlw>> : "1: Uses"
1634-
SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <<Relationship-UmVsYXRpb25zaGlw>> : "2: Uses"
1635-
1636-
@enduml""", diagram.getDefinition());
1637-
}
1638-
1639-
@Test
1640-
public void dynamicView_ExternalComponentsAndSoftwareSystemBoundariesIncluded() {
1641-
Workspace workspace = new Workspace("Name");
1642-
1643-
SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1");
1644-
Container container1 = softwareSystem1.addContainer("Container 1");
1645-
Component component1 = container1.addComponent("Component 1");
1646-
Component component2 = container1.addComponent("Component 2");
1647-
1648-
SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2");
1649-
Container container2 = softwareSystem2.addContainer("Container 2");
1650-
Component component3 = container2.addComponent("Component 3");
1651-
1652-
component1.uses(component2, "Uses");
1653-
component2.uses(component3, "Uses");
1654-
1655-
DynamicView dynamicView = workspace.getViews().createDynamicView(container1, "Dynamic", "");
1656-
dynamicView.add(component1, component2);
1657-
dynamicView.add(component2, component3);
1658-
dynamicView.addProperty("structurizr.softwareSystemBoundaries", "true");
1567+
dynamicView.add(component3, container4);
16591568

16601569
Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView);
16611570
assertEquals("""
@@ -1738,22 +1647,26 @@ public void dynamicView_ExternalComponentsAndSoftwareSystemBoundariesIncluded()
17381647
</style>
17391648
17401649
rectangle "Software System 1\\n<size:16>[Software System]</size>" <<Boundary-U29mdHdhcmUgU3lzdGVtIDE=>> {
1741-
rectangle "Container 1\\n<size:16>[Container]</size>" <<Boundary-Q29udGFpbmVyIDE=>> {
1742-
rectangle "==Component 1\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem1.Container1.Component1
1743-
rectangle "==Component 2\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem1.Container1.Component2
1744-
}
1745-
1650+
rectangle "Container 1\\n<size:16>[Container]</size>" <<Boundary-Q29udGFpbmVyIDE=>> {
1651+
rectangle "==Component 1\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem1.Container1.Component1
1652+
rectangle "==Component 2\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem1.Container1.Component2
17461653
}
17471654
1748-
rectangle "Software System 2\\n<size:16>[Software System]</size>" <<Boundary-U29mdHdhcmUgU3lzdGVtIDI=>> {
1749-
rectangle "Container 2\\n<size:16>[Container]</size>" <<Boundary-Q29udGFpbmVyIDI=>> {
1750-
rectangle "==Component 3\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem2.Container2.Component3
1751-
}
1655+
}
17521656
1657+
rectangle "Software System 2\\n<size:16>[Software System]</size>" <<Boundary-U29mdHdhcmUgU3lzdGVtIDI=>> {
1658+
rectangle "Container 2\\n<size:16>[Container]</size>" <<Boundary-Q29udGFpbmVyIDI=>> {
1659+
rectangle "==Component 3\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem2.Container2.Component3
17531660
}
17541661
1662+
rectangle "==Container 4\\n<size:16>[Container]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem2.Container4
1663+
}
1664+
1665+
rectangle "==Container 4\\n<size:16>[Container]</size>" <<Element-RWxlbWVudA==>> as SoftwareSystem2.Container4
1666+
17551667
SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <<Relationship-UmVsYXRpb25zaGlw>> : "1: Uses"
17561668
SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <<Relationship-UmVsYXRpb25zaGlw>> : "2: Uses"
1669+
SoftwareSystem2.Container2.Component3 --> SoftwareSystem2.Container4 <<Relationship-UmVsYXRpb25zaGlw>> : "3: Uses"
17571670
17581671
@enduml""", diagram.getDefinition());
17591672
}
@@ -2261,16 +2174,22 @@ public void dynamicView_ContainerScopedWithGroups() {
22612174
}
22622175
</style>
22632176
2264-
rectangle "A\\n<size:16>[Container]</size>" <<Boundary-QQ==>> {
2265-
rectangle "Group 1" <<Group-R3JvdXAgMQ==>> as groupR3JvdXAgMQ== {
2266-
rectangle "==A\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as A.A.A
2177+
rectangle "A\\n<size:16>[Software System]</size>" <<Boundary-QQ==>> {
2178+
rectangle "A\\n<size:16>[Container]</size>" <<Boundary-QQ==>> {
2179+
rectangle "Group 1" <<Group-R3JvdXAgMQ==>> as groupR3JvdXAgMQ== {
2180+
rectangle "==A\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as A.A.A
2181+
}
2182+
22672183
}
22682184
22692185
}
22702186
2271-
rectangle "B\\n<size:16>[Container]</size>" <<Boundary-Qg==>> {
2272-
rectangle "Group 2" <<Group-R3JvdXAgMg==>> as groupR3JvdXAgMg== {
2273-
rectangle "==B\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as B.B.B
2187+
rectangle "B\\n<size:16>[Software System]</size>" <<Boundary-Qg==>> {
2188+
rectangle "B\\n<size:16>[Container]</size>" <<Boundary-Qg==>> {
2189+
rectangle "Group 2" <<Group-R3JvdXAgMg==>> as groupR3JvdXAgMg== {
2190+
rectangle "==B\\n<size:16>[Component]</size>" <<Element-RWxlbWVudA==>> as B.B.B
2191+
}
2192+
22742193
}
22752194
22762195
}

0 commit comments

Comments
 (0)