mermaid-js / mermaid

Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown
https://mermaid.js.org
MIT License
71.14k stars 6.41k forks source link

Use Barker's Notation and Positional Conventions in ERD mermaids #2751

Open jzabroski opened 2 years ago

jzabroski commented 2 years ago

Is your feature request related to a problem? Please describe. Barker's Notation is a way of arranging an entity relationship diagram such that:

This way of arranging Entity Relationship Diagrams is discussed in David C. Hay's book, Data Model Patterns. It is a powerful way to avoid spaghetti diagrams. The wikipedia page on Barker's notation notes (emphasis mine):

This notation was and still is used by the Oracle CASE modelling tools. It is a variation of the "crows foot" style of data modelling that was favoured by many over the original Chen style of ERD modelling because of its readability and efficient use of drawing space.

David Hay's book does it more justice than other guides to the technique, because he is a true teacher. In particular, pg. 16-18 on Positional Conventions covers the reasoning, noting in the concluding paragraph:

Conventions about the position of entities on a page are less well articulated in the industry (and certainly less frequently followed) than are conventions about syntax. Among those who do follow positional conventions are some who argue that the crows' feet should point down and to the right. While this policy is clearly heresy to your author, it does at least impose the required consistency, and he will grudgingly admit that it does no harm if everyone within the same organization follows it. What is important is that a single convention is used within the organization.

image image

Describe the solution you'd like In the current docs, the primary example of an Entity Relation Diagram mermaids is:

  ```mermaid
  erDiagram
    CUSTOMER ||--o{ ORDER : places
    ORDER ||--|{ LINE-ITEM : contains
    CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
which creates:
  ```mermaid
  erDiagram
    CUSTOMER ||--o{ ORDER : places
    ORDER ||--|{ LINE-ITEM : contains
    CUSTOMER }|..|{ DELIVERY-ADDRESS : uses

I would prefer a solution that, once rendered looks like:

                 .----------------------------------------.
                |    Customer                             |
                '-----------------------------------------'
                     |                               \|/
                   ====                              -|-
                     | places                         | uses
                     o                               -|-
                    /|\                              /|\
               .----------------.  .---------------------.
               |  Order         |  | Delivery-Address    |
               `----------------`  '---------------------'

If you notice, the idea is that rather than create arcs in the relationships, you simply widen the boxes until the relationships can become a straight line.

Describe alternatives you've considered 20 years of experience suggests this is the best solution. David C. Hay also covers some alternatives in very old whitepapers, but I side with him on the reasoning. I think you're more likely to hurt your brain getting into esoteric discussions that are now over 25 years old, if I write much more.

Currently, I use StarUML for drawings. But it's effectively abandoned. The open source code was last updated in 2014. A fork, WhiteStarUML, exists, but was also abandoned in 2018. StarUML these days continues as licensed, closed source software: https://staruml.io/ StarUML also does not automatically lay things out as I described above, but it is fairly rapid at entering data, and it doesn't take much time to reposition a schema. Still I have always desired better.

Additional context I'd be interested in helping do this, but I have no idea how to do it.

jzabroski commented 2 years ago

@spopida Can I get feedback on this? I saw from the commit history you were the architect of this feature and its current renderer. As I mentioned, I am open to contributing, I just don't have any background in this sort of thing.

spopida commented 2 years ago

@jzabroski Hi - first I apologize for missing your original post in Feb; I guess it slipped under my radar. I'll try to give you some useful tips, although I am rather rusty. You must treat everything I say below with a little caution because of this - I might overlook an important detail, for example...

First, I also had zero experience with this kind of thing - no diagramming experience, no JavaScript - in fact, no programming since 2014, and I am no genius, so I strongly encourage you to give it a go.

That said, the main challenge is that you have limited control on how the graph is laid out because the layout calculation is delegated to a third-party library - dagre, so that will be an important source of info for you.

Also have a read of the main mermaid documentation files, but especially this one.

There first key stage in laying out a diagram is to parse the declarative text of the diagram. This is done using another library - jison. You only really need to understand this if you are changing the syntax of the diagram. I would forget this initially, although when you get something working, you may need to tweak the syntax so that when you declare a diagram, you can say what kind of layout you would like. For now I'll gloss over this, except to say that the output of parsing the jison file is a model defined in the erDb.js file. The syntax of Jison is derived from flex and bison, which in turn are derived from lex and yacc! If / when you need to worry about this, then I found this book very useful.

The real work in terms of laying everything out and drawing the diagram is done in erRenderer.js. The entry point for this is the draw function. This receives the diagram declarative text, calls the parser to convert that into a data model, then it uses graphlib and dagre to layout the graph. There is quite a lot detail involved here to adjust the sizes of boxes and ensure things get drawn in the right order (bearing in mind that some parts of the diagram are deliberately drawn 'on top' of, or overlapping other parts). The only way to get to grips with this is to read the code.

I suppose there are really two aspects of an ER diagram that you need to control in order to achieve your goal. First, you need to be able to control the relative positioning of each entity so that the entities appear in the right places. I would look closely at the configuration possiblilities of dagre here because you may be able to exert enough influence simply by parameterising dagre. Second, you obviously need to be able to adjust the width (and height?) of individual entities. This is definitely possible, because I did it when I added the ability to include attributes inside the entity boxes. However, more recently I have noticed there is a bug here because the title area of an entity that includes attributes is often too narrow. I don't know if this was a bug created by me or added subsequently. But anyway, you can adjust the size of boxes in theory. It could be challenging finding out exactly what size to make each box because the desired size of a box depends on the actual size of other boxes...but they depend on other boxes.

So, I don't know if it will be very easy to achieve your goal, but I do think it is well worth trying. I think it would be a great enhancement!

One final piece of info that might be useful ... I racked my brain for many hours trying to figure out how to elegantly join a crows foot with a box from any angle, without the ends of the crows foot overlapping (or underlapping). The answer I came up with was actually to join two arcs and a centre line to the centre of each entity box, and then to superimpose the opaque entity box on top, thus 'cutting' off what would otherwise look a bit like a leaf, and turning it into a crows foot intersecting with the edge of the box perfectly. This was a eureka moment for me in making it work nicely, but it is something you will have to alter to achieve your goal, otherwise every incoming relationship line will always orient towards the centre of the entity.

So, I'll leave it there for now ... I hope that gets you started, and I'll try to help if necessary.

jzabroski commented 2 years ago

The answer I came up with was actually to join two arcs and a centre line to the centre of each entity box, and then to superimpose the opaque entity box on top, thus 'cutting' off what would otherwise look a bit like a leaf, and turning it into a crows foot intersecting with the edge of the box perfectly. This was a eureka moment for me in making it work nicely, but it is something you will have to alter to achieve your goal, otherwise every incoming relationship line will always orient towards the centre of the entity.

Good point. I will try a golden ratio for partitioning where along one rectangle's surface to attach the crow's feet to, maybe that will work.