Obsah
1. Knihovny Diagrams a go-diagrams určené pro tvorbu diagramů s architekturou systémů
2. Clustery v diagramech tvořených knihovnou Diagrams
3. Vytvoření clusteru v diagramu
4. Větší množství clusterů v diagramu
5. Hierarchické (vnořené) clustery
6. Další příklad hierarchických clusterů
7. Knihovna go-diagrams inspirovaná knihovnou Diagrams
8. Prázdný diagram vytvořený knihovnou go-diagrams
9. Diagram s dvojicí uzlů a jednou hranou
10. Propojení většího množství uzlů
12. Pojmenování skupiny v diagramu
14. Programové vytvoření skupiny s vazbami 1:N a N:1
15. Porovnání knihoven Diagrams a go-diagrams
16. Repositář s demonstračními příklady psanými v Pythonu
17. Repositář s demonstračními příklady psanými v jazyce Go
18. Odkazy na články s tématem programové tvorby grafů a diagramů
1. Knihovny Diagrams a go-diagrams určené pro tvorbu diagramů s architekturou systémů
V dnešním článku nejdříve dokončíme popis možností knihovny nazvané Diagrams, jíž jsme se zabývali minule, a poté se seznámíme s další podobně koncipovanou knihovnou, tentokrát ovšem určenou pro programovací jazyk Go. Jedná se o knihovnu pojmenovanou go-diagrams. Tato knihovna je navržena takovým způsobem, aby byla s původní Pythonovskou knihovnou Diagrams do určité míry kompatibilní, což se týká především způsobu definice diagramů (ve skutečnosti je však použití varianty pro Go složitější, než v případě knihovny Diagrams pro Python, což si ostatně ukážeme na demonstračních příkladech).
2. Clustery v diagramech tvořených knihovnou Diagrams
Minule jsme si ukázali, jakým způsobem se s využitím knihovny Diagrams tvoří diagramy. Připomeňme si, že se nejprve vytvoří objekt typu Diagram (ideálně v rámci takzvaného kontextu, tedy bloku with), následně se zkonstruují různé typy uzlů a nakonec se tyto uzly propojí s využitím přetíženého operátoru >>. Taktéž je možné namísto jediného uzlu použít seznam či n-tici s uzly – poté se zobrazí propojení typu 1:N nebo N:1:
from diagrams import Diagram
from diagrams.onprem.queue import Kafka, ActiveMQ
from diagrams.programming.language import Go, Rust
from diagrams.aws.database import RDS
with Diagram("Clusters #1", show=True, direction="LR"):
# definice uzlu
consumer = Kafka("input stream")
db = RDS("storage")
# rozvetveni
workersA = [Go("worker #1"),
Go("worker #2"),
Go("worker #3")]
buffer = ActiveMQ("buffer")
# rozvetveni
workersB = [Rust("worker #1"),
Rust("worker #2"),
Rust("worker #3")]
producer = Kafka("output stream")
# propojeni uzlu grafu orientovanymi hranami
consumer >> workersA >> buffer >> workersB >> producer
db >> workersA
Výsledek dnešního prvního demonstračního příkladu vypadá následovně:
Obrázek 1: Diagram vytvořený prvním demonstračním příkladem.
Ve skutečnosti je však mnohdy vhodné některé uzly seskupit do takzvaného clusteru. Jak se tato operace provede si ukážeme v navazujících kapitolách.
3. Vytvoření clusteru v diagramu
Cluster neboli seskupení uzlů, se ve skutečnosti provede velmi jednoduše. Postačuje pouze vytvořit uzly (resp. přesněji řečeno ty uzly, které se mají sdružit) v rámci kontextu, tedy uvnitř bloku with v němž je zkonstruován objekt typu Cluster. V následující ukázce skriptu se vytvoří dva uzly sdružené do clusteru:
with Cluster("jméno clusteru"):
# definice uzlu v clusteru
uzel1 = Kafka("input stream")
uzel2 = RDS("storage")
Samozřejmě je však možné pracovat i se seznamem uzlů, tedy následovně:
with Cluster("jméno clusteru"):
# definice uzlu v clusteru
uzel1 = Kafka("input stream")
# rozvetveni
uzly = [Go("worker #1"),
Go("worker #2"),
Go("worker #3")]
uzel2 = RDS("storage")
Podívejme se nyní na nepatrně upravený předchozí demonstrační příklad, do něhož byl přidán cluster:
from diagrams import Diagram
from diagrams import Cluster
from diagrams.onprem.queue import Kafka, ActiveMQ
from diagrams.programming.language import Go, Rust
from diagrams.aws.database import RDS
with Diagram("Clusters #2", show=True, direction="LR"):
# definice clusteru
with Cluster("Input processor"):
# definice uzlu v clusteru
consumer = Kafka("input stream")
# rozvetveni
workersA = [Go("worker #1"),
Go("worker #2"),
Go("worker #3")]
db = RDS("storage")
# definice uzlu mimo cluster
buffer = ActiveMQ("buffer")
# rozvetveni
workersB = [Rust("worker #1"),
Rust("worker #2"),
Rust("worker #3")]
producer = Kafka("output stream")
# propojeni uzlu grafu orientovanymi hranami
consumer >> workersA >> buffer >> workersB >> producer
db >> workersA
Výsledek by měl nyní vypadat následovně:
Obrázek 2: Diagram vytvořený druhým demonstračním příkladem.
4. Větší množství clusterů v diagramu
Nic nám samozřejmě nebrání vytvořit v diagramu větší množství clusterů. Jedinou podmínkou je, že se tyto clustery nebudou překrývat (i to je však možné zařídit některými triky). V dalším demonstračním příkladu je ukázána definice diagramu s několika uzly rozdělenými mezi dvojici clusterů. Oba clustery (resp. uzly v clusteru) jsou propojeny přes buffer/frontu představovanou uzlem ActiveMQ (což je jeden ze známých message brokerů):
from diagrams import Diagram
from diagrams import Cluster
from diagrams.onprem.queue import Kafka, ActiveMQ
from diagrams.programming.language import Go, Rust
from diagrams.aws.database import RDS
with Diagram("Clusters #3", show=True, direction="LR"):
# definice clusteru
with Cluster("Input processor"):
# definice uzlu v clusteru
consumer = Kafka("input stream")
# rozvetveni
workersA = [Go("worker #1"),
Go("worker #2"),
Go("worker #3")]
db = RDS("storage")
# definice uzlu mimo cluster
buffer = ActiveMQ("buffer")
with Cluster("Output processor"):
# rozvetveni
workersB = [Rust("worker #1"),
Rust("worker #2"),
Rust("worker #3")]
producer = Kafka("output stream")
# propojeni uzlu grafu orientovanymi hranami
consumer >> workersA >> buffer >> workersB >> producer
db >> workersA
Výsledek bude v tomto případě následující:
Obrázek 3: Diagram vytvořený třetím demonstračním příkladem.
5. Hierarchické (vnořené) clustery
Cluster může kromě uzlů obsahovat i další clustery, takže diagram může zobrazovat i poměrně složitou hierarchii navrhovaného systému. V Pythonu se vnořené clustery definují vnořenými bloky with:
with Cluster("Input processor"):
# definice uzlu v clusteru
uzel1 = Kafka("input stream")
# definice clusteru uvnitř cluster
with Cluster("Worker group A"):
# definice uzlu v podclusteru
uzel2 = Kafka("input stream")
Prakticky může vypadat definice diagramu s hierarchií clusterů následovně:
from diagrams import Diagram
from diagrams import Cluster
from diagrams.onprem.queue import Kafka, ActiveMQ
from diagrams.programming.language import Go, Rust
from diagrams.aws.database import RDS
with Diagram("Clusters #4", show=True, direction="LR"):
# definice clusteru
with Cluster("Input processor"):
# definice uzlu v clusteru
consumer = Kafka("input stream")
# definice clusteru uvnitr cluster
with Cluster("Worker group A"):
# rozvetveni
workersA = [Go("worker #1"),
Go("worker #2"),
Go("worker #3")]
db = RDS("storage")
# definice uzlu mimo cluster
buffer = ActiveMQ("buffer")
with Cluster("Output processor"):
# definice clusteru uvnitr cluster
with Cluster("Worker group B"):
# rozvetveni
workersB = [Rust("worker #1"),
Rust("worker #2"),
Rust("worker #3")]
producer = Kafka("output stream")
# propojeni uzlu grafu orientovanymi hranami
consumer >> workersA >> buffer >> workersB >> producer
db >> workersA
Výsledný diagram:
Obrázek 4: Diagram vytvořený čtvrtým demonstračním příkladem.
6. Další příklad hierarchických clusterů
Poslední demonstrační příklad, který je založen na knihovně Diagrams, ukazuje způsob vytvoření diagramu s trojím zanořením clusterů, tedy situaci, kdy jeden cluster obsahuje další clustery a i ty obsahují další clustery z uzly. Maximální úroveň zanoření není prakticky omezena:
from diagrams import Diagram
from diagrams import Cluster
from diagrams.onprem.queue import Kafka, ActiveMQ
from diagrams.programming.language import Go, Rust
from diagrams.aws.database import RDS
with Diagram("Clusters #5", show=True, direction="LR"):
with Cluster("Out wonderful pipeline"):
# definice clusteru
with Cluster("Input processor"):
# definice uzlu v clusteru
consumer = Kafka("input stream")
# definice clusteru uvnitr cluster
with Cluster("Worker group A"):
# rozvetveni
workersA = [Go("worker #1"),
Go("worker #2"),
Go("worker #3")]
db = RDS("storage")
# definice uzlu mimo cluster
buffer = ActiveMQ("buffer")
with Cluster("Output processor"):
# definice clusteru uvnitr cluster
with Cluster("Worker group B"):
# rozvetveni
workersB = [Rust("worker #1"),
Rust("worker #2"),
Rust("worker #3")]
producer = Kafka("output stream")
# propojeni uzlu grafu orientovanymi hranami
consumer >> workersA >> buffer >> workersB >> producer
db >> workersA
Obrázek 5: Diagram vytvořený pátým demonstračním příkladem.
7. Knihovna go-diagrams inspirovaná knihovnou Diagrams
Ve druhé části dnešního článku se seznámíme se základy používání knihovny nazvané go-diagrams. Jedná se o poměrně přiléhavý název, protože tato knihovna je skutečně určena pro použití v programovacím jazyku Go a současně byl její návrh do určité míry inspirován výše popsanou knihovnou Diagrams. Nicméně – jak ostatně uvidíme v navazujících kapitolách, v nichž budou ukázány demonstrační příklady – se skutečně jedná pouze o inspiraci, protože vytváření diagramů je v go-diagrams přece jen poněkud odlišné. Je to do značné míry způsobeno rozdílnostmi mezi Pythonem a Go: v prvním případě se jedná o dynamicky typovaný programovací jazyk se sémantikou pro definici kontextů s využitím bloku with, ve druhém případě o jazyk staticky typovaný, navíc nepodporující ani blok with ani žádnou další podobně koncipovanou jazykovou konstrukci (Go nemá ani koncept nepovinných pojmenovaných parametrů, což je opět jeden z rozdílů, na který narazíme).
Další rozdíl spočívá v tom, že zatímco knihovna Diagrams dokázala diagramy vykreslovat automaticky (dokonce i s náhledem), je tomu v případě go-diagrams jinak, protože po překladu a spuštění kódu se vytvoří soubor s koncovkou .dot, který je nutné následně dále zpracovat nástrojem GraphViz, o němž jsme se v tomto miniseriálu již několikrát zmínili. Teprve GraphViz dokáže z definice grafu (tentokrát popsaného pomocí DSL) vytvořit rastrový obrázek, vektorovou kresbu, dokument v PDF atd.
8. Prázdný diagram vytvořený knihovnou go-diagrams
Knihovna go-diagrams se instaluje s využitím základních prostředků nabízených ekosystémem programovacího jazyka Go. Pro stažení, překlad a instalaci knihovny je nutné použít tento příkaz:
$ go get github.com/blushft/go-diagrams
Pro otestování, zda je knihovna go-diagrams nainstalována a zda je volatelná, použijeme následující demonstrační příklad, který představuje definici prázdného (nicméně korektního) diagramu. Povšimněte si použití konstruktoru pro vytvoření diagramu a taktéž explicitního požadavku na jeho vykreslení (což ovšem neznamená vykreslení obrázku, ale vygenerování souboru .dot):
package main
import (
"log"
"github.com/blushft/go-diagrams/diagram"
)
func main() {
// inicializace objektu představujícího diagram
diagram, err := diagram.New(diagram.Label("Diagram #1"), diagram.Filename("diagram1"))
// kontrola konstrukce objektu
if err != nil {
log.Fatal(err)
}
// vykreslení diagramu
err = diagram.Render()
// kontrola, zda bylo vykreslení provedeno bez chyby
if err != nil {
log.Fatal(err)
}
}
Příklad přeložíme (na pozadí) a spustíme:
$ go run diagrams1.go
Po spuštění by se měl v aktuálním (pracovním) adresáři objevit nový podadresář, který obsahuje jak definici diagramu v jazyku DOT, tak i datové soubory s ikonami jednotlivých uzlů. Tyto datové soubory uvidíme u následujících demonstračních příkladů. Podívejme se na obsah zmíněného souboru s definicí diagramu v jazyku DOT:
digraph root {
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=13;
label="Diagram #1";
nodesep=0.6;
pad=2;
rankdir=LR;
ranksep=0.75;
splines=ortho;
}
Vykreslení obrázku zajistí příkaz:
$ dot -Tpng diagram1.dot > diagram.png
Obrázek 6: Výsledek získaný předchozím příkazem.
9. Diagram s dvojicí uzlů a jednou hranou
První demonstrační příklad založený na knihovně go-diagrams zajisté nebyl příliš oslnivý, protože dokázal vykreslit pouze prázdný diagram. Ukažme si tedy poněkud složitější diagram (vlastně první diagram, který má skutečně reálný význam) s dvojicí uzlů propojených hranou. Podobně jako v knihovně Diagrams je i zde zapotřebí nejdříve zkonstruovat oba uzly a následně je propojit – ovšem nikoli přetíženým operátorem >>, ale metodou Connect. Zdrojový kód tohoto příkladu naleznete na adrese https://github.com/tisnik/go-root/blob/master/article74/diagram2.go:
package main
import (
"log"
"github.com/blushft/go-diagrams/diagram"
"github.com/blushft/go-diagrams/nodes/apps"
)
func main() {
// inicializace objektu představujícího diagram
diagram, err := diagram.New(diagram.Label("Diagram #2"), diagram.Filename("diagram2"))
// kontrola konstrukce objektu
if err != nil {
log.Fatal(err)
}
// deklarace uzlů v diagramu
inet := apps.Network.Internet().Label("Internet")
proxy := apps.Network.Caddy().Label("3scale")
// propojení uzlů v diagramu
diagram.Connect(inet, proxy)
// vykreslení diagramu
err = diagram.Render()
// kontrola, zda bylo vykreslení provedeno bez chyby
if err != nil {
log.Fatal(err)
}
}
Vygenerovaný soubor s definicí grafu v doménově specifickém jazyce DOT:
digraph root {
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=13;
label="Diagram #2";
nodesep=0.6;
pad=2;
rankdir=LR;
ranksep=0.75;
splines=ortho;
wuyslsjy->munjokhx [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
munjokhx [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/apps/network/caddy.png", imagescale=true, label="3scale", labelloc=b, shape=none, style=rounded, width=1.4 ];
wuyslsjy [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/apps/network/internet.png", imagescale=true, label=Internet, labelloc=b, shape=none, style=rounded, width=1.4 ];
}
Diagram po vykreslení příkazem dot:
Obrázek 7: Výsledek získaný příkazem dot.
10. Propojení většího množství uzlů
Ve třetím demonstračním příkladu založeném na knihovně go-diagrams je definován diagram s trojicí uzlů, které jsou načteny ze dvou různých balíčků (viz sekce import). Tento diagram budeme dále rozvíjet v navazujících kapitolách:
package main
import (
"log"
"github.com/blushft/go-diagrams/diagram"
"github.com/blushft/go-diagrams/nodes/apps"
"github.com/blushft/go-diagrams/nodes/gcp"
)
func main() {
// inicializace objektu představujícího diagram
diagram, err := diagram.New(diagram.Label("Diagram #3"), diagram.Filename("diagram3"))
// kontrola konstrukce objektu
if err != nil {
log.Fatal(err)
}
// deklarace uzlů v diagramu
inet := apps.Network.Internet().Label("Internet")
proxy := apps.Network.Caddy().Label("3scale")
router := gcp.Network.Router().Label("Router")
// propojení uzlů v diagramu
diagram.Connect(inet, proxy)
diagram.Connect(proxy, router)
// vykreslení diagramu
err = diagram.Render()
// kontrola, zda bylo vykreslení provedeno bez chyby
if err != nil {
log.Fatal(err)
}
}
Vygenerovaný soubor s definicí grafu v doménově specifickém jazyce DOT:
digraph root {
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=13;
label="Diagram #3";
nodesep=0.6;
pad=2;
rankdir=LR;
ranksep=0.75;
splines=ortho;
loggiusv->vwecchtf [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
dkxkvtkt->loggiusv [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
dkxkvtkt [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/apps/network/internet.png", imagescale=true, label=Internet, labelloc=b, shape=none, style=rounded, width=1.4 ];
loggiusv [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/apps/network/caddy.png", imagescale=true, label="3scale", labelloc=b, shape=none, style=rounded, width=1.4 ];
vwecchtf [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/gcp/network/router.png", imagescale=true, label=Router, labelloc=b, shape=none, style=rounded, width=1.4 ];
}
Diagram po vykreslení příkazem dot:
Obrázek 8: Výsledek získaný příkazem dot.
11. Skupiny uzlů v diagramu
Čtvrtý demonstrační příklad je již odlišný od příkladů předchozích, protože zde vybrané uzly sdružujeme do skupin, což je vlastně obdoba clusterů z knihovny Diagrams. Na skupinu se můžeme dívat jako na kontejner pro uzly, které se do skupiny přidávají metodou Add. Skupina je pojmenována, ovšem toto jméno není ve výsledném diagramu zobrazeno:
package main
import (
"log"
"github.com/blushft/go-diagrams/diagram"
"github.com/blushft/go-diagrams/nodes/apps"
"github.com/blushft/go-diagrams/nodes/gcp"
)
func main() {
// inicializace objektu představujícího diagram
diagram4, err := diagram.New(diagram.Label("Diagram #4"), diagram.Filename("diagram4"))
// kontrola konstrukce objektu
if err != nil {
log.Fatal(err)
}
// deklarace uzlů v diagramu
inet := apps.Network.Internet().Label("Internet")
proxy := apps.Network.Caddy().Label("3scale")
router := gcp.Network.Router().Label("Router")
diagram4.Group(diagram.NewGroup("Wild west").Add(inet).Add(proxy))
// propojení uzlů v diagramu
diagram4.Connect(inet, proxy)
diagram4.Connect(proxy, router)
// vykreslení diagramu
err = diagram4.Render()
// kontrola, zda bylo vykreslení provedeno bez chyby
if err != nil {
log.Fatal(err)
}
}
Vygenerovaný soubor s definicí grafu v doménově specifickém jazyce DOT:
digraph root {
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=13;
label="Diagram #4";
nodesep=0.6;
pad=2;
rankdir=LR;
ranksep=0.75;
splines=ortho;
winxwgsu->qbhrmyaa [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
qbhrmyaa->rkhrhwbq [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
subgraph "cluster_Wild west" {
bgcolor="#E5F5FD";
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=12;
labeljust=l;
pencolor="#AEB6BE";
shape=box;
style=rounded;
qbhrmyaa [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=2.1999999999999997, image="assets/apps/network/caddy.png", imagescale=true, label="3scale", labelloc=b, shape=none, style=rounded, width=1.4 ];
winxwgsu [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=2.1999999999999997, image="assets/apps/network/internet.png", imagescale=true, label=Internet, labelloc=b, shape=none, style=rounded, width=1.4 ];
};
rkhrhwbq [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/gcp/network/router.png", imagescale=true, label=Router, labelloc=b, shape=none, style=rounded, width=1.4 ];
}
Diagram po vykreslení příkazem dot:
Obrázek 9: Diagram po vykreslení příkazem dot.
12. Pojmenování skupiny v diagramu
Dostáváme se k příkladu pátému, který se od příkladu předchozího liší pouze v jediné maličkosti – v přidání štítku (label) do skupiny. Štítek bude zobrazen i ve výsledném diagramu:
diagram5.Group(diagram.NewGroup("Wild west").Label("Wild west").Add(inet).Add(proxy))
Úplný zdrojový kód tohoto příkladu:
package main
import (
"log"
"github.com/blushft/go-diagrams/diagram"
"github.com/blushft/go-diagrams/nodes/apps"
"github.com/blushft/go-diagrams/nodes/gcp"
)
func main() {
// inicializace objektu představujícího diagram
diagram5, err := diagram.New(diagram.Label("Diagram #5"), diagram.Filename("diagram5"))
// kontrola konstrukce objektu
if err != nil {
log.Fatal(err)
}
// deklarace uzlů v diagramu
inet := apps.Network.Internet().Label("Internet")
proxy := apps.Network.Caddy().Label("3scale")
router := gcp.Network.Router().Label("Router")
diagram5.Group(diagram.NewGroup("Wild west").Label("Wild west").Add(inet).Add(proxy))
// propojení uzlů v diagramu
diagram5.Connect(inet, proxy)
diagram5.Connect(proxy, router)
// vykreslení diagramu
err = diagram5.Render()
// kontrola, zda bylo vykreslení provedeno bez chyby
if err != nil {
log.Fatal(err)
}
}
Opět si ukažme, jak vypadá soubor vygenerovaný v doménově specifickém jazyku DOT:
digraph root {
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=13;
label="Diagram #5";
nodesep=0.6;
pad=2;
rankdir=LR;
ranksep=0.75;
splines=ortho;
pmpohiec->tymwuwkk [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
tymwuwkk->juurwdxb [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
subgraph "cluster_Wild west" {
bgcolor="#E5F5FD";
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=12;
label="Wild west";
labeljust=l;
pencolor="#AEB6BE";
shape=box;
style=rounded;
pmpohiec [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=2.1999999999999997, image="assets/apps/network/internet.png", imagescale=true, label=Internet, labelloc=b, shape=none, style=rounded, width=1.4 ];
tymwuwkk [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=2.1999999999999997, image="assets/apps/network/caddy.png", imagescale=true, label="3scale", labelloc=b, shape=none, style=rounded, width=1.4 ];
};
juurwdxb [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/gcp/network/router.png", imagescale=true, label=Router, labelloc=b, shape=none, style=rounded, width=1.4 ];
}
Výsledný diagram po vykreslení příkazem dot:
Obrázek 10: Výsledný diagram po vykreslení příkazem dot.
13. Propojení typu 1:N a N:1
V knihovně Diagrams se spojení typu 1:N a N:1 řešilo takovým způsobem, že se uzly uložily do seznamu nebo do n-tice. V případě go-diagrams není řešení tak přímočaré. Jeden z možných způsobů zobrazení spojení 1:N a N:1 spočívá v tom, že všechna tato spojení explicitně zapíšeme do zdrojového kódu s definicí diagramu. Uzly mohou, ale nutně nemusí, být uloženy ve skupině:
diagram6.Connect(inet, proxy1) diagram6.Connect(inet, proxy2) diagram6.Connect(inet, proxy3) diagram6.Connect(proxy1, router) diagram6.Connect(proxy2, router) diagram6.Connect(proxy3, router)
Celý zdrojový kód tohoto příkladu vypadá následovně:
package main
import (
"log"
"github.com/blushft/go-diagrams/diagram"
"github.com/blushft/go-diagrams/nodes/apps"
"github.com/blushft/go-diagrams/nodes/gcp"
"github.com/blushft/go-diagrams/nodes/generic"
)
func main() {
// inicializace objektu představujícího diagram
diagram6, err := diagram.New(diagram.Label("Diagram #6"), diagram.Filename("diagram6"))
// kontrola konstrukce objektu
if err != nil {
log.Fatal(err)
}
// deklarace uzlů v diagramu
inet := apps.Network.Internet().Label("Internet")
proxy1 := generic.Network.Firewall().Label("3scale")
proxy2 := generic.Network.Firewall().Label("3scale")
proxy3 := generic.Network.Firewall().Label("3scale")
router := gcp.Network.Router().Label("Router")
diagram6.Connect(inet, proxy1)
diagram6.Connect(inet, proxy2)
diagram6.Connect(inet, proxy3)
diagram6.Connect(proxy1, router)
diagram6.Connect(proxy2, router)
diagram6.Connect(proxy3, router)
// vykreslení diagramu
err = diagram6.Render()
// kontrola, zda bylo vykreslení provedeno bez chyby
if err != nil {
log.Fatal(err)
}
}
Vygenerovaný soubor s popisem diagramu v doménově specifickém jazyce DOT:
digraph root {
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=13;
label="Diagram #6";
nodesep=0.6;
pad=2;
rankdir=LR;
ranksep=0.75;
splines=ortho;
ddncgkdg->yetpkgzs [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
ddncgkdg->xnbxgnsh [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
ddncgkdg->jubmcseh [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
yetpkgzs->jhvutahu [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
xnbxgnsh->jhvutahu [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
jubmcseh->jhvutahu [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
ddncgkdg [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/apps/network/internet.png", imagescale=true, label=Internet, labelloc=b, shape=none, style=rounded, width=1.4 ];
jhvutahu [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/gcp/network/router.png", imagescale=true, label=Router, labelloc=b, shape=none, style=rounded, width=1.4 ];
jubmcseh [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/generic/network/firewall.png", imagescale=true, label="3scale", labelloc=b, shape=none, style=rounded, width=1.4 ];
xnbxgnsh [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/generic/network/firewall.png", imagescale=true, label="3scale", labelloc=b, shape=none, style=rounded, width=1.4 ];
yetpkgzs [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/generic/network/firewall.png", imagescale=true, label="3scale", labelloc=b, shape=none, style=rounded, width=1.4 ];
}
Výsledný diagram po vykreslení příkazem dot:
Obrázek 11: Výsledný diagram po vykreslení příkazem dot.
14. Programové vytvoření skupiny s vazbami 1:N a N:1
V dnešním posledním demonstračním příkladu je ukázáno, jak je možné vylepšit nutnost přidání několika podobných uzlů do skupiny, typicky se zajištěním napojení typu 1:N a N:1. Nejprve uzly vytvoříme a uložíme je do pole, popř. do řezu (slice):
proxies := make([]*diagram.Node, ProxiesCount)
for i := 0; i < ProxiesCount; i++ {
label := fmt.Sprintf("Proxy #%d", i+1)
proxies[i] = generic.Network.Firewall().Label(label)
}
Následně vytvoříme skupinu a všechny uzly z pole/řezu do ní přidáme. Navíc metodami pojmenovanými ConnectAllFrom a ConnectAllTo zajistíme propojení všech těchto uzlů s uzlem předcházejícím (vazba 1:N) i uzlem následujícím (vazba N:1):
diagram7.Group(diagram.NewGroup("Proxies").
Add(proxies...).
ConnectAllFrom(inet.ID()).
ConnectAllTo(router.ID()),
)
Úplný zdrojový kód dnešního posledního příkladu vypadá takto:
package main
import (
"fmt"
"log"
"github.com/blushft/go-diagrams/diagram"
"github.com/blushft/go-diagrams/nodes/apps"
"github.com/blushft/go-diagrams/nodes/gcp"
"github.com/blushft/go-diagrams/nodes/generic"
)
const ProxiesCount = 3
func main() {
// inicializace objektu představujícího diagram
diagram7, err := diagram.New(diagram.Label("Diagram #7"), diagram.Filename("diagram7"))
// kontrola konstrukce objektu
if err != nil {
log.Fatal(err)
}
// deklarace uzlů v diagramu
inet := apps.Network.Internet().Label("Internet")
proxies := make([]*diagram.Node, ProxiesCount)
for i := 0; i < ProxiesCount; i++ {
label := fmt.Sprintf("Proxy #%d", i+1)
proxies[i] = generic.Network.Firewall().Label(label)
}
router := gcp.Network.Router().Label("Router")
diagram7.Add(inet)
diagram7.Add(router)
diagram7.Group(diagram.NewGroup("Proxies").
Add(proxies...).
ConnectAllFrom(inet.ID()).
ConnectAllTo(router.ID()),
)
// vykreslení diagramu
err = diagram7.Render()
// kontrola, zda bylo vykreslení provedeno bez chyby
if err != nil {
log.Fatal(err)
}
}
Soubor DOT vygenerovaný tímto příkladem:
digraph root {
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=13;
label="Diagram #7";
nodesep=0.6;
pad=2;
rankdir=LR;
ranksep=0.75;
splines=ortho;
zlvpebgu->kjxceadk [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
ixelswwc->kjxceadk [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
dpjzrxvm->bxkejjqb [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
dpjzrxvm->zlvpebgu [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
dpjzrxvm->ixelswwc [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
bxkejjqb->kjxceadk [ color="#7B8894", dir=forward, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13 ];
subgraph cluster_Proxies {
bgcolor="#E5F5FD";
fontcolor="#2D3436";
fontname="Sans-Serif";
fontsize=12;
labeljust=l;
pencolor="#AEB6BE";
shape=box;
style=rounded;
bxkejjqb [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/generic/network/firewall.png", imagescale=true, label="Proxy #1", labelloc=b, shape=none, style=rounded, width=1.4 ];
ixelswwc [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/generic/network/firewall.png", imagescale=true, label="Proxy #3", labelloc=b, shape=none, style=rounded, width=1.4 ];
zlvpebgu [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/generic/network/firewall.png", imagescale=true, label="Proxy #2", labelloc=b, shape=none, style=rounded, width=1.4 ];
};
dpjzrxvm [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/apps/network/internet.png", imagescale=true, label=Internet, labelloc=b, shape=none, style=rounded, width=1.4 ];
kjxceadk [ fixedsize=true, fontcolor="#2D3436", fontname="Sans-Serif", fontsize=13, height=1.7999999999999998, image="assets/gcp/network/router.png", imagescale=true, label=Router, labelloc=b, shape=none, style=rounded, width=1.4 ];
}
A konečně toto je podoba výsledného diagramu:
Obrázek 12: Výsledný diagram po vykreslení příkazem dot.
15. Porovnání knihoven Diagrams a go-diagrams
Knihovny Diagrams a go-diagrams jsou sice založeny na podobné filozofii zápisu definice diagramů, ovšem již z předchozích příkladů bylo patrné, jak se obě knihovny od sebe odlišují. Diagrams je – alespoň podle mého názoru – mnohem jednodušeji použitelná a nabízí větší flexibilitu. Výhodou go-diagrams je, alespoň teoreticky, větší typová bezpečnost při deklaraci grafů, ovšem v praxi se ukazuje, že typové kontroly nejsou v tomto případě dokonalé a na chybu se přijde až při volání nástroje dot. Osobně jsem si pro reálné použití vybral Diagrams.
16. Repositář s demonstračními příklady psanými v Pythonu
Zdrojové kódy všech minule i dnes popsaných demonstračních příkladů určených pro Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
17. Repositář s demonstračními příklady psanými v jazyce Go
Budu se částečně opakovat: zdrojové kódy všech dnes použitých demonstračních příkladů vytvořených v jazyce Go byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně stovku kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:
| # | Příklad/soubor | Stručný popis | Cesta |
|---|---|---|---|
| 1 | diagram1.go | prázdný diagram vytvořený knihovnou go-diagrams | https://github.com/tisnik/go-root/blob/master/article74/diagram1.go |
| 2 | diagram2.go | diagram s dvojicí uzlů a jednou hranou | https://github.com/tisnik/go-root/blob/master/article74/diagram2.go |
| 3 | diagram3.go | propojení většího množství uzlů | https://github.com/tisnik/go-root/blob/master/article74/diagram3.go |
| 4 | diagram4.go | skupiny uzlů v diagramu | https://github.com/tisnik/go-root/blob/master/article74/diagram4.go |
| 5 | diagram5.go | pojmenování skupiny v diagramu | https://github.com/tisnik/go-root/blob/master/article74/diagram5.go |
| 6 | diagram6.go | propojení typu 1:N a N:1 | https://github.com/tisnik/go-root/blob/master/article74/diagram6.go |
| 7 | diagram7.go | programové vytvoření skupiny s vazbami 1:N a N:1 | https://github.com/tisnik/go-root/blob/master/article74/diagram7.go |
18. Odkazy na články s tématem programové tvorby grafů a diagramů
V této kapitole jsou uvedeny odkazy na předchozí články, v nichž jsme se zabývali tvorbou různých typů grafů a diagramů – a to v naprosté většině případů s využitím nějakého doménově specifického jazyka neboli DSL (Domain Specific Language), popř. nějakého univerzálního programovacího jazyka:
- Nástroje pro tvorbu UML diagramů
https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu/ - Nástroje pro tvorbu UML diagramů z příkazové řádky
https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu-z-prikazove-radky/ - Nástroje pro tvorbu UML diagramů z příkazové řádky (II)
https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu-z-prikazove-radky-ii/ - Nástroje pro tvorbu grafů a diagramů z příkazové řádky
https://www.root.cz/clanky/nastroje-pro-tvorbu-grafu-a-diagramu-z-prikazove-radky/ - Sledování správy paměti v Pythonu s využitím nástroje objgraph
https://www.root.cz/clanky/sledovani-spravy-pameti-v-pythonu-s-vyuzitim-nastroje-objgraph/ - Programová tvorba diagramů v jazyku Clojure s využitím knihovny Rhizome
https://www.root.cz/clanky/programova-tvorba-diagramu-v-jazyku-clojure-s-vyuzitim-knihovny-rhizome/ - Tvorba sekvenčních diagramů v Pythonu s využitím knihovny Napkin
https://www.root.cz/clanky/tvorba-sekvencnich-diagramu-v-pythonu-s-vyuzitim-knihovny-napkin/ - Tvorba vývojových diagramů přímo ze zdrojových kódů Pythonu
https://www.root.cz/clanky/tvorba-vyvojovych-diagramu-primo-ze-zdrojovych-kodu-pythonu/ - Tvorba diagramů s architekturou systémů s využitím knihovny Diagrams
https://www.root.cz/clanky/tvorba-diagramu-s-architekturou-systemu-s-vyuzitim-knihovny-diagrams/
19. Odkazy na Internetu
- Flowchart (Wikipedia)
https://en.wikipedia.org/wiki/Flowchart - DRAKON
https://en.wikipedia.org/wiki/DRAKON - Modeling language
https://en.wikipedia.org/wiki/Modeling_language - Napkin na GitHubu
https://github.com/pinetr2e/napkin - Napkin 0.6.8 na PyPi
https://pypi.org/project/napkin/ - PlantUML (home page)
http://plantuml.sourceforge.net/ - PlantUML (download page)
http://sourceforge.net/projects/plantuml/files/plantuml.jar/download - PlantUML (Language Reference Guide)
http://plantuml.sourceforge.net/PlantUML_Language_Reference_Guide.pdf - Rhizome
https://github.com/ztellman/rhizome - Swagger to UML
https://github.com/nlohmann/swagger_to_uml - pydiagrams
https://github.com/billingtonm/pydiagrams - graphviz(3) – Linux man page
https://linux.die.net/man/3/graphviz - dot(1) – Linux man page
https://linux.die.net/man/1/dot - neato(1) – Linux man page
https://linux.die.net/man/1/neato - twopi(1) – Linux man page
https://linux.die.net/man/1/twopi - circo(1) – Linux man page
https://linux.die.net/man/1/circo - fdp(1) – Linux man page
https://linux.die.net/man/1/fdp - sfdp(1) – Linux man page
https://linux.die.net/man/1/sfdp - Plain-text diagrams take shape in Asciidoctor!
http://asciidoctor.org/news/2014/02/18/plain-text-diagrams-in-asciidoctor/ - Graphviz – Graph Visualization Software
http://www.graphviz.org/ - graphviz (Manual Page)
http://www.root.cz/man/7/graphviz/ - dot (Manual page)
http://www.root.cz/man/1/dot/ - dot (Manual v PDF)
https://graphviz.org/pdf/dot.1.pdf - Ditaa home page
http://ditaa.sourceforge.net/ - Ditaa introduction
http://ditaa.sourceforge.net/#intro - Ditaa usage
http://ditaa.sourceforge.net/#usage - Node, Edge and Graph Attributes
http://www.graphviz.org/doc/info/attrs.html - Graphviz (Wikipedia)
http://en.wikipedia.org/wiki/Graphviz - Unified Modeling Language
https://en.wikipedia.org/wiki/Unified_Modeling_Language - UML basics: The sequence diagram
http://www.ibm.com/developerworks/rational/library/3101.html - UML 2 State Machine Diagrams: An Agile Introduction
http://www.agilemodeling.com/artifacts/stateMachineDiagram.htm - Sequence diagram (Wikipedia)
https://en.wikipedia.org/wiki/Sequence_diagram - UML 2 Sequence Diagrams: An Agile Introduction
http://www.agilemodeling.com/artifacts/sequenceDiagram.htm - A Quick Introduction to UML Sequence Diagrams
http://www.tracemodeler.com/articles/a_quick_introduction_to_uml_sequence_diagrams/ - UML Sequence Diagrams
https://www.uml-diagrams.org/sequence-diagrams.html - Web Sequence Diagrams
https://www.websequencediagrams.com/ - Drawing sequence diagrams “napkin style”
https://modeling-languages.com/drawing-sequence-diagrams-napkin-style/ - Curated list of UML tools – 2020 edition
https://modeling-languages.com/uml-tools/#textual - Flowchart diagrams vs. UML activity diagrams
https://stackoverflow.com/questions/7081215/flowchart-diagrams-vs-uml-activity-diagrams - Kopenograms – Graphical Language for Structured Algorithms
https://kopenogram.org/Assets/Kopenograms_Graphical_Language_for_Structured_Algorithms.pdf - Kopenograms and Their Implementation in BlueJ
https://link.springer.com/chapter/10.1007%2F978–3–319–46535–7_8 - The simplest way to describe your flows
https://code2flow.com/ - Allan Mogensen and his Legacy
http://www.worksimp.com/articles/allan-mogensen.htm - Diagrams: Diagram as Code
https://diagrams.mingrammer.com/ - Diagrams: Guides
https://diagrams.mingrammer.com/docs/guides/diagram - Diagrams: Nodes
https://diagrams.mingrammer.com/docs/nodes/onprem - go-diagrams
https://github.com/blushft/go-diagrams - GoJS
https://gojs.net/latest/index.html - Code visualization: How to turn complex code into diagrams
https://www.lucidchart.com/blog/visualize-code-documentation - Create dependency diagrams from your code
https://docs.microsoft.com/en-us/visualstudio/modeling/create-layer-diagrams-from-your-code?view=vs-2019 - Software Architecture Diagrams as Code
https://shekhargulati.com/2020/04/21/software-architecture-diagrams-as-code/ - Processing spreadsheet data in Go
https://appliedgo.net/spreadsheet/ - Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools