Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

New Diagram: Architecture #5452

Draft
wants to merge 43 commits into
base: develop
Choose a base branch
from

Conversation

NicolasNewman
Copy link
Contributor

@NicolasNewman NicolasNewman commented Apr 10, 2024

📑 Summary

Architecture diagrams allows users to show relations between services

Resolves #5367

📏 Design Decisions

Parser

Terminology

  • Service: a node within a diagram. Represents services such as from AWS, Azure, Docker, etc
  • Group: a collection of related services (similar in presentation to subgraphs for other diagrams). This could be a VPC on AWS, different APIs which talk to one another, etc

Syntax

Currently, the syntax is defined as:

  • Services are defined as service {id}({icon})[{title}] (in {group_name})?
  • Groups are defined as group {id}[{title}] (in {group_id})?
  • Edges are defined as {id_1} ( ( )?(L|R|T|B)--(L|R|T|B)( ) )? {id_2}

Once I transition from jison to Langium, I plan to add the following extensions:

  • Groups: group {id}({icon})[{title}] (in {group_id})?
    • Allow icons to be used for group names
  • Edges: {id_1} ( ( )?(L|R|T|B)-([{label}])-(L|R|T|B)( ) )? {id_2}
    • Allow edges to have a label
    • Allow forked edges traversing the XY axis (see Layout for more info). I've yet to decide on the syntax for this
  • Services: service {id}( ({icon}) | ("{text}") )[{title}] (in {group_name})?
    • Allow text to be used for a service instead of an icon

Additionally, the syntax for groups (and potentially forked edges?) will be modified to better follow a declare then organize approach

Layout

  • The positions of services are determined by specifying a required direction for an edge between the two
  • Edges which traverse the X & Y axis (LT, RB, etc) are connected by two perpendicular lines
  • Each service can only have one edge per side
  • Internally, a spatial map of each service is maintained which specifies an (x,y) coordinate of where each service is in relation to one another ([0,1] is above [0,0]). This forces the layout to have a consistent grid like pattern

Comments

As mentioned in the issue, the LRTB syntax goes against the ethos of Mermaid. Additionally, while the spatial map creates very neatly organized layouts, it prevents users from having multiple edges going out of one side. Part of the reason I began designing it this way was to avoid this diagram type from essentially being a flowchart with icons.

Going forward, there's a few options that can fix this problem:

  1. Make the LRTB syntax optional. If they aren't ever specified, don't use the spatial map for generating the constraints for fcose and let it figure things out on its own.
  2. Keep the LRTB syntax required and instead add svg icon support to flowcharts. Ultimately the two diagram types are very similar in how they represent nodes and edges. My main reasoning for adding this new diagram type other then SVG support was for more precise control over node positioning like with block diagrams. I've already been using flowcharts to quickly mock up architecture diagrams before making something nicer in softwares like Lucidcharts.

No matter which option we choose, the LRTB syntax should be extended to:

  1. Allow forked edges. This means one edge out the X/Y axis would split into more edges that would expand outwards in the opposite axis. This would solve the issue of being limited to only a max of 4 edges per service.

SVG Icons

SVG icons are currently created and accessed through helper functions inside of rendering-util/svgRegister.ts. A global object stores the mapping of icon names to IconResolvers defined as

type IconResolver = (
  parent: Selection<SVGGElement, unknown, Element | null, unknown>,
  width?: number
) => Selection<SVGGElement, unknown, Element | null, unknown>;

IconResolvers can easily be created through the helper function createIcon. This function takes the SVG code as a string along with the original resolution. It returns a CB function taking the element to inject the SVG into and optionally a different size to scale it by.

const createIcon = (icon: string, originalSize: number): IconResolver => {
  return (
    parent: Selection<SVGGElement, unknown, Element | null, unknown>,
    size: number = originalSize
  ) => {
    parent.html(`<g style="transform: scale(${size / originalSize})">${icon}</g>`);
    return parent;
  };
};

A few generic icons I've created are located in rendering-utils/svg/. Additional icons can be added through the new iconLibraries config option. The types in svgRegister.ts are now exported from mermaid.ts, allowing developers to create their own icon library packages.

