This language allows to define entities and relationships in a concise way. In the following sections, you will learn how to define an entity, its properties and its relationships.
This is the way you can define an User
entity with name
, lastName
and active
properties:
User
name text(50)
lastName text(50)
active bool
As you can see, the properties of an entity must be indented (no matter how many tabs or spaces you use). Properties are defined by writing its name followed by its type, which can include an optional length, similar to SQL.
bool
: a boolean value, like true and false.short
,int
andlong
: integer numeric values, like 42, 73 or 2038.decimal
: real numeric values, like 3.14.text
: string values, like hello world or don't be evil.date
: a date without time, like 01/01/1970.time
: a time representation, like 00:00:00.datetime
: a date and time representation, like 01/01/1970 at 00:00:00.blob
: a binary object, a.k.a. a byte array.identifier
: a special type that represents the identifier of the entity.
Also, it's possible to add some modifier characters after a property's name. Supported modifiers are:
- Optional modifier (
?
): indicates that the property may or may not have a value. - Unique modifier (
!
): indicates that each possible value of the property must be used by at most one of the instances of the entity.
Here you can see an example of defining an optional unique property:
Task
description text(100)
priorityOrder?! int
done bool
Notice that we haven't defined an identifier property for the entities of the previous examples. When we don't define an
identifier for an entity, ERDiagram will generate one for us. By default, that property will be named id
both in
Database Model and Class Model, but it's possible to use different
naming strategies (TODO add link). Another possibility is to explicitly declare the identifier property of the
entity:
User
theUserId identifier
name text(50)
lastName text(50)
active bool
An entity cannot have more that one identifier
property, and this property doesn't accept any
modifier. Also, it doesn't matter if that property is defined at the beginning of the entity or
in another position; it will always be considered the first property of the entity.
Each relationship is defined by its direction and cardinality. It's direction indicates how data can be accessed, while it's cardinality indicates how many elements constitute the relationship. You can know more about cardinalities here.
The direction of the relationship is used to indicate how the data can be accessed from one side of the relationship to the other one.
ERDiagram supports 3 different direction values:
- left-to-right (
->
): indicates that the right entity of the relationship is accessible from the left entity. - right-to-left (
<-
): indicates that the left entity of the relationship is accessible from the right entity. - bidirectional (
<->
): indicates that both entities are accessible from the other.
It's possible to name the members of a relationship using aliases. Imagine you are modelling a Travel
entity which
has two originCity
and destinationCity
attributes, both referencing the City
entity. You could
write Travel -> City
in order to model one relationship from Travel
to City
, but how can you model both relationships at the same time?
The way that ERDiagram solves this is by using aliases:
Travel *-> City originCity
Travel *-> City destinationCity
By doing this, you're naming the right member of your relationship. You can also name both sides of the relationship, just like this:
Employee subordinates *<-> Employee boss
When you don't specify any alias, the name of the entity is used as the name of the member.
This is how you can define the different types of relationships (examples are written using bidirectional relationships):
- one-to-one:
A 1<->1 B
or simplyA <-> B
. - one-to-many:
A 1<->* B
or simplyA <->* B
. - many-to-one:
A *<->1 B
or simplyA *<-> B
. - many-to-many:
A *<->* B
.
As you can see, you can omit the character 1
, as that's the default cardinality.
In addition to 1
and *
characters, it's also possible to use the question mark character (?
)
in order to indicate a 0..1
cardinality. For example, imagine you are modelling a tree structure, where each node has
a relationship to its parent. As root nodes don't have a parent, the relationship should be a many-to-zero-or-one,
which can be written this way:
TreeNode child *<->? TreeNode parent
It's also possible to define a name for the relationship. This is done by writing it between brackets after the relationship definition:
User *<->* User (Friendship)
This is specially useful in many-to-many relationships, in order to define the name of the intermediate table of the Database Model.
Employee *<-> Company
The cardinality of this relationship indicates that each employee belongs to one company, and each company has many employees.
On the other side, its direction indicates that the employee's company can be accessed from the Employee instance, and the company's employees can be accessed from the Company instance.
Employee *->? Employee boss
The cardinality of this relationship indicates that each employee may have a boss or not, and it can have many subordinates.
On the other side, its direction indicates that employee's boss can be accessed from the Employee instance, but it's not possible to get its subordinates.
Employee *<-* Project (EmployeesWorkingOnProjects)
The cardinality of this relationship indicates that each employee can work on many projects, and each project can have many employees working on it.
On the other side, its direction indicates that it's possible to get the employees that are working on a project, but it's not possible to access to an employee's projects.
Finally, the relationship's name is EmployeesWorkingOnProjects, which may be used as a name for the intermediate relationship table used in relational databases.
Even when Uncle Bob doesn't like them, every language needs comments, so ERDiagram couldn't be less 😛
Comments begin with a hash character (#
) followed by any other ones. Only line comments are supported so far.
Here you can see some example comments:
TreeNode
description text(100)
deleted bool # we use logical erase
# The root node doesn't have a parent, that's why
# the "parent" member of the relationship is optional.
TreeNode child *<->? TreeNode parent