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