// Example icon

// rendering-util/svg/database.ts
export default createIcon(
  `<g>
      <rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
     ...
</g>`,
  80
);

// rendering-util/svg/index.ts
const defaultIconLibrary: IconLibrary = {
  database: database,
  // ...
};

// index.html
mermaid.initialize({
  theme: 'base',
  startOnLoad: true,
  // This is just an example. It is already loaded by default within MermaidAPI.ts
  iconLibraries: [defaultIconLibrary],
  // ...
});

Alternatives

From conversations in the linked issue, ZenUML already handles SVG icons on their end. Upon furthur inspection, they rely on declaring an svg module which essentially resolves the imported SVG as a string. Since the end result of both of these approaches are the same (having the SVG code as a string), personally I think it's debatable if we want to go through with this extra step. The main con I foresee is that it'll add more work for people who want to make their own icon library package as they will need to setup the declarations themselves.

On a side note, it looks like ZenUML directly added the official AWS icons to their repo here. IANAL but the terms Amazon states are:

Customers and partners are permitted by AWS to use the resources below to create architecture diagrams

This may mean we can make official icon packs in seperate packages, or have them be lazy loaded as needed. Ultimately I'll leave that decision up to you as that isn't my area of specialties.

Todo

  • Unit tests
  • Documentation
  • More generic default icons? If anyone has ideas let me know and I can put something together in Illustrator
  • Convert parser from jison to Langium
  • Implemented parser extensions
    • Edge labels
    • Icons as group names
    • Text instead of service icon
  • Forked edges
  • Edges between groups
  • More control over styling

📋 Tasks

Make sure you

Copy link

netlify bot commented Apr 10, 2024

Deploy Preview for mermaid-js ready!

Name Link
🔨 Latest commit b09dc5d
🔍 Latest deploy log https://app.netlify.com/sites/mermaid-js/deploys/663e38e34c436800087acb67
😎 Deploy Preview https://deploy-preview-5452--mermaid-js.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

codecov bot commented Apr 10, 2024

Codecov Report

Attention: Patch coverage is 0.35300% with 1976 lines in your changes are missing coverage. Please review.

Project coverage is 5.48%. Comparing base (4f64242) to head (b09dc5d).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           develop   #5452      +/-   ##
==========================================
- Coverage     5.72%   5.48%   -0.25%     
==========================================
  Files          278     298      +20     
  Lines        42046   44015    +1969     
  Branches       490     536      +46     
