OCL Constraints

OCL Constraints and Validation

Learn to define and validate OCL constraints on Ecore models.

The Object Constraint Language (OCL) allows you to specify invariants, derivation rules, and pre/post conditions that models must satisfy. In this tutorial, you’ll:

  • Define invariant constraints on model elements

  • Use collection operations for validation

  • Create derived attributes with OCL expressions

  • Validate models against constraints

40 mins Estimated Time

Section 1

Understanding OCL Constraints

OCL constraints specify rules that model instances must follow. They’re expressed as boolean expressions that evaluate to true for valid models.

Types of OCL constraints:

  • Invariants: Conditions that must always be true

  • Derivation rules: How to compute derived attributes

  • Pre/post conditions: Method contracts (input/output)

Common OCL operations:

  • forAll: All elements satisfy condition

  • exists: At least one element satisfies condition

  • select/reject: Filter collections

  • isUnique: All values are distinct

  • size: Collection element count

Step 1

Examine the Bank Account metamodel with OCL constraints ensuring unique account numbers, valid emails, adult customers, positive balances, and proper account formats.

bank-account.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="Bank" nsURI="http://www.example.org/bank" nsPrefix="bank">
4
5 <!-- OCL Constraints Documentation -->
6 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
7 <details key="documentation" value="Bank Account metamodel demonstrating OCL constraints including invariants, derivation rules, and pre/post conditions."/>
8 </eAnnotations>
9
10 <!-- Bank: Container for customers and accounts -->
11 <eClassifiers xsi:type="ecore:EClass" name="Bank">
12 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
13 <details key="constraints" value="uniqueAccountNumbers uniqueCustomerIds"/>
14 </eAnnotations>
15 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
16 <!-- Invariant: All account numbers must be unique -->
17 <details key="uniqueAccountNumbers" value="self.accounts->isUnique(accountNumber)"/>
18 <!-- Invariant: All customer IDs must be unique -->
19 <details key="uniqueCustomerIds" value="self.customers->isUnique(customerId)"/>
20 </eAnnotations>
21 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
22 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
23 <eStructuralFeatures xsi:type="ecore:EAttribute" name="swiftCode" lowerBound="1"
24 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
25 <eStructuralFeatures xsi:type="ecore:EReference" name="customers" upperBound="-1"
26 eType="#//Customer" containment="true" eOpposite="#//Customer/bank"/>
27 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
28 eType="#//Account" containment="true" eOpposite="#//Account/bank"/>
29 </eClassifiers>
30
31 <!-- Customer: Bank customer with accounts -->
32 <eClassifiers xsi:type="ecore:EClass" name="Customer">
33 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
34 <details key="constraints" value="validEmail adultCustomer"/>
35 </eAnnotations>
36 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
37 <!-- Invariant: Email must contain @ symbol -->
38 <details key="validEmail" value="self.email.indexOf('@') > 0"/>
39 <!-- Invariant: Customer must be at least 18 years old -->
40 <details key="adultCustomer" value="self.age >= 18"/>
41 </eAnnotations>
42 <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerId" lowerBound="1"
43 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
44 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
45 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
46 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" lowerBound="1"
47 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
48 <eStructuralFeatures xsi:type="ecore:EAttribute" name="age" lowerBound="1"
49 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
50 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
51 eOpposite="#//Bank/customers"/>
52 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
53 eType="#//Account" eOpposite="#//Account/owner"/>
54 <!-- Derived: Total balance across all accounts -->
55 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalBalance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
56 changeable="false" volatile="true" transient="true" derived="true">
57 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
58 <details key="derivation" value="self.accounts->collect(balance)->sum()"/>
59 </eAnnotations>
60 </eStructuralFeatures>
61 <!-- Derived: Number of active accounts -->
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="activeAccountCount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
63 changeable="false" volatile="true" transient="true" derived="true">
64 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
65 <details key="derivation" value="self.accounts->select(isActive)->size()"/>
66 </eAnnotations>
67 </eStructuralFeatures>
68 </eClassifiers>
69
70 <!-- AccountType: Enum for account types -->
71 <eClassifiers xsi:type="ecore:EEnum" name="AccountType">
72 <eLiterals name="SAVINGS" value="0"/>
73 <eLiterals name="CURRENT" value="1"/>
74 <eLiterals name="FIXED_DEPOSIT" value="2"/>
75 </eClassifiers>
76
77 <!-- Account: Bank account with balance constraints -->
78 <eClassifiers xsi:type="ecore:EClass" name="Account">
79 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
80 <details key="constraints" value="positiveBalance validAccountNumber sufficientMinimumBalance"/>
81 </eAnnotations>
82 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
83 <!-- Invariant: Balance must not be negative -->
84 <details key="positiveBalance" value="self.balance >= 0"/>
85 <!-- Invariant: Account number must be 10 digits -->
86 <details key="validAccountNumber" value="self.accountNumber.size() = 10"/>
87 <!-- Invariant: Savings accounts must maintain minimum balance -->
88 <details key="sufficientMinimumBalance" value="self.accountType = AccountType::SAVINGS implies self.balance >= self.minimumBalance"/>
89 </eAnnotations>
90 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountNumber" lowerBound="1"
91 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
92 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountType" lowerBound="1"
93 eType="#//AccountType" defaultValueLiteral="SAVINGS"/>
94 <eStructuralFeatures xsi:type="ecore:EAttribute" name="balance" lowerBound="1"
95 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="0.0"/>
96 <eStructuralFeatures xsi:type="ecore:EAttribute" name="minimumBalance"
97 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="100.0"/>
98 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isActive"
99 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
100 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
101 eOpposite="#//Bank/accounts"/>
102 <eStructuralFeatures xsi:type="ecore:EReference" name="owner" lowerBound="1" eType="#//Customer"
103 eOpposite="#//Customer/accounts"/>
104 <eStructuralFeatures xsi:type="ecore:EReference" name="transactions" upperBound="-1"
105 eType="#//Transaction" containment="true" eOpposite="#//Transaction/account"/>
106 <!-- Derived: Total deposits -->
107 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalDeposits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
108 changeable="false" volatile="true" transient="true" derived="true">
109 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
110 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::DEPOSIT)->collect(amount)->sum()"/>
111 </eAnnotations>
112 </eStructuralFeatures>
113 <!-- Derived: Total withdrawals -->
114 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalWithdrawals" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
115 changeable="false" volatile="true" transient="true" derived="true">
116 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
117 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::WITHDRAWAL)->collect(amount)->sum()"/>
118 </eAnnotations>
119 </eStructuralFeatures>
120 <!-- Operation with pre/post conditions -->
121 <eOperations name="withdraw" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean">
122 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
123 <!-- Precondition: Amount must be positive -->
124 <details key="pre" value="amount > 0"/>
125 <!-- Precondition: Must have sufficient funds -->
126 <details key="pre" value="self.balance >= amount"/>
127 <!-- Postcondition: Balance is reduced -->
128 <details key="post" value="self.balance = self.balance@pre - amount"/>
129 </eAnnotations>
130 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
131 </eOperations>
132 <eOperations name="deposit">
133 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
134 <!-- Precondition: Amount must be positive -->
135 <details key="pre" value="amount > 0"/>
136 <!-- Postcondition: Balance is increased -->
137 <details key="post" value="self.balance = self.balance@pre + amount"/>
138 </eAnnotations>
139 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
140 </eOperations>
141 </eClassifiers>
142
143 <!-- TransactionType: Enum for transaction types -->
144 <eClassifiers xsi:type="ecore:EEnum" name="TransactionType">
145 <eLiterals name="DEPOSIT" value="0"/>
146 <eLiterals name="WITHDRAWAL" value="1"/>
147 <eLiterals name="TRANSFER" value="2"/>
148 </eClassifiers>
149
150 <!-- Transaction: Individual transaction record -->
151 <eClassifiers xsi:type="ecore:EClass" name="Transaction">
152 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
153 <details key="constraints" value="positiveAmount validDescription"/>
154 </eAnnotations>
155 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
156 <!-- Invariant: Transaction amount must be positive -->
157 <details key="positiveAmount" value="self.amount > 0"/>
158 <!-- Invariant: Description must not be empty -->
159 <details key="validDescription" value="self.description.size() > 0"/>
160 </eAnnotations>
161 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionId" lowerBound="1"
162 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
163 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionType" lowerBound="1"
164 eType="#//TransactionType"/>
165 <eStructuralFeatures xsi:type="ecore:EAttribute" name="amount" lowerBound="1"
166 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
167 <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" lowerBound="1"
168 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
169 <eStructuralFeatures xsi:type="ecore:EAttribute" name="timestamp" lowerBound="1"
170 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
171 <eStructuralFeatures xsi:type="ecore:EReference" name="account" lowerBound="1" eType="#//Account"
172 eOpposite="#//Account/transactions"/>
173 </eClassifiers>
174</ecore:EPackage>

