Simple Domain Modeling Language
Language Primer
Table of Contents
This short primer introduces the Simple Domain Modeling Language (SDML) and how you can apply it to model your business domain. This is not a replacement for the main SDML documentation but instead a quick tour of the language, it’s features and application. This introduction will focus only on the SDML surface syntax, the textual representation that most people will interact with directly, whereas the main documentation also covers the semantics of the language and mappings between SDML and other key models. The primer also covers the core concepts that almost everyone will need to know, with pointers to the more advanced topics in the main documentation (notes marked with 🎛).
So, let’s start…
1. Why do I want a Domain Model?
Modeling in software development has had a difficult history, in some technical domains it is considered an essential tool. However, in many others, and especially with the emergence of agile development practices, it is considered an unnecessary overhead. George Box (statistician) famously wrote, “All models are wrong, some are useful.”, and later expanded it to
All models are approximations. Assumptions, whether implied or clearly stated, are never exactly true. All models are wrong, but some models are useful. So the question you need to ask is not “Is the model true?” (it never is) but “Is the model good enough for this particular application?” — George E. P. Box, 1976.
So why would we build a domain model? Well, from the introduction to the guidance topic Domain Modeling in the Scaled Agile Framework we get the following.
Domain modeling is one of the key models used in software engineering: if you only model one thing in Agile, model the domain. A relatively small domain-modeling effort is a great tool for controlling the complexity of the system under development. It may help in resolving countless ambiguities in both the requirements and the design intent. Domain modeling simply reflects our understanding of real-world entities and their relationships and responsibilities that cover the problem domain.
It is the essential characteristic of the domain model that it must reflect real-world entities, the tangible and intangible artifacts that are the common vocabulary of the domain. For example, in a Hotel domain model we may introduce Hotel, Room, Customer, and Booking as entities whereas a car rental domain model would have Location, Vehicle, Customer, and Booking. These entities should not be abstracted more than necessary, while the term Location in the rental model could be considered as a generic form for Hotel it is not, they are not the same and the choice of Location was not for genericity or abstraction but because that is the term that the rental car industry uses.
As such, a domain model is an essential communication tool, not only between the business and technology sides of an organization but also between the business and it’s customers and partners. Some industry standards for example use a common domain model to help facilitate the understanding of the standards themselves and how they apply between parties.
1.1. Where do I start?
One answer to that question is whether existing domain models already exist for your industry, often from standards organizations. If not then you would want to get a group of subject-matter experts (SMEs) together to talk through the key components of your business and how they relate to each other. You want to identify first those real-world entities that comprise the core of the business, and start with the ones your customers would be able to articulate – only because that simple test helps to identify the truly essential elements.
Include stakeholders across your business and with different roles and perspectives. This is not a time to be parochial, to codify existing organizational structures or silos. Ask yourself, “If we were to fundamentally reorganize our internal structure, would these entities have to change?” and if the answer is yes you almost certainly have something wrong. The danger is that your modeling effort falls foul of Conway’s law:
Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure. — Melvin E. Conway, 1967.
Another good place to start is to ask participants to describe the business in only 1 or 2 sentences. In the following we have highlighted obvious candidate entities in bold, and phrases that may hide entities in italics.
- Retail: Customers go to a store to purchase items, they may have a specific item in mind or expect to find choices they can choose between. Depending on the kind of item this choice may be variations (in size or color for example) of the same item or the same kind of item from different manufacturers.
- Hotel: A customer books a room according to their choice of size, bed type, or other criteria, for specific dates at a particular property. A customer may make certain kinds of purchase against their room which are then added to the final bill on completion of their stay.
- Long-Haul Logistics: The business comprises matching available transportation capacity on one or more routes to deliver a customer’s load from source to destination within some time constraint. A load may be one or more standard palettes, or one or more standard containers. This results in one or more delivery contracts.
The next step may be to ask questions such as “does the (candidate) entity X have a natural and unique identifier?”, “what properties does the (candidate) entity X have?”, or “if we don’t think X is an entity, what is it?”. All of these help to elaborate an initial framework that you can hang more detail on as you spend more time with more stakeholders. Whether you capture this in a structured form such as SDML, a graphical form such as a Visio diagram or data model diagram, or even natural language notes, you now have a domain model.
The first of these questions is actually the most important as we can define an entity according to the following rules.
An Entity is:
- the name of a real-world concept,
- usually being a noun in the language used to describe the domain,
- that has an independent lifecycle (is not dependent on the lifecycle of another),
- and has a natural and unique identifier.
2. What is SDML?
SDML is a textual language that allows you to describe a domain model in a relatively simple structured manner. We say relatively because the language allows you to build models iteratively, capturing the essentials first and elaborating on them over time. It is common for domain models to be the output of workshop sessions and so the ability to capture a valid model in real-time even if it lacks detail is a useful feature. With this in mind we will elaborate on the module definition in listing 1 during this primer.
1: module retail is end
This is, obviously, not a very informative definition of the retail domain; however, it is a valid model and a good enough place to start. But, as you’ve been tasked with being the scribe at a workshop outlining the domain model for a retail business you’re on track so far. We will see more features in the following sections.
One question you may have, why a textual language, why not a fancy graphical one, or an even fancier web-based graphical one? The reasons are purely pragmatic; 1. simpler to build, 2. now you have a persistent format, 3. easier to agree details, 4. not everyone is a visual thinker, and 5. it’s usually easier to define a visual representation of textual form than a textual representation of a visual one. One (and not necessarily the most urgent) of the problems that has hampered the adoption of the UML language for example is that there is no standard representation and so all tools have to write import/export capabilities to share models leading to errors or inconsistencies. SDML has a native representation, the surface syntax (it actually has two, more on that in a moment) and all tool builders can rely on being able to read and write that syntax.
What is the other representation? The textual language, shown in listing 1 has a direct mapping to, and can be considered an alternate syntax for, a particular RDF/OWL ontology [need link]. This means that the semantics of SDML are then described by the OWL ontology, then OWL, and then RDF. Understanding this is not necessary to using or understanding the SDML syntax.
The SDML project, which is more than just the language definition, provides editor integration for authoring and tools for drawing and documentation. It also produces this primer and the main documentation.
2.1. What it isn’t
- SDML is not, as we said above, a graphical language. It relies on a defined mapping to the UML language that allows the core elements of the textual language to be represented visually in UML.
- SDML is not a product, it’s freely available and open source.
- SDML is not a standard, there’s no committee managing changes. Feel free to file an enhancement request, or if you have a change a pull request, because…
- SDML is not done, we expect evolution as we see interesting opportunities to help you, and you see things we missed.
3. What can I do with it?
The value of a domain model is in being able to describe the domain in an unambiguous way, so that it can become a shared vocabulary which in turn requires some rigor. One of the goals of SDML is to allow you to apply rigor as needed and elaborate the model over time. Some modeling tools, techniques, and processes, seem to require you to front-load the details and it can be hard to get past the blank page problem as it feels like you need to do so much just to start.
This section will show this elaboration process in action as we build the models for our example domains: Retail, Hospitality, and Logistics.
3.1. Capture Entities
Let’s add the entities from the previous natural language description to our module. In each of the following sections
we will repeat the descriptive text and follow it with an initial model. Note that all we need to do at this stage is
list the entities we have identified so far using the rather obvious keyword entity
.
3.1.1. Retail Example
Customers go to a store to purchase items, they may have a specific item in mind or expect to find choices they can choose between. Depending on the kind of item this choice may be variations (in size or color for example) of the same item or the same kind of item from different manufacturers.
When we look at the italic text above we note the possible entities Kind, Variation, Size, Color, and Manufacturer.
However, as we talk them through with the stakeholders we feel that they do not meet the criteria outlined in the
section Where do I start?, except Manufacturer
.
1: module retail is 2: 3: entity Customer 4: 5: entity Store 6: 7: entity Purchase 8: 9: entity Item 10: 11: entity Manufacturer 12: 13: end
Note that, depending on how you state the initial part of the description above you may end up with a different name for
the entity Purchase
; “A store sells items to customers” for example will result in essentially the same entity but with
the name Sale
only because the subject of the sentence has changed. Beware of this, try and always pick a subject to use
for all descriptions to avoid models that include both purchase and sale! In general, we recommend that you pick the
customer as the primary subject as it means you end up with fewer internal names or jargon names for things.
3.1.2. Hospitality Example
A customer books a room according to their choice of size, bed type, or other criteria, for specific dates at a particular property. A customer may make certain kinds of purchases against their room which are then added to the final bill on completion of their stay.
The only additional entity beyond the set already identified in bold is possibly Stay
, which at first looks a lot like
Booking
in that it has a start and end date and references a Customer
and Property
. However, a booking reference the
type of room where as the stay references the actual room, and the start and end dates may not be the same if the
customer left early for example.
In listing 3 we will fill in some details, just to see how it’s done. Specifically note
the use of the arrow operator ->
or →
that denotes the type (right-hand side) of a member (left-hand side). Also, if you
remember that a part of the definition of an entity is that it has strong identity, so as we are adding details to
entities we must provide an identity
member.
1: module hospitality is 2: 3: import xsd 4: 5: entity Customer 6: 7: entity Booking is 8: identity booking_code -> string 9: customer -> Customer 10: property -> Property 11: room_type -> unknown ;; don't know what this is yet. 12: start_date -> xsd:date 13: end_date -> xsd:date 14: number_of_adults -> integer 15: number_of_children -> integer 16: end 17: 18: entity Room 19: 20: entity Property 21: 22: entity Purchase 23: 24: entity Bill is 25: identity transaction_id -> unknown 26: for_stay -> Stay 27: charges -> {0..} Purchase 28: end 29: 30: entity Stay is 31: identity id -> unknown 32: from_booking -> {0..1} Booking 33: end 34: 35: end
The following are useful pointers to features of the entity definition.
- Lines 9-10: the members named
customer
andproperty
reference other entities in the same module even though neither of those entities are complete. - Line 11: the member named
room_type
has the type*unknown*
which is an explicit signal that this model is incomplete as the type has not yet been decided. - Line 11: the semi-colon introduces a line comment, so we added a little note to ourselves about the unknown type for this member. The use of two semi-colons is a style preference as it makes the comment easier to find.
- Lines 8,11-15: the type names
string
andinteger
(andunknown
) are builtin, the typexsd:date
is not, it has been imported from another module namedxsd
. The namexsd:date
is a qualified name which uses the colon character to combine module and definition name. - Line 27: the member named in
charges
inBill
is a sequence type, which means it can have a varying number of values. The specification for upper and lower bounds on a sequence type are it’s cardinality, the lack of a upper bound means that the range is unbounded. The default cardinality being1
. - Line 32: the member named
from_booking
inStay
is another sequence type, although with the cardinality of0..1
may be seen as optional.
The builtin datatypes in SDML are derived from those in the XML Schema, Part 2 specification, the same datatypes used in RDF. The major datatypes are listed in table 1 and compared to corresponding datatypes in SQL, Java and Rust. SQL’s Data Definition Language (DDL) is a useful comparison given that it may be familiar to less technical stakeholders.
Datatype | SQL Equivalent | Java Equivalent | Rust Equivalent |
---|---|---|---|
binary |
BINARY or VARBINARY |
byte[] |
Vec<u8> or [u8] |
boolean |
BIT |
boolean |
bool |
decimal |
DECIMAL |
BigDecimal in java.math |
|
double |
DOUBLE PRECISION |
double |
f64 |
integer |
INTEGER |
long |
i64 |
string |
CHAR or VARCHAR |
String |
String or str |
unsigned |
u64 |
||
uri |
|||
xsd:date |
DATE |
||
xsd:time |
TIME |
||
xsd:dateTime |
DATETIME |
||
xsd:duration |
INTERVAL |
3.1.3. Logistics Example
The business comprises matching available transportation capacity on one or more routes to deliver a customer’s load from source to destination within some time constraint. A load may be one or more standard palettes, or one or more standard containers. This results in one or more delivery contracts.
Note also that we have dropped some of the entities from the original
You can use Annotation Properties to attach documentation properties to your model. In the following example we have added a preferred label “Logistics” (line 6), an alternate label “Shipping” (line 7), and a description (line 8) annotation to the module itself. Don’t worry about the syntax of these for now, that’s covered in an advanced topic later. We have added some to the logistics example in listing 4.
1: module logistics is 2: 3: import [ dc skos uuid ] 4: import [ gs1 ] 5: 6: @skos:prefLabel = "Logistics"@en 7: @skos:altLabel = "Shipping"@en 8: @dc:description = "A domain model for long-haul logistics."@en 9: 10: entity Location 11: ; Global Location Number GLN 12: 13: enum LocationKind 14: 15: entity Consignor is 16: identity gln -> GlobalLocationNumber 17: lei -> {0..1} gs1:LegalEntityIdentifier 18: end 19: 20: entity Consignment is 21: identity ginc -> gs1:GlobalIdentificationNumberForConsignment 22: consignor -> Consignor 23: shipments -> {1..} Shipment 24: end 25: 26: entity Shipment is 27: 28: @editorialNote = 29: "need to add logistic units -- are these entities?" 30: 31: identity gsin -> gs1:GlobalShipmentIdentificationNumber 32: from -> Location 33: to -> Location 34: end 35: 36: end
It is also time to deal with the import
statement (lines 3,4) that we have already seen in a number of examples. An import statement
is used to make definitions in other modules available to us in the current module. Import statements are simpler than a
lot of programming languages in that you can import either a module by name or individual definitions within a module.
In either case SDML requires you to use the qualified name of any imported definition. A qualified name is simply the
name of the module followed by a colon and then the name of the definition. In listing 4 we
see a single import statement with a list of module names, dc
and skos
. These modules allow us to reference the
annotation properties dc:description
, skos:altLabel
, skos:definition
, and skos:prefLabel
.
Annotation properties are like constants in programming, they become attached to their enclosing definition and are
immutable. A property is introduced by the initial @
character and the property name follows. This property is then
assigned a value, using the datatypes in table 1. Some annotations, such as those in listing
4 above, are purely descriptive while others actually affect the semantics of the model
element they are attached to.
Strings, as you can see in listing 4 may be followed by another @
character which
introduces a language tag that identifies the natural language used in the string constant.
3.2. Add details with Structures and Enums
Now that you have the key elements of your domain model in place, you can start to see how this becomes the core vocabulary shared between customers, business, and technical stakeholders. However, a Store, Property, or Shipment are really not interesting as entities without some more detail. While we did add some of the inter-entity relationships in the previous section the only entity we added any additional members to was Booking. In the following sections we will use two more definition types, structures and enumerations to add clarity and structure to the models we have developed so far.
A structure will look very much like an entity in that it has a name and a set of members, however it does not have an identity member. As this means we cannot include them in an entity by reference to an identifier all structures are included within any enclosing structure or entity.
An enumeration is a list of unique variants that represent distinct options.
3.2.1. Retail Example
In listing 5 we have added a common Address
structure (line 13) that is used by the
customer, manufacturer, and store entities. This example does not introduce an enumeration directly; however, the
imported type AlphaTwoCountryCode
is an enumeration (described in listing 6 below). Additionally, the
cardinality expression on line 25 introduces a new keyword ordered
that is a constraint over the sequence that it’s
members must maintain their order, thus the sequence acts as an array or vector. In this example we assume that the 7
elements of the sequence represent the days of the week so ordering would be important (although is the first element
Sunday or Monday?).
1: module retail is 2: 3: import [ skos xsd ] 4: import gs1 5: import iso_17442 6: import iso_3166_1 7: 8: entity Customer is 9: identity customer_id -> unknown 10: address -> Address 11: end 12: 13: structure Address is 14: street {1..2} -> string 15: apartment {0..1} -> string 16: city -> string 17: state_or_district -> string 18: postal_code -> string 19: country -> iso_3166_1:AlphaTwoCountryCode 20: end 21: 22: entity Store is 23: identity location_number -> gs1:GlobalLocationNumber 24: address -> Address 25: open_hours -> {ordered 7} Hours 26: has_pickup -> boolean 27: end 28: 29: structure Hours is 30: open -> xsd:time 31: close -> xsd:time 32: end 33: 34: entity Purchase 35: 36: entity Item is 37: @skos:note = "See following advanced topic." 38: identity gtin -> gs1:GlobalTradeItemNumber 39: description -> string 40: manufacturer -> Manufacturer 41: end 42: 43: entity Manufacturer is 44: identity manufacturer_id -> iso_17442:LegalEntityId 45: address -> Address 46: end 47: 48: end
The following listing shows the definition of the enumeration AlphaTwoCountryCode
. The whitespace in this example should
be ignored, it is just useful to layout a grid of 26 rows of 26 columns for all the possible combination of two ASCII
upper case letters.
1: module iso_3166_1 is 2: 3: enum AlphaTwoCountryCode of 4: AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ 5: BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ 6: CA CC CD CF CG CH CI CK CL CM CN CO CR CU CV CW CX CY CZ 7: DE DJ DK DM DO DZ 8: EC EE EG EH ER ES ET 9: FI FJ FK FM FO FR 10: ;; ... 11: ;; ... 12: ;; ... 13: VA VC VE VG VI VN VU 14: WF WS 15: YE YT 16: ZA ZM ZW 17: end 18: 19: end
Given this module name, iso_3166_1
, we should explain the use of underscores in module names. While any name in SDML may
include underscores, typically as a word separator, the convention in module names is to use the underscore to note any
logical hierarchy in definitions. So we have all ISO standards start with iso_
followed by the standard number 3166
and
then in this case the part number _1
.
Consider using custom datatypes to constrain basic types to avoid errors. For example, in listing 5 the
identifier for an Item
(line 36) is a GTIN or Global Trade Item Number. This standardized identifier has a number of specific
formats but the shortest of those is 8 characters and the longest is 13 characters. So, we create a new datatype which
restricts the basic string to between 8 and 13 characters only using a regex pattern using an annotation property. This
datatype and another standard from GS1 is shown in listing 7.
1: module gs1 is 2: 3: import xsd 4: 5: datatype GlobalLocationNumber <- string is 6: @xsd:pattern = "^[0-9]{13}$" 7: end 8: 9: datatype GlobalTradeItemNumber <- string is 10: @xsd:pattern = "(^[0-9]{8}$)|(^[0-9]{13}$)|(^[0-9]{14}$)" 11: end 12: 13: end
Table 1 lists the annotation properties that can you can use to restrict existing datatypes. All
scalar types have the following additional constraints; maxInclusive
, maxExclusive
, minInclusive
, and minExclusive
.
Datatype | Builtin Constraints |
---|---|
binary |
minLength , maxLength , and length |
boolean |
|
decimal |
pattern , totalDigits , fractionDigits , and scalar |
double |
pattern , and scalar |
integer |
pattern , and scalar |
string |
pattern , minLength , maxLength , length , and langRange |
unsigned |
pattern , and scalar |
uri |
pattern , minLength , maxLength , and length |
xsd:date |
pattern , explicitTimezone and scalar |
xsd:time |
pattern , explicitTimezone and scalar |
xsd:dateTime |
pattern , explicitTimezone and scalar |
xsd:duration |
pattern , explicitTimezone and scalar |
Sequence Types | pattern , minLength , maxLength , and length |
3.2.2. Hospitality Example
Listing 8 adds 2 datatype definitions, 1 enum definition, and 7 structure definitions.
We have also made the previous entity Room
a structure as it is dependent wholly on the valid entity Property
. Lines 79
and 85 introduce a new sequence constraint with the keyword unique
that requires members of the sequence to be unique,
effectively defining the sequence as a set.
1: module hospitality is 2: 3: import xsd 4: 5: entity Customer 6: 7: entity Booking is 8: identity booking_code -> string 9: customer -> Customer 10: property -> Property 11: room_style -> RoomStyle 12: room_rate -> Currency 13: dates -> DateRange 14: number_of_adults -> integer 15: number_of_children -> integer 16: end 17: 18: entity Property is 19: identity property_id -> unsigned 20: name -> string 21: styles -> {1..} RoomStyle 22: rooms -> {1..} Room 23: end 24: 25: entity Bill is 26: identity transaction_id -> unknown 27: for_stay -> Stay 28: charges -> {0..} Purchase 29: end 30: 31: entity Stay is 32: identity id -> unknown 33: booking -> {0..1} Booking 34: room_number -> unsigned 35: room_rate -> {0..1} Currency ;; if different from booking 36: dates -> DateRange 37: tax_percent -> RatePercentage 38: discount_percent -> RatePercentage 39: end 40: 41: datatype Currency <- decimal is 42: @xsd:totalDigits = 9 43: @xsd:fractionDigits = 3 44: end 45: 46: datatype RatePercentage <- decimal is 47: @xsd:totalDigits = 5 48: @xsd:fractionDigits = 3 49: end 50: 51: enum RoomFeature of 52: View 53: NonSmoking 54: Smoking 55: Accessible 56: OneSingleBed 57: OneDoubleBed 58: OneQueenBed 59: OneKingBed 60: TwoSingleBeds 61: TwoDoubleBeds 62: TwoQueenBeds 63: TwoKingBeds 64: Suite 65: Fridge 66: Kitchen 67: Balcony 68: end 69: 70: structure DateRange is 71: starts -> xsd:date 72: ends -> xsd:date 73: end 74: 75: structure Room is 76: number -> unsigned 77: floor -> unsigned 78: style -> RoomStyle 79: additional_features -> {unique 0..*} RoomFeature 80: rate -> Currency 81: end 82: 83: structure RoomStyle is 84: name -> string 85: features -> {unique 1..*} RoomFeature 86: end 87: 88: structure Charge is 89: item -> string 90: provider -> string 91: charge -> decimal 92: tax_percent -> RatePercentage 93: discount_percent -> RatePercentage 94: end 95: 96: structure Purchase is 97: stay -> Stay 98: charges -> {0..} Charge 99: payment -> Payment 100: end 101: 102: structure Payment is 103: amount -> Currency 104: instrument -> PaymentInstrument 105: end 106: 107: structure PaymentInstrument 108: end
While this module has added a lot of detail, one structure at the end, PaymentInstrument
is not yet complete. Be
patient, we will come back to that in the section 3.4 later.
You can add a specific type of annotation, a constraint, to capture conditions that cannot be expressed by the model’s structure alone. For example, in listing 9 we have a simple structure that contains a start date and an end date. They are correctly typed so we know they have to be dates, but what if we were to set the start date to be after the end date, how do we validate that? Can we?
1: module hospitality is 2: 3: import xsd 4: 5: structure DateRange is 6: assert valid_range = "range starts before it ends" 7: 8: assert formal_range is 9: forall self, self.starts <= self.ends 10: end 11: 12: starts -> xsd:date 13: ends -> xsd:date 14: end 15: 16: end
3.2.3. Logistics Example
The detail added to the logistics example is extensive, but some highlights are the use of “kind” enumerations for
Location
(line 10), LogisticsUnit
(line 48), and LogisticsAsset
(line 103). We have also added a constraint to the
logistics unit structure which enforces a rule that a box may not include other units. The more astute reader will note
that this is woefully insufficient as it does not stop us defining a unit of kind palette which includes a unit of kind
rail car – that’s some big palette.
1: module logistics is 2: 3: import [ dc owl rdf skos uuid ] 4: import [ gs1 ] 5: 6: @skos:prefLabel = "Logistics"@en 7: @skos:altLabel = "Shipping"@en 8: @dc:description = "A domain model for long-haul logistics."@en 9: 10: entity Location is 11: identity gln -> gs1:GlobalLocationNumber 12: kind -> LocationKind 13: within -> {0..1} Location 14: end 15: 16: enum LocationKind of 17: Office 18: Warehouse 19: WarehouseYard 20: WarehouseDock 21: RailDepot 22: RailYard 23: Port 24: PortLoading 25: Airport 26: AirportTransfer 27: end 28: 29: entity Consignor is 30: identity gln -> gs1:GlobalLocationNumber 31: lei -> {0..1} gs1:LegalEntityIdentifier 32: name -> string 33: end 34: 35: entity Consignment is 36: identity ginc -> gs1:GlobalIdentificationNumberForConsignment 37: consignor -> Consignor 38: shipments -> {1..} Shipment 39: end 40: 41: entity Shipment is 42: identity gsin -> gs1:GlobalShipmentIdentificationNumber 43: from -> Location 44: to -> Location 45: units -> {1..} LogisticsUnit 46: end 47: 48: structure LogisticsUnit is 49: assert can_include_others = "not empty(includes) implies not kind = Box" 50: sscc -> gs1:SerialShippingContainerCode 51: kind -> LogisticsUnitKind 52: asset -> {1..} LogisticsAsset 53: dimension -> Dimensions 54: weight -> decimal 55: hazmat_classification -> {unique 0..9} DotHazardClassification 56: includes -> {0..} LogisticsUnit 57: end 58: 59: enum LogisticsUnitKind of 60: Box 61: Pallet 62: PalletStack 63: Container 64: RailCar 65: end 66: 67: enum DotHazardClassification of 68: @owl:equivalentClass = unsigned 69: Explosives is 70: @rdf:value = 1 71: end 72: Gasses is 73: @rdf:value = 2 74: end 75: FlammableLiquids is 76: @rdf:value = 3 77: end 78: FlammableSolids is 79: @rdf:value = 4 80: end 81: OxodizingSubstances is 82: @rdf:value = 5 83: end 84: ToxicSubstances is 85: @rdf:value = 6 86: end 87: RadioactiveMaterials is 88: @rdf:value = 7 89: end 90: CorrosiveSubstances is 91: @rdf:value = 8 92: end 93: MiscellaneousDangerousMaterials is 94: @rdf:value = 9 95: end 96: end 97: 98: structure Dimensions is 99: height -> decimal 100: width -> decimal 101: depth -> decimal 102: end 103: 104: structure LogisticsAsset is 105: grai -> gs1:GlobalReturnableAssetIdentifier 106: kind -> LogisticsAssetKind 107: end 108: 109: enum LogisticsAssetKind of 110: Pallet 111: Container 112: RailCar 113: end 114: 115: end
You will probably be thinking that all this detail is making the module quite large and possibly cumbersome. In the following section (3.3) we will discuss approaches to organizing modules to allow not only ease of navigation but also parallel editing.
The enumeration DotHazardClassification
in listing 10 above probably looks
intimidating compared to the previous examples, and that’s OK. This enumeration represents the U.S. Department of
Transportation’s Federal Motor Carrier Safety Administration’s “Nine Classes of Hazardous Material” classification. As
such the values one through nine are defined by government and so we want to be able to associate those values with the
variants of the enum.
We use two key annotation properties that affect the semantics of the enum.
rdf:value
- this denotes the relationship between the variant and a specific value. It is not important that the values are numbers, only that they are of the same type and unique.
owl:equivalentClass
- this denotes that the enumeration may be considered equivalent to the some type. It is important that all values attached to variants are valid for the type attached to the enum.
Also, the cardinality expression on member hazmat_classification
is {unique 1..9}
, to denote that while a unit may
contain a combination of coded materials each code appears only once.
3.3. Describe Events emitted by Entities
SDML does not have a language for describing behavior, so no sequence, activity, state machine, or other diagrams. Given
that the domain model should describe the what of a domain, not the how it is too tempting to start to describe the
behavior inside the domain, it’s operating rules. Additionally, when describing a domain model it is often the case that
the language used by stakeholders to describe any functional aspect of the model tends to be asynchronous and loosely
coupled. To support this SDML provides the event
definition, a way to describe events emitted by entities on state
changes or other internal rules, but importantly these are part of the natural description/vocabulary of the domain.
An event looks very much like a structure, but is has a source
keyword followed by the name of the entity which emits
this event. In fact the similarity between an event and a structure is not accidental as it’s possible to see an event
definition as some additional syntax for a structure which, and this is the important part, contains a reference to an
instance of the entity type. In listing 11 we have a simple entity (line 5) and a simple event
(line 10) emitted by the entity. If we were to create an equivalent structure it might look like that defined on line
13, which in turn is equivalent to the second structure on line 18.
1: module example is 2: 3: import uuid 4: 5: entity Example is 6: identity id -> uuid:Uuid 7: some_other_thing -> integer 8: end 9: 10: event ExampleCreated source Example 11: 12: ;; the above event is "equivalent" to the structure below 13: structure ExampleCreatedStructure is 14: source_entity -> Example 15: end 16: 17: ;; the above is also "equivalent" to the structure below 18: structure ExampleCreatedStructure2 is 19: source_entity_id -> uuid:Uuid 20: end 21: 22: end
One specific different between a structure definition and an event definition is that the former without an explicit
body (between is
and end
) is incomplete, an event does not need a body as it does already have a single member, the
source entity. In many cases events need only be a distinct type with a reference to the particular entity and no other
information.
3.3.1. Retail Example
1: module retail_customers is 2: 3: entity Customer 4: 5: entity Purchase 6: 7: structure ItemQuantity is 8: item -> Item 9: quantity -> integer 10: end 11: 12: event InventoryAdjustment source Purchase is 13: adjustments -> {1..} ItemQuantity 14: end 15: 16: end
1: module retail is 2: 3: entity Store 4: 5: event StoreNowOpen source Store is 6: at -> xsd:dateTime 7: end 8: event StoreNowClosed source Store is 9: at -> xsd:dateTime 10: end 11: 12: end
1: module retail is 2: 3: entity Item 4: 5: entity Manufacturer 6: 7: end
3.3.2. Hospitality Example
1: module hospitality_customers is 2: 3: entity Customer 4: 5: entity Booking 6: 7: enum BookingChange of 8: RoomStyle 9: Dates 10: Occupancy 11: end 12: 13: event BookingCreated source Booking 14: event BookingUpdated source Booking is 15: what_changed -> BookingChange 16: end 17: event BookingCanceled source Booking is 18: reason -> string 19: end 20: 21: end
1: module hospitality_properties is 2: 3: entity Property 4: 5: entity Stay 6: 7: event CheckedIn source Stay 8: event CheckedOut source Stay 9: event RoomCharged source Stay is 10: charge -> Charge 11: end 12: 13: entity Bill 14: 15: event BillReady source Bill 16: event PaymentMade source Bill is 17: payment -> Payment 18: end 19: event Settled source Bill 20: 21: end
3.3.3. Logistics Example
1: module logistics is 2: 3: import [ dc skos uuid ] 4: import [ gs1 ] 5: 6: @skos:prefLabel = "Logistics"@en 7: @skos:altLabel = "Shipping"@en 8: @dc:description = "A domain model for long-haul logistics."@en 9: 10: entity Consignor 11: 12: ;; based uopn ANSI X.12 204 13: event CarrierLoadTender source Consignor 14: 15: entity Carrier 16: 17: ;; based uopn ANSI X.12 990 18: event CarrierLoadTenderResponse source Carrier 19: 20: entity Consignment 21: 22: ;; based uopn ANSI X.12 211 23: event BillOfLading source Consignment 24: ;; based uopn ANSI X.12 210 25: event FreightDetailsAndInvoice source Consignment 26: 27: entity Shipment 28: 29: ;; based uopn ANSI X.12 856 30: event AdvanceShippingNotice source Shipment 31: ;; based uopn ANSI X.12 214 32: event TransportStatusNotification source Shipment 33: event TransferRequest source Shipment 34: event TransferConfirmation source Shipment 35: 36: enum TransferKind of 37: Pickup 38: Dropoff 39: end 40: 41: end
3.4. Describe “This or That” with Unions
So far we have ween entities, structures, events, and enumerations.
3.4.1. Hospitality Example
1: module hospitality_properties is 2: 3: import pii 4: 5: structure PaymentInstrument is 6: assert only_one = 7: "one, and only one, member may be present" 8: cash -> {0..1} Currency 9: credit_balance -> {0..1} Currency 10: points -> {0..1} unsigned 11: card -> {0..1} pii:CardToken 12: end 13: 14: end
1: module hospitality_properties is 2: 3: import pii 4: 5: union PaymentInstrument of 6: Currency as Cash 7: Currency as CreditBalance 8: unsigned as Points 9: pii:CardToken as Card 10: end 11: 12: end
3.4.2. Logistics Example
1: module gs1 is 2: 3: import [ xsd ] 4: 5: datatype UniversalProductCode <- string 6: datatype EuropeanArticleNumber <- string 7: datatype InternationalStandardBookNumber <- string 8: datatype InternationalStandardMusicNumber <- string 9: datatype InternationalStandardSerialNumber <- string 10: 11: union GlobalTradeIdentificationNumber of 12: @skos:prefLabel = "Global Trade Identification Number"@en 13: @skos:altLabel = "GTIN"@en 14: UniversalProductCode 15: EuropeanArticleNumber 16: InternationalStandardBookNumber 17: InternationalStandardMusicNumber 18: InternationalStandardSerialNumber 19: end 20: 21: end
4. How do I use it?
DDD
4.1. Editing SDML Source
TBD
4.2. Drawing Diagrams
Figure 1: Hospitality Concepts (Entities)
Figure 2: Hospitality UML
4.3. Document Modules
TBD
5. What do I do next?
TBD
5.2. For Tool Builders
TBD
5.3. More Reading
- What is Domain Modeling? in the SDML reference.
- Domain Modeling: What you need to know before coding, Norberto RodrÃguez (Norbs). Thoughtworks.
- A Brief Introduction to Domain Modeling, Oleg Chursin.
- What is a Domain Model?, [A].
- Domain Model, Wikipedia.
6. A Brief Glossary
- Annotation
- A property or constraint attached to a definition that may document, classify, or constrain the definition in ways the SDML syntax cannot. For example, attaching a descriptive property, a prescriptive definition, or categorization labels.
- Constraint
- A mechanism to describe correctness, or well-formedness, rules for a particular model and specifically those rules that may be hard or impossible to capture in the SDML syntax itself.
- Datatype
- An unstructured type that describes the atomic values (strings, numbers, etc.) in a model. Making a datatype more specific such as saying a string has 4-10 characters only, or a quantity is 1-100 only, documents more information than string/number alone.
- Definition
- In SDML all elements in a model such as modules, entities, structures, fields in a structure, or variants of an enumeration, are definitions. Definitions all have an identity which is unique within it’s parent namespace.
- Domain
- Domain Model
- Domain-Driven Development
- Entity
- Enumeration
- Features
- Formal Constraint
- Informal Constraint
- Member
- A member is a field within a product type, consisting of a name and target type. A member may alternatively be a reference to a property role.
- Model
- Module
- Namespace
- Ordering (Sequence)
- A constraint that denotes whether a sequence is ordered or unordered.
- Product Type
- A product type is one of entity, event, or structure.
- Property (Annotation)
- Property (Member)
- Role
- Sequence
- Structure
- Sum Type
- A sum type is one of enumeration, property, or union.
- Type Variant
- UML
- Union
- Uniqueness (Sequence)
- A constraint that denotes whether a sequence contains only unique elements or is non-unique.
- Value Variant
- Variant
- A unique