==========================================
+ Hits          2409    2416       +7     
- Misses       39637   41599    +1962     
Flag Coverage Δ
unit 5.48% <0.35%> (-0.25%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
packages/mermaid/src/themes/theme-default.js 94.47% <100.00%> (+0.09%) ⬆️
.build/jsonSchema.ts 0.00% <0.00%> (ø)
.../mermaid/scripts/create-types-from-json-schema.mts 0.00% <0.00%> (ø)
packages/mermaid/src/docs/.vitepress/config.ts 0.00% <0.00%> (ø)
packages/parser/src/language/architecture/index.ts 0.00% <0.00%> (ø)
...s/mermaid/src/diagram-api/diagram-orchestration.ts 0.00% <0.00%> (ø)
packages/parser/src/language/index.ts 0.00% <0.00%> (ø)
packages/mermaid/src/mermaid.ts 0.00% <0.00%> (ø)
packages/mermaid/src/themes/theme-base.js 3.74% <0.00%> (-0.08%) ⬇️
packages/mermaid/src/themes/theme-dark.js 2.69% <0.00%> (-0.06%) ⬇️
... and 22 more

... and 1 file with indirect coverage changes

@NicolasNewman
Copy link
Contributor Author

@sidharthv96 Continuing our conversation from the issue, I commented on SVG loading and my thought process behind how the layout is handled. Let me know what your thoughts are.

Additionally, an example of what forked edges would look like once implemented:
image

Regarding multiple edges going into one side of a service, it's possible but I'll need to spend some time figuring out the cleanest way to implement that functionality. Ultimately I was planning on using forked edges to handle that scenario but after looking at your example the arrows can't be as neatly represented the same way through forked edges.
image

@NicolasNewman
Copy link
Contributor Author

NicolasNewman commented Apr 18, 2024

Here is where the renderer currently stands.

From a stylistic standpoint I'm considering:

  1. shortening the edges so they don't overlap with the service label.
  2. removing the rotation of the labels for Y/XY edges (or adding a config option)

Simple diagram with an icon in the group name

image

Groups within groups

image

90 degree edges

image

Edge Arrows

image

Edge Labels

image

image

@hemeroc
Copy link

hemeroc commented Apr 19, 2024

Regarding the icons it would be awesome if it's possible to use any of those:

Maybe they could be shipped as icon library? From a license PoV this should be okey and would immediately add many icons.

More generic default icons? If anyone has ideas let me know and I can put something together in Illustrator

Or take some of the good ones as default so you don't need to build them yourself.

@sidharthv96
Copy link
Member

Solid progress @NicolasNewman 👍

Minor note regarding readability of labels.

I feel the label that goes from top to bottom and right to left is more readable.

image
image image

@NicolasNewman
Copy link
Contributor Author

Update on diagram syntax changes:

Edges between groups

One limitation of fcose is the 3 constraint parameters don't factor in groups. Since this prevented me from directly having edges between group nodes, I had to implement a work-around by extended the parser to signal that an edge endpoint should connect to the group border and not the node itself. In practice, the implementation looks like:
image

architecture
        group left_group(cloud)[Left]
        group right_group(cloud)[Right]
        group top_group(cloud)[Top]
        group bottom_group(cloud)[Bottom]
        group center_group(cloud)[Center]

        service left_disk(disk)[Disk] in left_group
        service right_disk(disk)[Disk] in right_group
        service top_disk(disk)[Disk] in top_group
        service bottom_disk(disk)[Disk] in bottom_group
        service center_disk(disk)[Disk] in center_group

        left_disk{group} (R--L) center_disk{group}
        right_disk{group} (L--R) center_disk{group}
        top_disk{group} (B--T) center_disk{group}
        bottom_disk{group} (T--B) center_disk{group}

Note that the {group} modifier doesn't have to be applied to both ends.

Forked edges

I decided to handle this problem with the concept of a junction node. Internally, its positioned and rendered almost identically to service nodes with the exception that its entirely transparent with no label associated.

image

architecture
        service left_disk(disk)[Disk]
        service top_disk(disk)[Disk]
        service bottom_disk(disk)[Disk]
        service top_gateway(internet)[Gateway]
        service bottom_gateway(internet)[Gateway]
        junction juncC
        junction juncR

        left_disk R--L juncC
        top_disk B--T juncC
        bottom_disk T--B juncC
        juncC R--L juncR
        top_gateway B--T juncR
        bottom_gateway T--B juncR

Additional example

image

architecture
        group left
        group right
        service left_disk(disk)[Disk] in left
        service top_disk(disk)[Disk] in left
        service bottom_disk(disk)[Disk] in left
        service top_gateway(internet)[Gateway] in right
        service bottom_gateway(internet)[Gateway] in right
        junction juncC in left
        junction juncR in right

        left_disk R--L juncC
        top_disk B--T juncC
        bottom_disk T--B juncC


        top_gateway (B--T juncR
        bottom_gateway (T--B juncR

        juncC{group} R--L) juncR{group}

Remaining Goals

As of now, the primary features that remain are:

  1. Controlling arrow orientation for junction edges
  2. Flexible styling
  3. Unit tests
  4. Finalize the docs

Lingering Questions

@sidharthv96

  1. Should a mode be added which handles the positioning on its own? I've mentioned my opinion on that earlier where I feel that it would then be very similar to flowcharts. I personally think it would make more sense to update flowcharts to have the same svg icon support.
  2. How to handle the inclusion of AWS/Azure/etc icons. Should it be included with Mermaid and dynamically loaded? Should it be split off into its own package? If so, who will host it? (that could also serve as a template for anyone who would like to make their own icon library)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proposal: Cloud Architecture Diagram
3 participants