Section 2

Constraint Categories

Let’s examine the different types of constraints in detail. Each serves a specific validation purpose.

Step 1

Uniqueness constraints ensure no duplicate identifiers.

The uniqueAccountNumbers constraint uses isUnique() to verify all account numbers are distinct across the bank. Similarly, uniqueCustomerIds ensures customer IDs don’t repeat.

bank-account.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="Bank" nsURI="http://www.example.org/bank" nsPrefix="bank">
4
5 <!-- OCL Constraints Documentation -->
6 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
7 <details key="documentation" value="Bank Account metamodel demonstrating OCL constraints including invariants, derivation rules, and pre/post conditions."/>
8 </eAnnotations>
9
10 <!-- Bank: Container for customers and accounts -->
11 <eClassifiers xsi:type="ecore:EClass" name="Bank">
12 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
13 <details key="constraints" value="uniqueAccountNumbers uniqueCustomerIds"/>
14 </eAnnotations>
15 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
16 <!-- Invariant: All account numbers must be unique -->
17 <details key="uniqueAccountNumbers" value="self.accounts->isUnique(accountNumber)"/>
18 <!-- Invariant: All customer IDs must be unique -->
19 <details key="uniqueCustomerIds" value="self.customers->isUnique(customerId)"/>
20 </eAnnotations>
21 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
22 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
23 <eStructuralFeatures xsi:type="ecore:EAttribute" name="swiftCode" lowerBound="1"
24 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
25 <eStructuralFeatures xsi:type="ecore:EReference" name="customers" upperBound="-1"
26 eType="#//Customer" containment="true" eOpposite="#//Customer/bank"/>
27 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
28 eType="#//Account" containment="true" eOpposite="#//Account/bank"/>
29 </eClassifiers>
30
31 <!-- Customer: Bank customer with accounts -->
32 <eClassifiers xsi:type="ecore:EClass" name="Customer">
33 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
34 <details key="constraints" value="validEmail adultCustomer"/>
35 </eAnnotations>
36 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
37 <!-- Invariant: Email must contain @ symbol -->
38 <details key="validEmail" value="self.email.indexOf('@') > 0"/>
39 <!-- Invariant: Customer must be at least 18 years old -->
40 <details key="adultCustomer" value="self.age >= 18"/>
41 </eAnnotations>
42 <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerId" lowerBound="1"
43 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
44 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
45 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
46 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" lowerBound="1"
47 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
48 <eStructuralFeatures xsi:type="ecore:EAttribute" name="age" lowerBound="1"
49 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
50 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
51 eOpposite="#//Bank/customers"/>
52 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
53 eType="#//Account" eOpposite="#//Account/owner"/>
54 <!-- Derived: Total balance across all accounts -->
55 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalBalance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
56 changeable="false" volatile="true" transient="true" derived="true">
57 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
58 <details key="derivation" value="self.accounts->collect(balance)->sum()"/>
59 </eAnnotations>
60 </eStructuralFeatures>
61 <!-- Derived: Number of active accounts -->
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="activeAccountCount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
63 changeable="false" volatile="true" transient="true" derived="true">
64 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
65 <details key="derivation" value="self.accounts->select(isActive)->size()"/>
66 </eAnnotations>
67 </eStructuralFeatures>
68 </eClassifiers>
69
70 <!-- AccountType: Enum for account types -->
71 <eClassifiers xsi:type="ecore:EEnum" name="AccountType">
72 <eLiterals name="SAVINGS" value="0"/>
73 <eLiterals name="CURRENT" value="1"/>
74 <eLiterals name="FIXED_DEPOSIT" value="2"/>
75 </eClassifiers>
76
77 <!-- Account: Bank account with balance constraints -->
78 <eClassifiers xsi:type="ecore:EClass" name="Account">
79 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
80 <details key="constraints" value="positiveBalance validAccountNumber sufficientMinimumBalance"/>
81 </eAnnotations>
82 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
83 <!-- Invariant: Balance must not be negative -->
84 <details key="positiveBalance" value="self.balance >= 0"/>
85 <!-- Invariant: Account number must be 10 digits -->
86 <details key="validAccountNumber" value="self.accountNumber.size() = 10"/>
87 <!-- Invariant: Savings accounts must maintain minimum balance -->
88 <details key="sufficientMinimumBalance" value="self.accountType = AccountType::SAVINGS implies self.balance >= self.minimumBalance"/>
89 </eAnnotations>
90 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountNumber" lowerBound="1"
91 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
92 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountType" lowerBound="1"
93 eType="#//AccountType" defaultValueLiteral="SAVINGS"/>
94 <eStructuralFeatures xsi:type="ecore:EAttribute" name="balance" lowerBound="1"
95 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="0.0"/>
96 <eStructuralFeatures xsi:type="ecore:EAttribute" name="minimumBalance"
97 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="100.0"/>
98 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isActive"
99 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
100 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
101 eOpposite="#//Bank/accounts"/>
102 <eStructuralFeatures xsi:type="ecore:EReference" name="owner" lowerBound="1" eType="#//Customer"
103 eOpposite="#//Customer/accounts"/>
104 <eStructuralFeatures xsi:type="ecore:EReference" name="transactions" upperBound="-1"
105 eType="#//Transaction" containment="true" eOpposite="#//Transaction/account"/>
106 <!-- Derived: Total deposits -->
107 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalDeposits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
108 changeable="false" volatile="true" transient="true" derived="true">
109 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
110 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::DEPOSIT)->collect(amount)->sum()"/>
111 </eAnnotations>
112 </eStructuralFeatures>
113 <!-- Derived: Total withdrawals -->
114 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalWithdrawals" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
115 changeable="false" volatile="true" transient="true" derived="true">
116 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
117 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::WITHDRAWAL)->collect(amount)->sum()"/>
118 </eAnnotations>
119 </eStructuralFeatures>
120 <!-- Operation with pre/post conditions -->
121 <eOperations name="withdraw" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean">
122 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
123 <!-- Precondition: Amount must be positive -->
124 <details key="pre" value="amount > 0"/>
125 <!-- Precondition: Must have sufficient funds -->
126 <details key="pre" value="self.balance >= amount"/>
127 <!-- Postcondition: Balance is reduced -->
128 <details key="post" value="self.balance = self.balance@pre - amount"/>
129 </eAnnotations>
130 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
131 </eOperations>
132 <eOperations name="deposit">
133 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
134 <!-- Precondition: Amount must be positive -->
135 <details key="pre" value="amount > 0"/>
136 <!-- Postcondition: Balance is increased -->
137 <details key="post" value="self.balance = self.balance@pre + amount"/>
138 </eAnnotations>
139 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
140 </eOperations>
141 </eClassifiers>
142
143 <!-- TransactionType: Enum for transaction types -->
144 <eClassifiers xsi:type="ecore:EEnum" name="TransactionType">
145 <eLiterals name="DEPOSIT" value="0"/>
146 <eLiterals name="WITHDRAWAL" value="1"/>
147 <eLiterals name="TRANSFER" value="2"/>
148 </eClassifiers>
149
150 <!-- Transaction: Individual transaction record -->
151 <eClassifiers xsi:type="ecore:EClass" name="Transaction">
152 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
153 <details key="constraints" value="positiveAmount validDescription"/>
154 </eAnnotations>
155 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
156 <!-- Invariant: Transaction amount must be positive -->
157 <details key="positiveAmount" value="self.amount > 0"/>
158 <!-- Invariant: Description must not be empty -->
159 <details key="validDescription" value="self.description.size() > 0"/>
160 </eAnnotations>
161 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionId" lowerBound="1"
162 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
163 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionType" lowerBound="1"
164 eType="#//TransactionType"/>
165 <eStructuralFeatures xsi:type="ecore:EAttribute" name="amount" lowerBound="1"
166 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
167 <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" lowerBound="1"
168 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
169 <eStructuralFeatures xsi:type="ecore:EAttribute" name="timestamp" lowerBound="1"
170 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
171 <eStructuralFeatures xsi:type="ecore:EReference" name="account" lowerBound="1" eType="#//Account"
172 eOpposite="#//Account/transactions"/>
173 </eClassifiers>
174</ecore:EPackage>

