Skip to main content

Association (UML)

An Association between entities can be illustrated in UML with a solid line connecting the entities that are related.

Figure 1 Company is associated with Employee

Associations in UML are used to depict relationships and their multiplicity. Additional information, such as the Navigability or a Qualifier or the role, can be used to provide further details about the relationship.

Associations illustrated with a solid line are by default bidirectional: Both entities are aware of each other and can query each entity at the opposite end the line. This inherent Bidirectionality can be made obvious by using open arrow-heads at each end of the line:

Figure 2 Company can query its Employee. Employee knows its Company. This is the same as Figure 1

If an Association should not by bidirectional, only one arrow-head should be used at the end of the entity that is queryable, i.e. which is navigable:

Figure 3 Company can query its Address. Address is not aware of its owner (Company)

Multiplicity

A Multiplicity at each end of the solid line indicates the number of associated entities of the given type. The default Multiplicity is 1 in the UML metamodel, so it is good practice to specify the multiplicity, with lower and upper bounds if required. Common forms of multiplicities are

MultiplicityDescriptionExample
1Exactly one.A has exactly one B
* (or n)Any number, including zero.A has none or more B
0..1Zero or one.A has none or one B
1..*One or more.A has one or more B
Figure 4 A can query B, of which it has at least one. It can have unlimited Bs.

Qualified Association

Qualified Associations are used to provide information about an additional Qualifier that is used for querying an association. It is "the UML equivalent of a programming concept variously known as associative array, [...]" [📖UML, p. 74]

Figure 5 Company has multiple Addresses (and at least one). An Address is qualified using AddressType

Example

The following Relationships between Address, Company, Employee, Account should be modelled with UML:

  • A Company has at least one Employee

  • A Company has at least one Address, qualified through some kind of Address-type (e.g. invoicing, headquarter and so on)

  • An Employee has exactly one Company

  • An Employee has exactly one Address

  • An Employee has exactly one Account

  • An Account has exactly one Employee

UML Diagram

The diagram uses the notation introduced above. The role owner was added to the association Account -> Employee to provide a deeper semantic context of the relationship.

Figure 6

Implementation

The implementation is rather naive as it forgoes possible infrastructure requirements (as in Company's in-memory storage of the Employees). Of interest is the implementation of the Qualified Association of the Company's multiple Addresses: Here, a Company has a Usage Dependency with AddressType to realize the qualifier.

AddressType.php
enum AddressType: string
{
case SHIPPING = "shipping";
case INVOICE = "invoice";
case MARKETING = "marketing";
case HEADQUARTER = "hq";
}
Address.php
class Address
{
private string $street;
private string $city;
private string $country;
}
Account.php
class Account
{
private Employee $owner;
private Money $balance;
}
Employee.php
class Employee
{
private Account $account;
private Company $company;
private Address $address;
}
Company.php
class Employee
{
private array $addresses;
private array $employees;

public function getAddress(AddressType $addressType): ?Address
{
return $this->addresses[$addressType->value] ?? null:
}
}

Can Address-instances be shared?

Company and Employee have an association with Address, and therefor both of them are associated with "a same type", but do they share the same object instances of Address? The diagram is representing a class diagram, and the association does not provide further details about the nature of their relationship. Thus, they do not necessarily reference the same instance of Address at first, just that they are associated with instances of this type. It makes no prediction if those instances are the same. This would rather be an implementation detail of the target system. Here's how implementing it differently can have a severe impact to the system's data consistency, and where further notation with the help of UML can clarify its intent.

Referencing the same Address as an Entity

If Company and Employee reference the same Entity, both point to one and the same uniquely identifiable instance of Address. If Company changes its address, e.g. the street-name changes, those changes would be affecting the Employee referencing the same Address-(id)entity: The name of the street where the Employee lives changes, too. This is often referred to as Aliasing Bug.

Referencing the same Address as a Value Object

If Company and Employee reference the same Value Object, both reference an immutable object: If Company changes its address, e.g. the street-name changes, a new Value Object is created and replaces the previous Address-Object of Company. The Employee's place of residence is not affected by this change, since it still holds the reference to the original Value Object.

Address as Value Obect

class Address
{
public function setStreet(string $street): Address
{
return new Address(
$street,
$this->city,
$this->country
);
}
}

class Employee
{
public function setAddress(Address $adress)
{
$this->address = $address;
}
}

class Company
{
public function setAddress(AddressType $addressType, Address $adress)
{
$this->addresses[$addressType->value] = $address;
}
}


class AddressAsValueObjectTest extends TestCase
{
public function testForReference()
{
$type = AddressType::SHIPPING;
$address = new Address("street", "city", "country");

$company->setAddress($address, $type);
$employee->setAddress($address);

$this->assertSame($company->getAddress($type), $employee->getAddress());

$company->setAddress($address->setStreet("moved to 23"), $type);

$this->assertNotSame($company->getAddress($type), $employee->getAddress());
}
}

see also