Draw - programming software design diagrams

This module provides an API to program software design diagrams using the Go programming language.

Drawing diagrams by hand, though very intuitive and easy to create are rarely maintained over time. If we want to keep them up to date with our code, we need another way.
Autogenerated diagrams from source code is one way to go about it. However then the problem becomes one of making it informative enough for the inteded audience, ie. it's more difficult to filter out unwanted elements or place them in a logical order depending on the discussion/purpose.
We can also resort to using text formats and process those into diagrams, eg. mscgen or graphviz. These are great tools which bring design diagrams closer to the source code and benefit from the same revision control as other code. The problem of maintaining diagrams however remains, refactoring code doesn't touch the diagrams.

This module uses the benefits of the text base diagram formats by using the Go programming language. Ie. not another format rather it provides an API for programming diagrams. Developers can refer to types directly from the modules they want to describe. Whatever information can be gathered from the source code is used, which allows the programmer to focus on creating a diagram visually suitable for their reader.

Install

    go get github.com/gregoryv/draw/...

API documentation

About

Written by Gregory Vinčić
MIT License


Quick start

Each diagram is a Go type specifically designed to provide an easy and intuitive way of "programming" diagrams. Elements are either fixed strings or taken from the source code by using concrete instances of real types. This allows for refactoring to also update diagrams within a package.
Once you selected which elements to include, position them relative to each other in the diagram. Relative placement has the benefit of adaptive diagrams once you add more methods or fields to your structs. It works for most cases, eliminating manual updates.

Materials() docs.Part interface Material Materials() docs.Door struct Model Materials() docs.Window struct Frontdoor Windows Rooms() docs.House struct Relations are automa- tically rendered Small example diagram
package docs

import (
	"github.com/gregoryv/draw/design"
	"github.com/gregoryv/draw/shape"
)

func ExampleSmallClassDiagram() *design.ClassDiagram {
	var (
		d      = design.NewClassDiagram()
		house  = d.Struct(House{})
		door   = d.Struct(Door{})
		window = d.Struct(Window{})
		part   = d.Interface((*Part)(nil))
		note   = shape.NewNote(`Relations are automa-
tically rendered`)
	)
	d.Place(part).At(20, 20)        // absolute positioning
	d.Place(door).RightOf(part, 70) // optional extra spacing
	d.Place(window).Below(door)
	d.Place(house, note).RightOf(door, 70)

	d.HAlignCenter(house, door, part, note)
	d.SetCaption("Small example diagram")
	return d
}

Once a diagram is done, you can render the SVG in different ways

These pages for instance are generated using the github.com/gregoryv/web package using the Inline() method and looks something like the below code

package docs

import . "github.com/gregoryv/web"

func Example_RenderInlinedDiagram() {
	page := NewPage(
		Html(
			Body(
				H1("An example"),
				ExampleSmallClassDiagram().Inline(),
			),
		),
	)
	page.SaveAs("mypage.html")
}

Styling is currently provided by draw.ClassAttributes and can be changed to some degree. For now font size and family should not be changed as size of shapes will not adapt to the styling values. The idea is however that the default styling should be left alone.

There are more design diagram types available, take a look below. If you are missing something that could benefit the community, please let me know.

Diagrams

Class

In class diagrams the author wants to convey design relations between various entities. However the relations and most of the element naming can be generated from the source code. The author should add what is needed for a clear picture, ie. selecting entities to show and position them in a perceptible manner.

Direction() Height() Position() SetClass() SetX() SetY() Width() WriteSVG() shape.Shape interface Title Fields Methods Font Pad Edge() HideFields() HideMethod() HideMethods() SetFont() SetTextPad() shape.Record struct Point Radius Above() Below() Distance() Edge() Equals() LeftOf() RightOf() String() XY() XYfloat64() shape.Circle struct Edge() shape.Diamond struct shape.Triangle struct Spacing Above() At() Below() LeftOf() Move() RightOf() shape.Adjuster struct Height LineHeight SetScale() TextWidth() draw.Font struct Font TextPad Pad Spacing SetOutput() Write() draw.Style struct design.Relation struct SVG Aligner Style AdaptSize() Append() Caption() Height() Inline() Link() LinkAll() Note() Place() PlaceGrid() Prepend() SaveAs() SetCaption() SetHeight() SetScale() SetSize() SetWidth() String() TextWidth() Width() WriteSVG() design.Diagram struct HAlignBottom() HAlignCenter() HAlignTop() VAlignCenter() VAlignLeft() VAlignRight() shape.Aligner struct Diagram ColWidth VMargin Add() AddColumns() AddInterface() AddStruct() ClearLinks() Group() Return() Skip() design.SequenceDiagram struct Diagram HideRealizations() Interface() Slice() Struct() design.ClassDiagram struct Figure 1. Class diagram of design and design.shape packages
Source: class_example.go