Step 2

Format validation checks string patterns.

The validEmail constraint checks that email addresses contain an ‘@’ symbol. The validAccountNumber constraint verifies account numbers are exactly 10 digits.

bank-account.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="Bank" nsURI="http://www.example.org/bank" nsPrefix="bank">
4
5 <!-- OCL Constraints Documentation -->
6 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
7 <details key="documentation" value="Bank Account metamodel demonstrating OCL constraints including invariants, derivation rules, and pre/post conditions."/>
8 </eAnnotations>
9
10 <!-- Bank: Container for customers and accounts -->
11 <eClassifiers xsi:type="ecore:EClass" name="Bank">
12 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
13 <details key="constraints" value="uniqueAccountNumbers uniqueCustomerIds"/>
14 </eAnnotations>
15 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
16 <!-- Invariant: All account numbers must be unique -->
17 <details key="uniqueAccountNumbers" value="self.accounts->isUnique(accountNumber)"/>
18 <!-- Invariant: All customer IDs must be unique -->
19 <details key="uniqueCustomerIds" value="self.customers->isUnique(customerId)"/>
20 </eAnnotations>
21 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
22 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
23 <eStructuralFeatures xsi:type="ecore:EAttribute" name="swiftCode" lowerBound="1"
24 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
25 <eStructuralFeatures xsi:type="ecore:EReference" name="customers" upperBound="-1"
26 eType="#//Customer" containment="true" eOpposite="#//Customer/bank"/>
27 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
28 eType="#//Account" containment="true" eOpposite="#//Account/bank"/>
29 </eClassifiers>
30
31 <!-- Customer: Bank customer with accounts -->
32 <eClassifiers xsi:type="ecore:EClass" name="Customer">
33 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
34 <details key="constraints" value="validEmail adultCustomer"/>
35 </eAnnotations>
36 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
37 <!-- Invariant: Email must contain @ symbol -->
38 <details key="validEmail" value="self.email.indexOf('@') > 0"/>
39 <!-- Invariant: Customer must be at least 18 years old -->
40 <details key="adultCustomer" value="self.age >= 18"/>
41 </eAnnotations>
42 <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerId" lowerBound="1"
43 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
44 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
45 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
46 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" lowerBound="1"
47 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
48 <eStructuralFeatures xsi:type="ecore:EAttribute" name="age" lowerBound="1"
49 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
50 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
51 eOpposite="#//Bank/customers"/>
52 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
53 eType="#//Account" eOpposite="#//Account/owner"/>
54 <!-- Derived: Total balance across all accounts -->
55 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalBalance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
56 changeable="false" volatile="true" transient="true" derived="true">
57 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
58 <details key="derivation" value="self.accounts->collect(balance)->sum()"/>
59 </eAnnotations>
60 </eStructuralFeatures>
61 <!-- Derived: Number of active accounts -->
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="activeAccountCount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
63 changeable="false" volatile="true" transient="true" derived="true">
64 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
65 <details key="derivation" value="self.accounts->select(isActive)->size()"/>
66 </eAnnotations>
67 </eStructuralFeatures>
68 </eClassifiers>
69
70 <!-- AccountType: Enum for account types -->
71 <eClassifiers xsi:type="ecore:EEnum" name="AccountType">
72 <eLiterals name="SAVINGS" value="0"/>
73 <eLiterals name="CURRENT" value="1"/>
74 <eLiterals name="FIXED_DEPOSIT" value="2"/>
75 </eClassifiers>
76
77 <!-- Account: Bank account with balance constraints -->
78 <eClassifiers xsi:type="ecore:EClass" name="Account">
79 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
80 <details key="constraints" value="positiveBalance validAccountNumber sufficientMinimumBalance"/>
81 </eAnnotations>
82 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
83 <!-- Invariant: Balance must not be negative -->
84 <details key="positiveBalance" value="self.balance >= 0"/>
85 <!-- Invariant: Account number must be 10 digits -->
86 <details key="validAccountNumber" value="self.accountNumber.size() = 10"/>
87 <!-- Invariant: Savings accounts must maintain minimum balance -->
88 <details key="sufficientMinimumBalance" value="self.accountType = AccountType::SAVINGS implies self.balance >= self.minimumBalance"/>
89 </eAnnotations>
90 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountNumber" lowerBound="1"
91 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
92 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountType" lowerBound="1"
93 eType="#//AccountType" defaultValueLiteral="SAVINGS"/>
94 <eStructuralFeatures xsi:type="ecore:EAttribute" name="balance" lowerBound="1"
95 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="0.0"/>
96 <eStructuralFeatures xsi:type="ecore:EAttribute" name="minimumBalance"
97 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="100.0"/>
98 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isActive"
99 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
100 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
101 eOpposite="#//Bank/accounts"/>
102 <eStructuralFeatures xsi:type="ecore:EReference" name="owner" lowerBound="1" eType="#//Customer"
103 eOpposite="#//Customer/accounts"/>
104 <eStructuralFeatures xsi:type="ecore:EReference" name="transactions" upperBound="-1"
105 eType="#//Transaction" containment="true" eOpposite="#//Transaction/account"/>
106 <!-- Derived: Total deposits -->
107 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalDeposits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
108 changeable="false" volatile="true" transient="true" derived="true">
109 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
110 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::DEPOSIT)->collect(amount)->sum()"/>
111 </eAnnotations>
112 </eStructuralFeatures>
113 <!-- Derived: Total withdrawals -->
114 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalWithdrawals" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
115 changeable="false" volatile="true" transient="true" derived="true">
116 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
117 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::WITHDRAWAL)->collect(amount)->sum()"/>
118 </eAnnotations>
119 </eStructuralFeatures>
120 <!-- Operation with pre/post conditions -->
121 <eOperations name="withdraw" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean">
122 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
123 <!-- Precondition: Amount must be positive -->
124 <details key="pre" value="amount > 0"/>
125 <!-- Precondition: Must have sufficient funds -->
126 <details key="pre" value="self.balance >= amount"/>
127 <!-- Postcondition: Balance is reduced -->
128 <details key="post" value="self.balance = self.balance@pre - amount"/>
129 </eAnnotations>
130 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
131 </eOperations>
132 <eOperations name="deposit">
133 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
134 <!-- Precondition: Amount must be positive -->
135 <details key="pre" value="amount > 0"/>
136 <!-- Postcondition: Balance is increased -->
137 <details key="post" value="self.balance = self.balance@pre + amount"/>
138 </eAnnotations>
139 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
140 </eOperations>
141 </eClassifiers>
142
143 <!-- TransactionType: Enum for transaction types -->
144 <eClassifiers xsi:type="ecore:EEnum" name="TransactionType">
145 <eLiterals name="DEPOSIT" value="0"/>
146 <eLiterals name="WITHDRAWAL" value="1"/>
147 <eLiterals name="TRANSFER" value="2"/>
148 </eClassifiers>
149
150 <!-- Transaction: Individual transaction record -->
151 <eClassifiers xsi:type="ecore:EClass" name="Transaction">
152 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
153 <details key="constraints" value="positiveAmount validDescription"/>
154 </eAnnotations>
155 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
156 <!-- Invariant: Transaction amount must be positive -->
157 <details key="positiveAmount" value="self.amount > 0"/>
158 <!-- Invariant: Description must not be empty -->
159 <details key="validDescription" value="self.description.size() > 0"/>
160 </eAnnotations>
161 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionId" lowerBound="1"
162 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
163 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionType" lowerBound="1"
164 eType="#//TransactionType"/>
165 <eStructuralFeatures xsi:type="ecore:EAttribute" name="amount" lowerBound="1"
166 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
167 <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" lowerBound="1"
168 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
169 <eStructuralFeatures xsi:type="ecore:EAttribute" name="timestamp" lowerBound="1"
170 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
171 <eStructuralFeatures xsi:type="ecore:EReference" name="account" lowerBound="1" eType="#//Account"
172 eOpposite="#//Account/transactions"/>
173 </eClassifiers>
174</ecore:EPackage>

