Aspects vs. Inheritance
The :
-based syntax option introduced in the CDL reference looks very much like (multiple) inheritance and in fact has very much the same effects. Yet, it is not based on inheritance but on mixins, which are more powerful and also avoid common problems like the infamous diamond shapes in type derivations.
Sample Model Using Aspects
When combined with persistence mapping there are a few things to note, that goes down to which strategy to choose to map inheritance to, for example, relational models. Let's investigate this briefly based on examples from some hypothetical IoT scenarios.
aspect Thing {
key ID : UUID;
title : String(100);
description : String;
}
entity Vehicle : Thing {
wheels : Integer @description: 'Number of Wheels';
manufacturer : String(255);
}
entity Bike : Vehicle {
}
entity Car : Vehicle {
engine : String(255);
}
entity Asset : Thing {
location : String;
}
Mapping to SQL
If that would be mapped to a SQL database, using CDS's canonical mapping, that would result in the equivalent of:
CREATE TABLE Vehicle (
ID NVARCHAR(36) NOT NULL,
title NVARCHAR(100),
description NVARCHAR(5000),
wheels INTEGER,
manufacturer NVARCHAR(255),
PRIMARY KEY(ID)
);
CREATE TABLE Bike (
ID NVARCHAR(36) NOT NULL,
title NVARCHAR(100),
description NVARCHAR(5000),
wheels INTEGER,
manufacturer NVARCHAR(255),
PRIMARY KEY(ID)
);
CREATE TABLE Car (
ID NVARCHAR(36) NOT NULL,
title NVARCHAR(100),
description NVARCHAR(5000),
wheels INTEGER,
manufacturer NVARCHAR(255),
engine NVARCHAR(255),
PRIMARY KEY(ID)
);
CREATE TABLE Asset (
ID NVARCHAR(36) NOT NULL,
title NVARCHAR(100),
description NVARCHAR(5000),
location NVARCHAR(5000),
PRIMARY KEY(ID)
);
That is: the mixins are identically cloned columns in each (leaf) entity. That would allow to apply common reuse functions defined on Things or Vehicles to all instances of all 'subclasses', for example, the same UIs.
However, if you would want to query all vehicles, you'd need a UNION
across Vehicle
, Bike
, and Car
; if you'd want to query all things, you'd have to add Assets
.
Alternative Mapping
In order to optimize for cross-subclasses queries, you might want to apply a single-table-per-node strategy and could do so as follows:
entity Thing {
key ID : UUID;
title : String(100);
description : String;
}
entity Vehicle {
key thing : Association to Thing;
wheels : Integer @description: 'Number of Wheels';
manufacturer : String(255);
}
entity Bike {
key vehicle : Association to Vehicle;
}
entity Car {
key vehicle : Association to Vehicle;
engine : String(255);
}
entity Asset {
key thing : Association to Thing;
location : String;
}
The corresponding SQL for Vehicle
and Car
would look like:
CREATE TABLE Vehicle (
wheels INTEGER,
manufacturer NVARCHAR(255),
thing_ID NVARCHAR(36) NOT NULL,
PRIMARY KEY(thing_ID)
);
CREATE TABLE Car (
engine NVARCHAR(255),
vehicle_thing_ID NVARCHAR(36) NOT NULL,
PRIMARY KEY(vehicle_thing_ID)
);
For the sake of more inheritance-look-alike you could also do the same as follows:
entity ThingHeader {
key ID : UUID;
title : String(100);
description : String;
}
aspect Thing {
key thing : Association to ThingHeader;
}
entity Vehicle : Thing {
wheels : Integer @description: 'Number of Wheels';
manufacturer : String(255);
}
// …
entity Asset : Thing {
location : String;
}
This would result in the following SQL DDL statements:
CREATE TABLE ThingHeader (
ID NVARCHAR(36) NOT NULL,
title NVARCHAR(100),
description NVARCHAR(5000),
PRIMARY KEY(ID)
);
CREATE TABLE Vehicle (
wheels INTEGER,
manufacturer NVARCHAR(255),
thing_ID NVARCHAR(36) NOT NULL,
PRIMARY KEY(thing_ID)
);
Bottom Line
There is no one-size-fits-all mapping strategy of inheritance graphs to databases. It always depends on what your expected (prevailing) data access characteristics are and which kind of databases you choose (for example, NoSQL vs SQL). Therefore, CDS intentionally refrains from pulling you into such a wrong one-size-fits-all dead end row in particular and from pulling you into inheritance in general. You have to model explicitly what you want.