Records describe each entity using package name and type. Methods and fields are shown only by name if visible. Details such as arguments and return values are left to the API documentation. Relations are automatically rendererd between entities if there is one.

Activity

Activity diagrams start in one position and show states and activities transitioning from one stated to another ending in one or more exits.

\n Commited push Build complete run git hook run tests Verified ok \n\n \n\n fails
package docs

import (
	"github.com/gregoryv/draw/design"
)

func ExampleActivityDiagram() *design.ActivityDiagram {
	d := design.NewActivityDiagram()

	d.Start().At(80, 20)
	d.Trans("push", "Commited")
	d.Trans("run git hook", "Build complete")

	test := d.Decide("run tests")
	d.Trans("ok", "Verified")
	d.Trans("", "EXIT")

	d.Or(test)
	d.TransRight("fails", "EXIT")
	return d
}

Sequence

Sequence diagrams are ment to describe a sequence of events, specifically calling methods or remote API calls. I've tried to emphasize the horizontal arrows over vertical lines and keep visual effects to a minimum. For now there is only one arrow variation. I found that embedding information by the subtle head variations and arrow line styling is hard to read.

app.Client app.Server sql.DB aws.SQS Private RPC using Gob encoding connect() SELECT Rows Transform to view model Send HTML Publish event
package docs

import (
	"database/sql"

	"github.com/gregoryv/draw/design"
	"github.com/gregoryv/draw/internal/app"
)

func ExampleSequenceDiagram() *design.SequenceDiagram {
	var (
		d   = design.NewSequenceDiagram()
		cli = d.AddStruct(app.Client{})
		srv = d.AddStruct(app.Server{})
		db  = d.AddStruct(sql.DB{})
		sqs = d.Add("aws.SQS")
	)
	d.Group(srv, sqs, "Private RPC using Gob encoding", "red")
	d.Link(cli, srv, "connect()")
	d.Link(srv, db, "SELECT").Class = "highlight"
	d.Link(db, srv, "Rows")
	d.Link(srv, srv, "Transform to view model").Class = "highlight"
	d.Link(srv, cli, "Send HTML")
	d.Link(srv, sqs, "Publish event")
	return d
}

Gantt chart

2019 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 01 December 02 03 04 05 06 07 08 09 10 Develop Release Vacation Figure 1. Project estimated delivery

Generic

Record Label \n Notes support multilines \n \n \n\n database Rect Waiting for go routine service

Shapes

Actor Arrow Circle \n Component Component Cylinder Database database Diamond Dot \n ExitDot \n\n Internet \n Internet Label label-text Line Note This describes something... Record fields methods record Rect a rectangle State active Triangle

Changelog

## [0.32.0] 2023-10-08

- Enable scaling of diagrams and shapes, e.g. Diagram.SetScale(1.5)
- Shapes no longer implement fmt.Stringer
- Hide field Note.Text

## [0.31.0] 2023-10-08

- Hide field Label.Text
- Remove aliased types and defaults in package shape
- Hide field Diagram.Caption

## [0.30.0] 2023-08-02

- Add shape.Labeled to simplify labeling any shape
- Group.WriteSVG writes all grouped shapes

## [0.29.0] 2023-08-02

- Add draw.DefaultFontFamily used in default classes
- Add shape.Hidden for hiding shapes
- Add shape.Anchor for linking shapes

## [0.28.0] 2023-08-25

- Add shape.Group and shape.Container
- shape.Card default width is 310px
- shape.Card takes optional note and text

## [0.27.0] 2023-08-20

- Add package draw/goviz for generating frame sequence diagrams

## [0.26.1] 2023-02-20

- Update dependencies

## [0.26.0] 2022-12-28

- Improve default link label positions
- Add shap.Card
- Consider a slice struct field of any type to be an
  aggregate. Ie. before []*Something was considered an aggregate
  whereas []Something was not. Now both are considered aggregates.
- Hide X, Y fields of shapes, use SetX, SetY and Position

## [0.25.0] 2022-04-21

- Draw relations of embedded interface fields

## [0.24.0] 2021-12-05

- Add Diagram.Note helper method
- Label shape supports linebreaks
- Add shapes Process, Store

## [0.23.0] 2021-09-04

- NewStyle does not take writer, defaults to ioutil.Discard.
  Use Style.SetOutput
- Add SequenceDiagram.Return method for drawing dashed arrows

## [0.22.1] 2021-06-14

- Fix arrows in sequence diagram, bug introduced in v0.22.0

## [0.22.0] 2021-06-10

- Update dependencies
- Remove func draw.Inline(), moved to design as private
- Remove type Arrow, use Line

## [0.21.2] 2021-04-21
## [0.21.1] 2021-04-21
## [0.21.0] 2021-04-21