Step 3

Business rules encode domain logic.

The adultCustomer constraint requires customers to be at least 18 years old. The sufficientMinimumBalance constraint ensures savings accounts maintain their minimum balance.

bank-account.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="Bank" nsURI="http://www.example.org/bank" nsPrefix="bank">
4
5 <!-- OCL Constraints Documentation -->
6 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
7 <details key="documentation" value="Bank Account metamodel demonstrating OCL constraints including invariants, derivation rules, and pre/post conditions."/>
8 </eAnnotations>
9
10 <!-- Bank: Container for customers and accounts -->
11 <eClassifiers xsi:type="ecore:EClass" name="Bank">
12 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
13 <details key="constraints" value="uniqueAccountNumbers uniqueCustomerIds"/>
14 </eAnnotations>
15 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
16 <!-- Invariant: All account numbers must be unique -->
17 <details key="uniqueAccountNumbers" value="self.accounts->isUnique(accountNumber)"/>
18 <!-- Invariant: All customer IDs must be unique -->
19 <details key="uniqueCustomerIds" value="self.customers->isUnique(customerId)"/>
20 </eAnnotations>
21 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
22 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
23 <eStructuralFeatures xsi:type="ecore:EAttribute" name="swiftCode" lowerBound="1"
24 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
25 <eStructuralFeatures xsi:type="ecore:EReference" name="customers" upperBound="-1"
26 eType="#//Customer" containment="true" eOpposite="#//Customer/bank"/>
27 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
28 eType="#//Account" containment="true" eOpposite="#//Account/bank"/>
29 </eClassifiers>
30
31 <!-- Customer: Bank customer with accounts -->
32 <eClassifiers xsi:type="ecore:EClass" name="Customer">
33 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
34 <details key="constraints" value="validEmail adultCustomer"/>
35 </eAnnotations>
36 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
37 <!-- Invariant: Email must contain @ symbol -->
38 <details key="validEmail" value="self.email.indexOf('@') > 0"/>
39 <!-- Invariant: Customer must be at least 18 years old -->
40 <details key="adultCustomer" value="self.age >= 18"/>
41 </eAnnotations>
42 <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerId" lowerBound="1"
43 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
44 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
45 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
46 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" lowerBound="1"
47 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
48 <eStructuralFeatures xsi:type="ecore:EAttribute" name="age" lowerBound="1"
49 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
50 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
51 eOpposite="#//Bank/customers"/>
52 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
53 eType="#//Account" eOpposite="#//Account/owner"/>
54 <!-- Derived: Total balance across all accounts -->
55 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalBalance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
56 changeable="false" volatile="true" transient="true" derived="true">
57 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
58 <details key="derivation" value="self.accounts->collect(balance)->sum()"/>
59 </eAnnotations>
60 </eStructuralFeatures>
61 <!-- Derived: Number of active accounts -->
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="activeAccountCount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
63 changeable="false" volatile="true" transient="true" derived="true">
64 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
65 <details key="derivation" value="self.accounts->select(isActive)->size()"/>
66 </eAnnotations>
67 </eStructuralFeatures>
68 </eClassifiers>
69
70 <!-- AccountType: Enum for account types -->
71 <eClassifiers xsi:type="ecore:EEnum" name="AccountType">
72 <eLiterals name="SAVINGS" value="0"/>
73 <eLiterals name="CURRENT" value="1"/>
74 <eLiterals name="FIXED_DEPOSIT" value="2"/>
75 </eClassifiers>
76
77 <!-- Account: Bank account with balance constraints -->
78 <eClassifiers xsi:type="ecore:EClass" name="Account">
79 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
80 <details key="constraints" value="positiveBalance validAccountNumber sufficientMinimumBalance"/>
81 </eAnnotations>
82 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
83 <!-- Invariant: Balance must not be negative -->
84 <details key="positiveBalance" value="self.balance >= 0"/>
85 <!-- Invariant: Account number must be 10 digits -->
86 <details key="validAccountNumber" value="self.accountNumber.size() = 10"/>
87 <!-- Invariant: Savings accounts must maintain minimum balance -->
88 <details key="sufficientMinimumBalance" value="self.accountType = AccountType::SAVINGS implies self.balance >= self.minimumBalance"/>
89 </eAnnotations>
90 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountNumber" lowerBound="1"
91 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
92 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountType" lowerBound="1"
93 eType="#//AccountType" defaultValueLiteral="SAVINGS"/>
94 <eStructuralFeatures xsi:type="ecore:EAttribute" name="balance" lowerBound="1"
95 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="0.0"/>
96 <eStructuralFeatures xsi:type="ecore:EAttribute" name="minimumBalance"
97 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="100.0"/>
98 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isActive"
99 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
100 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
101 eOpposite="#//Bank/accounts"/>
102 <eStructuralFeatures xsi:type="ecore:EReference" name="owner" lowerBound="1" eType="#//Customer"
103 eOpposite="#//Customer/accounts"/>
104 <eStructuralFeatures xsi:type="ecore:EReference" name="transactions" upperBound="-1"
105 eType="#//Transaction" containment="true" eOpposite="#//Transaction/account"/>
106 <!-- Derived: Total deposits -->
107 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalDeposits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
108 changeable="false" volatile="true" transient="true" derived="true">
109 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
110 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::DEPOSIT)->collect(amount)->sum()"/>
111 </eAnnotations>
112 </eStructuralFeatures>
113 <!-- Derived: Total withdrawals -->
114 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalWithdrawals" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
115 changeable="false" volatile="true" transient="true" derived="true">
116 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
117 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::WITHDRAWAL)->collect(amount)->sum()"/>
118 </eAnnotations>
119 </eStructuralFeatures>
120 <!-- Operation with pre/post conditions -->
121 <eOperations name="withdraw" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean">
122 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
123 <!-- Precondition: Amount must be positive -->
124 <details key="pre" value="amount > 0"/>
125 <!-- Precondition: Must have sufficient funds -->
126 <details key="pre" value="self.balance >= amount"/>
127 <!-- Postcondition: Balance is reduced -->
128 <details key="post" value="self.balance = self.balance@pre - amount"/>
129 </eAnnotations>
130 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
131 </eOperations>
132 <eOperations name="deposit">
133 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
134 <!-- Precondition: Amount must be positive -->
135 <details key="pre" value="amount > 0"/>
136 <!-- Postcondition: Balance is increased -->
137 <details key="post" value="self.balance = self.balance@pre + amount"/>
138 </eAnnotations>
139 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
140 </eOperations>
141 </eClassifiers>
142
143 <!-- TransactionType: Enum for transaction types -->
144 <eClassifiers xsi:type="ecore:EEnum" name="TransactionType">
145 <eLiterals name="DEPOSIT" value="0"/>
146 <eLiterals name="WITHDRAWAL" value="1"/>
147 <eLiterals name="TRANSFER" value="2"/>
148 </eClassifiers>
149
150 <!-- Transaction: Individual transaction record -->
151 <eClassifiers xsi:type="ecore:EClass" name="Transaction">
152 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
153 <details key="constraints" value="positiveAmount validDescription"/>
154 </eAnnotations>
155 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
156 <!-- Invariant: Transaction amount must be positive -->
157 <details key="positiveAmount" value="self.amount > 0"/>
158 <!-- Invariant: Description must not be empty -->
159 <details key="validDescription" value="self.description.size() > 0"/>
160 </eAnnotations>
161 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionId" lowerBound="1"
162 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
163 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionType" lowerBound="1"
164 eType="#//TransactionType"/>
165 <eStructuralFeatures xsi:type="ecore:EAttribute" name="amount" lowerBound="1"
166 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
167 <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" lowerBound="1"
168 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
169 <eStructuralFeatures xsi:type="ecore:EAttribute" name="timestamp" lowerBound="1"
170 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
171 <eStructuralFeatures xsi:type="ecore:EReference" name="account" lowerBound="1" eType="#//Account"
172 eOpposite="#//Account/transactions"/>
173 </eClassifiers>
174</ecore:EPackage>

Step 4

Value constraints validate numeric ranges.

The positiveBalance constraint on accounts and positiveAmount on transactions ensure financial values are never negative.

bank-account.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="Bank" nsURI="http://www.example.org/bank" nsPrefix="bank">
4
5 <!-- OCL Constraints Documentation -->
6 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
7 <details key="documentation" value="Bank Account metamodel demonstrating OCL constraints including invariants, derivation rules, and pre/post conditions."/>
8 </eAnnotations>
9
10 <!-- Bank: Container for customers and accounts -->
11 <eClassifiers xsi:type="ecore:EClass" name="Bank">
12 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
13 <details key="constraints" value="uniqueAccountNumbers uniqueCustomerIds"/>
14 </eAnnotations>
15 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
16 <!-- Invariant: All account numbers must be unique -->
17 <details key="uniqueAccountNumbers" value="self.accounts->isUnique(accountNumber)"/>
18 <!-- Invariant: All customer IDs must be unique -->
19 <details key="uniqueCustomerIds" value="self.customers->isUnique(customerId)"/>
20 </eAnnotations>
21 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
22 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
23 <eStructuralFeatures xsi:type="ecore:EAttribute" name="swiftCode" lowerBound="1"
24 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
25 <eStructuralFeatures xsi:type="ecore:EReference" name="customers" upperBound="-1"
26 eType="#//Customer" containment="true" eOpposite="#//Customer/bank"/>
27 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
28 eType="#//Account" containment="true" eOpposite="#//Account/bank"/>
29 </eClassifiers>
30
31 <!-- Customer: Bank customer with accounts -->
32 <eClassifiers xsi:type="ecore:EClass" name="Customer">
33 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
34 <details key="constraints" value="validEmail adultCustomer"/>
35 </eAnnotations>
36 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
37 <!-- Invariant: Email must contain @ symbol -->
38 <details key="validEmail" value="self.email.indexOf('@') > 0"/>
39 <!-- Invariant: Customer must be at least 18 years old -->
40 <details key="adultCustomer" value="self.age >= 18"/>
41 </eAnnotations>
42 <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerId" lowerBound="1"
43 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
44 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
45 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
46 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" lowerBound="1"
47 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
48 <eStructuralFeatures xsi:type="ecore:EAttribute" name="age" lowerBound="1"
49 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
50 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
51 eOpposite="#//Bank/customers"/>
52 <eStructuralFeatures xsi:type="ecore:EReference" name="accounts" upperBound="-1"
53 eType="#//Account" eOpposite="#//Account/owner"/>
54 <!-- Derived: Total balance across all accounts -->
55 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalBalance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
56 changeable="false" volatile="true" transient="true" derived="true">
57 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
58 <details key="derivation" value="self.accounts->collect(balance)->sum()"/>
59 </eAnnotations>
60 </eStructuralFeatures>
61 <!-- Derived: Number of active accounts -->
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="activeAccountCount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
63 changeable="false" volatile="true" transient="true" derived="true">
64 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
65 <details key="derivation" value="self.accounts->select(isActive)->size()"/>
66 </eAnnotations>
67 </eStructuralFeatures>
68 </eClassifiers>
69
70 <!-- AccountType: Enum for account types -->
71 <eClassifiers xsi:type="ecore:EEnum" name="AccountType">
72 <eLiterals name="SAVINGS" value="0"/>
73 <eLiterals name="CURRENT" value="1"/>
74 <eLiterals name="FIXED_DEPOSIT" value="2"/>
75 </eClassifiers>
76
77 <!-- Account: Bank account with balance constraints -->
78 <eClassifiers xsi:type="ecore:EClass" name="Account">
79 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
80 <details key="constraints" value="positiveBalance validAccountNumber sufficientMinimumBalance"/>
81 </eAnnotations>
82 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
83 <!-- Invariant: Balance must not be negative -->
84 <details key="positiveBalance" value="self.balance >= 0"/>
85 <!-- Invariant: Account number must be 10 digits -->
86 <details key="validAccountNumber" value="self.accountNumber.size() = 10"/>
87 <!-- Invariant: Savings accounts must maintain minimum balance -->
88 <details key="sufficientMinimumBalance" value="self.accountType = AccountType::SAVINGS implies self.balance >= self.minimumBalance"/>
89 </eAnnotations>
90 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountNumber" lowerBound="1"
91 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
92 <eStructuralFeatures xsi:type="ecore:EAttribute" name="accountType" lowerBound="1"
93 eType="#//AccountType" defaultValueLiteral="SAVINGS"/>
94 <eStructuralFeatures xsi:type="ecore:EAttribute" name="balance" lowerBound="1"
95 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="0.0"/>
96 <eStructuralFeatures xsi:type="ecore:EAttribute" name="minimumBalance"
97 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" defaultValueLiteral="100.0"/>
98 <eStructuralFeatures xsi:type="ecore:EAttribute" name="isActive"
99 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
100 <eStructuralFeatures xsi:type="ecore:EReference" name="bank" eType="#//Bank"
101 eOpposite="#//Bank/accounts"/>
102 <eStructuralFeatures xsi:type="ecore:EReference" name="owner" lowerBound="1" eType="#//Customer"
103 eOpposite="#//Customer/accounts"/>
104 <eStructuralFeatures xsi:type="ecore:EReference" name="transactions" upperBound="-1"
105 eType="#//Transaction" containment="true" eOpposite="#//Transaction/account"/>
106 <!-- Derived: Total deposits -->
107 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalDeposits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
108 changeable="false" volatile="true" transient="true" derived="true">
109 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
110 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::DEPOSIT)->collect(amount)->sum()"/>
111 </eAnnotations>
112 </eStructuralFeatures>
113 <!-- Derived: Total withdrawals -->
114 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalWithdrawals" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
115 changeable="false" volatile="true" transient="true" derived="true">
116 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
117 <details key="derivation" value="self.transactions->select(t | t.transactionType = TransactionType::WITHDRAWAL)->collect(amount)->sum()"/>
118 </eAnnotations>
119 </eStructuralFeatures>
120 <!-- Operation with pre/post conditions -->
121 <eOperations name="withdraw" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean">
122 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
123 <!-- Precondition: Amount must be positive -->
124 <details key="pre" value="amount > 0"/>
125 <!-- Precondition: Must have sufficient funds -->
126 <details key="pre" value="self.balance >= amount"/>
127 <!-- Postcondition: Balance is reduced -->
128 <details key="post" value="self.balance = self.balance@pre - amount"/>
129 </eAnnotations>
130 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
131 </eOperations>
132 <eOperations name="deposit">
133 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
134 <!-- Precondition: Amount must be positive -->
135 <details key="pre" value="amount > 0"/>
136 <!-- Postcondition: Balance is increased -->
137 <details key="post" value="self.balance = self.balance@pre + amount"/>
138 </eAnnotations>
139 <eParameters name="amount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
140 </eOperations>
141 </eClassifiers>
142
143 <!-- TransactionType: Enum for transaction types -->
144 <eClassifiers xsi:type="ecore:EEnum" name="TransactionType">
145 <eLiterals name="DEPOSIT" value="0"/>
146 <eLiterals name="WITHDRAWAL" value="1"/>
147 <eLiterals name="TRANSFER" value="2"/>
148 </eClassifiers>
149
150 <!-- Transaction: Individual transaction record -->
151 <eClassifiers xsi:type="ecore:EClass" name="Transaction">
152 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
153 <details key="constraints" value="positiveAmount validDescription"/>
154 </eAnnotations>
155 <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL">
156 <!-- Invariant: Transaction amount must be positive -->
157 <details key="positiveAmount" value="self.amount > 0"/>
158 <!-- Invariant: Description must not be empty -->
159 <details key="validDescription" value="self.description.size() > 0"/>
160 </eAnnotations>
161 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionId" lowerBound="1"
162 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
163 <eStructuralFeatures xsi:type="ecore:EAttribute" name="transactionType" lowerBound="1"
164 eType="#//TransactionType"/>
165 <eStructuralFeatures xsi:type="ecore:EAttribute" name="amount" lowerBound="1"
166 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
167 <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" lowerBound="1"
168 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
169 <eStructuralFeatures xsi:type="ecore:EAttribute" name="timestamp" lowerBound="1"
170 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
171 <eStructuralFeatures xsi:type="ecore:EReference" name="account" lowerBound="1" eType="#//Account"
172 eOpposite="#//Account/transactions"/>
173 </eClassifiers>
174</ecore:EPackage>