- Line and arrow Height and Width methods consider head and tail shapes
- Diamond.Position returns top left
- Triangle points upwards and Position returns top left
- Replace ActivityDiagram.If with Or when switching decision
- Replace ActivityDiagram.Then with Trans and add TransRight methods
- Activity diagram default spacing increased from 40 to 60

## [0.20.0] 2021-04-16

- Add Label.SetHref and Component.SetHref
- Rename type xy.Position to xy.Point
- Show relations between slices and structs in ClassDiagram
- Add SequenceDiagram.Skip method for adding a dashed spacing
- Replace draw.TagWriter with nexus.Printer

## [0.19.0] 2021-04-05

- Add labeled hexagon shape
- Fix label alignment
- Adjuster respects diagram style.Spacing
- NewDiagram returns a pointer to diagram
- Diagram variations embed reference to diagram

## [0.18.0] 2021-01-23

- Include slice methods in VRecord
- Remove double padding for record titles
- Improve label alignment in Diagram.Link method

## [0.17.0] 2020-12-16

- Fix Class diagram to show pointer receiver methods
- Add SequenceDiagram.Group method for grouping columns with a labeled text area

## [0.16.0] 2020-10-30

- Diagrams Stringer implementations no longer inline css, use Inline method
- Add VRecord.Slice method for slice composition and aggregates

## [0.15.1] 2020-10-27

- Fix missing relations in inlined class diagrams

## [0.15.0] 2020-10-27

- Diagrams implement stringer interface
- AdaptSize makes width and height +1 pixel
- Add Inline method to diagrams
- Add type ClassAttributes with CSS method for easy inlining in HTML

## [0.14.0] 2020-10-06

- Fix VRecord to show pointer methods if pointer to struct given

## [0.13.0] 2020-09-12

- Add shape.Internet
- Add default spacing to shape.Adjuster
- Move type shape.Style to package draw
- Rename NewSvg to NewSVG

## [0.12.0] 2020-09-09
### Changed

- Moved draw/shape/design package to draw/design
- Renamed type Svg to SVG
- Renamed type SvgWriter to SVGWriter
- Renamed WriteSvg to WriteSVG

## [0.11.0] 2020-03-17
### Changed

- Generic design.NewVRecord for both structs and interfaces

### Removed

- Specific methods for creating struct and interface methods.

## [0.10.0] 2020-02-03
### Added

- Ganttchart weekly view
- Cylinder and database shapes
- Func shape.SetClass for setting class of many shapes

### Fixed

- Component and rect labels use class + "-title"

## [0.9.0] 2020-01-02
### Added

- ActivityDiagram helper methods, e.g Start, Then, If and Exit
- Mark current day by default in gantt chart

### Changed

- Renamed Direction constants LR and RL to RightDir and LeftDir
- NewGanttChart constructor uses date.String
- Exposed TagWriter
- shape.Svg moved to draw.Svg

### Fixed

- Label positioned correctly in activity diagram for vertical arrows

## [0.8.0] 2019-12-18
### Added

- Aggregate relation when pointer to specific type is used
- Gantt chart
- Actor shape

### Changed

- Label is optional when creating links with func Link
- Hide Height, Width attributes in Diagram
- shape.NewDot defaults to radius 10

## [0.7.0] - 2019-12-15
### Changed

- Namespace from gregoryv/go-design to gregoryv/draw. Fix your imports
  import "github.com/gregoryv/draw/shape/design"

## [0.6.0] - 2019-12-15
### Added

- Activity diagram examples
- Labeled links in plain diagrams
- State shape
- PlaceGrid to quickly place many shapes into a grid layout

## [0.5.0] - 2019-12-13
### Added

- Link method to generic diagram
- Rect, Dot, ExitDot and Component shapes

### Changed

- Label and circle has an edge
- Circle shape is stroked by default

## [0.4.0] - 2019-12-04
### Fixed

- Caption in sequence diagrams

### Changed

- Default color of records is white with pale lines


## [0.3.0] - 2019-10-12
### Fixed

- Arrows point to edge irrelevant of angle

### Added

- Diagrams have optional bottom centered caption
- Class diagram shows composition using diamond for tail
- Note shape with multiline support

### Changed

- Font size decoupled from class attributes
- Arrow for realizing interface is dashed
- Label and circle position is top left corner
- Hide methods realized by visible interfaces and structs


## [0.2.0] - 2019-10-03
### Changed

- All shapes can have a class
- Arrows in class diagrams attach to edge and point to center

### Fixed

- RightOf and LeftOf adjustments sets matching y position
- Use class for non self arrows

### Removed

- SvgWriterShape interface


## [0.1.0] - 2019-09-21
### Added

- Sequence diagram
- Class diagram
- Record shape with fields and methods
- Arrow, line and label shapes

License

MIT License

Copyright (c) 2019 Gregory Vinčić

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.