Section 3

Valid Model Instance

A valid model instance satisfies all OCL constraints. Let’s examine a properly structured bank with customers and accounts.

Step 1

Review a valid bank model instance that passes all constraints including unique identifiers, valid emails, adult customers, and proper balances.

valid-instance.xmi
1<?xml version="1.0" encoding="UTF-8"?>
2<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
3 xmlns:bank="http://www.example.org/bank">
4 <!--
5 Valid Bank Instance
6 This model satisfies all OCL constraints:
7 - Unique account numbers and customer IDs
8 - Valid email addresses (contain @)
9 - Adult customers (age >= 18)
10 - Positive account balances
11 - 10-digit account numbers
12 - Savings accounts maintain minimum balance
13 - Positive transaction amounts
14 -->
15 <bank:Bank name="First National Bank" swiftCode="FNBAUS33">
16 <!-- Customer 1: John Smith, adult with valid email -->
17 <customers customerId="CUST001" name="John Smith" email="john.smith@email.com" age="35">
18 <accounts accountNumber="1234567890" accountType="SAVINGS" balance="5000.00" minimumBalance="100.00" isActive="true">
19 <transactions transactionId="TXN001" transactionType="DEPOSIT" amount="1000.00" description="Initial deposit" timestamp="2025-01-01T10:00:00"/>
20 <transactions transactionId="TXN002" transactionType="DEPOSIT" amount="4000.00" description="Salary credit" timestamp="2025-01-15T09:30:00"/>
21 </accounts>
22 </customers>
23
24 <!-- Customer 2: Jane Doe, adult with valid email -->
25 <customers customerId="CUST002" name="Jane Doe" email="jane.doe@company.org" age="28">
26 <accounts accountNumber="0987654321" accountType="CURRENT" balance="12500.00" minimumBalance="0.00" isActive="true">
27 <transactions transactionId="TXN003" transactionType="DEPOSIT" amount="15000.00" description="Business payment" timestamp="2025-01-02T14:00:00"/>
28 <transactions transactionId="TXN004" transactionType="WITHDRAWAL" amount="2500.00" description="Office supplies" timestamp="2025-01-10T11:45:00"/>
29 </accounts>
30 <accounts accountNumber="1122334455" accountType="FIXED_DEPOSIT" balance="50000.00" minimumBalance="0.00" isActive="true">
31 <transactions transactionId="TXN005" transactionType="DEPOSIT" amount="50000.00" description="Fixed deposit" timestamp="2025-01-05T16:00:00"/>
32 </accounts>
33 </customers>
34
35 <!-- Customer 3: Bob Wilson, exactly 18 (minimum adult age) -->
36 <customers customerId="CUST003" name="Bob Wilson" email="bob.wilson@university.edu" age="18">
37 <accounts accountNumber="5566778899" accountType="SAVINGS" balance="500.00" minimumBalance="100.00" isActive="true">
38 <transactions transactionId="TXN006" transactionType="DEPOSIT" amount="500.00" description="Birthday gift" timestamp="2025-01-03T12:00:00"/>
39 </accounts>
40 </customers>
41 </bank:Bank>
42</xmi:XMI>

Section 4

Invalid Model Instance

Understanding constraint violations helps in designing robust models. Let’s examine a model with intentional violations.

Step 1

Review an invalid bank model instance with intentional violations including duplicate IDs, invalid emails, underage customers, negative balances, and malformed account numbers.

invalid-instance.xmi
1<?xml version="1.0" encoding="UTF-8"?>
2<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
3 xmlns:bank="http://www.example.org/bank">
4 <!--
5 Invalid Bank Instance - Contains multiple OCL constraint violations:
6
7 1. VIOLATION: Duplicate account numbers (uniqueAccountNumbers)
8 - Account "1234567890" appears twice
9
10 2. VIOLATION: Duplicate customer IDs (uniqueCustomerIds)
11 - Customer ID "CUST001" appears twice
12
13 3. VIOLATION: Invalid email (validEmail)
14 - "invalid-email" does not contain @
15
16 4. VIOLATION: Underage customer (adultCustomer)
17 - Age 16 is less than 18
18
19 5. VIOLATION: Negative balance (positiveBalance)
20 - Balance -500.00 is negative
21
22 6. VIOLATION: Invalid account number length (validAccountNumber)
23 - "12345" is only 5 digits, not 10
24
25 7. VIOLATION: Savings below minimum (sufficientMinimumBalance)
26 - Savings account with balance 50.00 but minimum is 100.00
27
28 8. VIOLATION: Negative transaction amount (positiveAmount)
29 - Amount -100.00 is negative
30
31 9. VIOLATION: Empty description (validDescription)
32 - Empty description string
33 -->
34 <bank:Bank name="Problem Bank" swiftCode="PROBLM33">
35
36 <!-- VIOLATION: Invalid email (no @ symbol) -->
37 <!-- VIOLATION: Underage customer (16 < 18) -->
38 <!-- VIOLATION: Duplicate customer ID "CUST001" -->
39 <customers customerId="CUST001" name="Tommy Young" email="invalid-email" age="16">
40
41 <!-- VIOLATION: Invalid account number (5 digits instead of 10) -->
42 <!-- VIOLATION: Negative balance -->
43 <accounts accountNumber="12345" accountType="CURRENT" balance="-500.00" minimumBalance="0.00" isActive="true">
44
45 <!-- VIOLATION: Negative transaction amount -->
46 <transactions transactionId="TXN001" transactionType="WITHDRAWAL" amount="-100.00" description="Bad withdrawal" timestamp="2025-01-01T10:00:00"/>
47
48 <!-- VIOLATION: Empty description -->
49 <transactions transactionId="TXN002" transactionType="DEPOSIT" amount="50.00" description="" timestamp="2025-01-02T10:00:00"/>
50 </accounts>
51 </customers>
52
53 <!-- VIOLATION: Duplicate customer ID "CUST001" (same as above) -->
54 <customers customerId="CUST001" name="Sarah Connor" email="sarah@future.com" age="30">
55
56 <!-- VIOLATION: Duplicate account number (same as in valid-instance) -->
57 <accounts accountNumber="1234567890" accountType="SAVINGS" balance="1000.00" minimumBalance="100.00" isActive="true">
58 <transactions transactionId="TXN003" transactionType="DEPOSIT" amount="1000.00" description="Valid deposit" timestamp="2025-01-03T10:00:00"/>
59 </accounts>
60
61 <!-- VIOLATION: Savings account below minimum balance -->
62 <!-- Balance 50.00 is less than minimumBalance 100.00 -->
63 <accounts accountNumber="9999888877" accountType="SAVINGS" balance="50.00" minimumBalance="100.00" isActive="true">
64 <transactions transactionId="TXN004" transactionType="DEPOSIT" amount="50.00" description="Small deposit" timestamp="2025-01-04T10:00:00"/>
65 </accounts>
66 </customers>
67 </bank:Bank>
68</xmi:XMI>

Section 5

Running Validation

Use swift-ecore to validate models against their metamodel constraints. The validator reports which constraints are violated and where.

Step 1

Validate the valid instance.

The validator confirms the model satisfies all constraints.

Terminal
1# Validate the valid bank instance against OCL constraints
2swift-ecore validate \
3 --metamodel bank-account.ecore \
4 --model valid-instance.xmi
5
6# Output:
7# Validating model against metamodel constraints...
8#
9# Checking constraint: uniqueAccountNumbers ... PASSED
10# Checking constraint: uniqueCustomerIds ... PASSED
11# Checking constraint: validEmail (Customer: John Smith) ... PASSED
12# Checking constraint: validEmail (Customer: Jane Doe) ... PASSED
13# Checking constraint: validEmail (Customer: Bob Wilson) ... PASSED
14# Checking constraint: adultCustomer (Customer: John Smith, age=35) ... PASSED
15# Checking constraint: adultCustomer (Customer: Jane Doe, age=28) ... PASSED
16# Checking constraint: adultCustomer (Customer: Bob Wilson, age=18) ... PASSED
17# Checking constraint: positiveBalance (all accounts) ... PASSED
18# Checking constraint: validAccountNumber (all accounts) ... PASSED
19# Checking constraint: sufficientMinimumBalance (savings accounts) ... PASSED
20# Checking constraint: positiveAmount (all transactions) ... PASSED
21# Checking constraint: validDescription (all transactions) ... PASSED
22#
23# Validation complete: All 13 constraints satisfied.
24# Model is VALID.

Step 2

Validate the invalid instance.

The validator reports each constraint violation with the affected element and constraint name.

Terminal
1# Validate the invalid bank instance against OCL constraints
2swift-ecore validate \
3 --metamodel bank-account.ecore \
4 --model invalid-instance.xmi
5
6# Output:
7# Validating model against metamodel constraints...
8#
9# Checking constraint: uniqueAccountNumbers ... FAILED
10# Violation: Duplicate account number '1234567890' found
11#
12# Checking constraint: uniqueCustomerIds ... FAILED
13# Violation: Duplicate customer ID 'CUST001' found
14#
15# Checking constraint: validEmail (Customer: Tommy Young) ... FAILED
16# Violation: Email 'invalid-email' does not contain '@'
17#
18# Checking constraint: adultCustomer (Customer: Tommy Young) ... FAILED
19# Violation: Customer age 16 is less than required minimum 18
20#
21# Checking constraint: positiveBalance (Account: 12345) ... FAILED
22# Violation: Balance -500.00 is negative
23#
24# Checking constraint: validAccountNumber (Account: 12345) ... FAILED
25# Violation: Account number '12345' has 5 digits, expected 10
26#
27# Checking constraint: sufficientMinimumBalance (Account: 9999888877) ... FAILED
28# Violation: Savings balance 50.00 is below minimum 100.00
29#
30# Checking constraint: positiveAmount (Transaction: TXN001) ... FAILED
31# Violation: Transaction amount -100.00 is negative
32#
33# Checking constraint: validDescription (Transaction: TXN002) ... FAILED
34# Violation: Transaction description is empty
35#
36# Validation complete: 9 constraint violations found.
37# Model is INVALID.

Check Your Understanding

Question 1 of 3

What does the OCL expression accounts->isUnique(a | a.accountNumber) check?

Question 2 of 3

How would you express “all customers must have positive account balances”?

Question 3 of 3

When is a constraint considered satisfied?

Complete MDE Workflow: From Metamodel to Code

Experience the full model-driven engineering workflow by building a complete system from metamodel design through code generation.