Integrated Workflows

Model Refactoring Pipeline

Learn how to refactor existing models and metamodels using a systematic pipeline approach.

In this tutorial, you’ll take a legacy metamodel, identify refactoring opportunities, apply systematic transformations, migrate existing instances, and validate the results. This demonstrates how MDE tools support model evolution and maintenance.

75 mins Estimated Time

Section 1

Analysing Legacy Models

Refactoring starts with understanding the current model structure and identifying improvement opportunities. We’ll analyse a legacy customer management system that has grown organically over time.

Systematic analysis reveals structural issues, naming inconsistencies, and missing relationships that need to be addressed through refactoring.

Legacy Model Analysis

Step 1

Examine the legacy metamodel.

This legacy metamodel shows common problems: inconsistent naming, unclear relationships, missing abstractions, and structural issues accumulated over time.

LegacyCustomer.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<!-- Legacy Product/Order Metamodel (Version 1.0) -->
3<!-- This metamodel demonstrates common design issues that need refactoring -->
4<!-- Issues include: God class, missing abstractions, poor naming, denormalisation -->
5<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="LegacyOrders" nsURI="http://www.example.org/legacy/orders/1.0" nsPrefix="legacy">
7
8 <!-- Issue 1: God class with too many responsibilities -->
9 <!-- Issue 2: String-based enumerations instead of proper EEnum -->
10 <!-- Issue 3: Redundant/denormalised data (total calculated at storage) -->
11 <eClassifiers xsi:type="ecore:EClass" name="Order">
12 <eStructuralFeatures xsi:type="ecore:EAttribute" name="orderId" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
13 <eStructuralFeatures xsi:type="ecore:EAttribute" name="orderDate" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
14 <!-- Issue: status as string instead of enum -->
15 <eStructuralFeatures xsi:type="ecore:EAttribute" name="status" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
16 <!-- Issue: Denormalised customer data embedded in order -->
17 <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
18 <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerEmail" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
19 <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerPhone" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
20 <!-- Issue: Address as concatenated string instead of structured -->
21 <eStructuralFeatures xsi:type="ecore:EAttribute" name="shippingAddress" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
22 <eStructuralFeatures xsi:type="ecore:EAttribute" name="billingAddress" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
23 <!-- Issue: Redundant total stored (should be derived) -->
24 <eStructuralFeatures xsi:type="ecore:EAttribute" name="totalAmount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
25 <eStructuralFeatures xsi:type="ecore:EAttribute" name="taxAmount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
26 <!-- Issue: Payment info in order instead of separate entity -->
27 <eStructuralFeatures xsi:type="ecore:EAttribute" name="paymentMethod" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
28 <eStructuralFeatures xsi:type="ecore:EAttribute" name="cardLastFour" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
29 <eStructuralFeatures xsi:type="ecore:EReference" name="items" upperBound="-1" eType="#//OrderItem" containment="true"/>
30 </eClassifiers>
31
32 <!-- Issue 4: Missing link to Product (uses productCode string instead) -->
33 <!-- Issue 5: Poor attribute naming (qty instead of quantity) -->
34 <eClassifiers xsi:type="ecore:EClass" name="OrderItem">
35 <eStructuralFeatures xsi:type="ecore:EAttribute" name="productCode" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
36 <eStructuralFeatures xsi:type="ecore:EAttribute" name="productName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
37 <!-- Issue: Abbreviated name -->
38 <eStructuralFeatures xsi:type="ecore:EAttribute" name="qty" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
39 <eStructuralFeatures xsi:type="ecore:EAttribute" name="unitPrice" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
40 <!-- Issue: Redundant line total stored -->
41 <eStructuralFeatures xsi:type="ecore:EAttribute" name="lineTotal" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
42 </eClassifiers>
43
44 <!-- Issue 6: Product with mixed concerns (inventory data in product) -->
45 <!-- Issue 7: Category as string instead of reference -->
46 <eClassifiers xsi:type="ecore:EClass" name="Product">
47 <eStructuralFeatures xsi:type="ecore:EAttribute" name="code" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
48 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
49 <eStructuralFeatures xsi:type="ecore:EAttribute" name="desc" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
50 <eStructuralFeatures xsi:type="ecore:EAttribute" name="price" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/>
51 <!-- Issue: Category as string -->
52 <eStructuralFeatures xsi:type="ecore:EAttribute" name="category" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
53 <!-- Issue: Inventory concerns mixed with product -->
54 <eStructuralFeatures xsi:type="ecore:EAttribute" name="stockQty" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
55 <eStructuralFeatures xsi:type="ecore:EAttribute" name="reorderLevel" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
56 <eStructuralFeatures xsi:type="ecore:EAttribute" name="warehouseLocation" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
57 </eClassifiers>
58
59 <!-- Root container with flat structure -->
60 <eClassifiers xsi:type="ecore:EClass" name="Store">
61 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
62 <eStructuralFeatures xsi:type="ecore:EReference" name="orders" upperBound="-1" eType="#//Order" containment="true"/>
63 <eStructuralFeatures xsi:type="ecore:EReference" name="products" upperBound="-1" eType="#//Product" containment="true"/>
64 </eClassifiers>
65</ecore:EPackage>

Step 2

Create sample legacy instances.

These instances represent existing data in the legacy format. They demonstrate the complexity and inconsistencies that refactoring aims to resolve.

legacy-data.xmi
1<?xml version="1.0" encoding="UTF-8"?>
2<!-- Legacy Order/Product Model Instances -->
3<!-- Sample data demonstrating issues in the legacy metamodel -->
4<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
5 xmlns="http://www.example.org/legacy/orders/1.0">
6 <Store name="Acme Electronics">
7 <!-- Products with mixed inventory data -->
8 <products code="LAPTOP-001" name="ProBook 15" desc="15-inch professional laptop"
9 price="1299.99" category="Electronics/Computers" stockQty="45"
10 reorderLevel="10" warehouseLocation="A-12-3"/>
11 <products code="LAPTOP-002" name="UltraBook Air" desc="Lightweight ultraportable laptop"
12 price="999.99" category="Electronics/Computers" stockQty="30"
13 reorderLevel="8" warehouseLocation="A-12-4"/>
14 <products code="PHONE-001" name="SmartPhone X" desc="Latest smartphone model"
15 price="899.00" category="Electronics/Mobile" stockQty="120"
16 reorderLevel="25" warehouseLocation="B-05-1"/>
17 <products code="CABLE-001" name="USB-C Cable" desc="2m USB-C charging cable"
18 price="19.99" category="Accessories/Cables" stockQty="500"
19 reorderLevel="100" warehouseLocation="C-01-1"/>
20 <products code="CASE-001" name="Laptop Sleeve 15" desc="Protective sleeve for 15-inch laptops"
21 price="49.99" category="Accessories/Cases" stockQty="80"
22 reorderLevel="20" warehouseLocation="C-02-1"/>
23
24 <!-- Order with denormalised customer data and string-based status -->
25 <orders orderId="ORD-2024-001" orderDate="2024-01-15"
26 status="SHIPPED"
27 customerName="John Smith" customerEmail="john.smith@example.com" customerPhone="+61 2 9876 5432"
28 shippingAddress="123 Main St, Sydney NSW 2000, Australia"
29 billingAddress="123 Main St, Sydney NSW 2000, Australia"
30 totalAmount="1369.98" taxAmount="136.99"
31 paymentMethod="CREDIT_CARD" cardLastFour="4532">
32 <items productCode="LAPTOP-001" productName="ProBook 15" qty="1" unitPrice="1299.99" lineTotal="1299.99"/>
33 <items productCode="CABLE-001" productName="USB-C Cable" qty="2" unitPrice="19.99" lineTotal="39.98"/>
34 <items productCode="CASE-001" productName="Laptop Sleeve 15" qty="1" unitPrice="49.99" lineTotal="49.99"/>
35 </orders>
36
37 <orders orderId="ORD-2024-002" orderDate="2024-01-16"
38 status="PENDING"
39 customerName="Sarah Johnson" customerEmail="s.johnson@company.com.au" customerPhone="+61 3 1234 5678"
40 shippingAddress="456 Business Park, Melbourne VIC 3000, Australia"
41 billingAddress="PO Box 789, Melbourne VIC 3001, Australia"
42 totalAmount="1898.00" taxAmount="189.80"
43 paymentMethod="INVOICE" cardLastFour="">
44 <items productCode="PHONE-001" productName="SmartPhone X" qty="2" unitPrice="899.00" lineTotal="1798.00"/>
45 <items productCode="CABLE-001" productName="USB-C Cable" qty="5" unitPrice="19.99" lineTotal="99.95"/>
46 </orders>
47
48 <orders orderId="ORD-2024-003" orderDate="2024-01-17"
49 status="DELIVERED"
50 customerName="Tech Solutions Pty Ltd" customerEmail="orders@techsolutions.com.au" customerPhone="+61 7 9999 8888"
51 shippingAddress="789 Tech Drive, Brisbane QLD 4000, Australia"
52 billingAddress="789 Tech Drive, Brisbane QLD 4000, Australia"
53 totalAmount="10999.90" taxAmount="1099.99"
54 paymentMethod="BANK_TRANSFER" cardLastFour="">
55 <items productCode="LAPTOP-002" productName="UltraBook Air" qty="10" unitPrice="999.99" lineTotal="9999.90"/>
56 <items productCode="CABLE-001" productName="USB-C Cable" qty="50" unitPrice="19.99" lineTotal="999.50"/>
57 </orders>
58
59 <!-- Order demonstrating data quality issues -->
60 <orders orderId="ORD-2024-004" orderDate="2024-01-18"
61 status="pending"
62 customerName="Jane Doe" customerEmail="jane.doe@email.com" customerPhone="0412345678"
63 shippingAddress="Unit 5/22 George St, Perth WA 6000"
64 billingAddress="Unit 5/22 George St, Perth WA 6000"
65 totalAmount="949.98" taxAmount="94.99"
66 paymentMethod="paypal" cardLastFour="">
67 <items productCode="PHONE-001" productName="SmartPhone X" qty="1" unitPrice="899.00" lineTotal="899.00"/>
68 <items productCode="CABLE-001" productName="USB-C Cable" qty="3" unitPrice="19.99" lineTotal="59.97"/>
69 </orders>
70 </Store>
71</xmi:XMI>

Step 3

Analyse structural issues with AQL.

Use AQL queries to identify structural problems, data quality issues, and opportunities for improvement in the legacy model.

Terminal
1# Analyse legacy metamodel for design issues
2# Uses swift-ecore to identify potential problems
3
4# Validate the legacy metamodel structure
5swift-ecore validate legacy-orders-v1.ecore
6
7# Output:
8# Validation complete. Found 8 potential issues:
9#
10# [WARNING] Order: Class has 14 attributes (recommended max: 7)
11# - Consider extracting related attributes into separate classes
12#
13# [WARNING] Order.status: String type used for enumerable values
14# - Consider using EEnum for: PENDING, SHIPPED, DELIVERED, CANCELLED
15#
16# [WARNING] Order: Contains denormalised customer data
17# - customerName, customerEmail, customerPhone should reference Customer class
18#
19# [WARNING] Order: Address stored as single string
20# - shippingAddress, billingAddress should use structured Address class
21#
22# [WARNING] OrderItem: No reference to Product class
23# - productCode string duplicates Product.code relationship
24#
25# [WARNING] OrderItem.qty: Abbreviated attribute name
26# - Consider renaming to 'quantity' for clarity
27#
28# [WARNING] Product: Mixed concerns detected
29# - Inventory attributes (stockQty, reorderLevel, warehouseLocation)
30# should be in separate InventoryItem class
31#
32# [WARNING] Product.desc: Abbreviated attribute name
33# - Consider renaming to 'description' for clarity
34
35# Generate detailed analysis report
36swift-ecore analyse legacy-orders-v1.ecore \
37 --report-format markdown \
38 --output analysis-report.md
39
40# Check for circular dependencies
41swift-ecore check-deps legacy-orders-v1.ecore
42
43# Output:
44# No circular dependencies found.
45# Reference graph: Store -> Order -> OrderItem
46# Store -> Product

Step 4

Document refactoring goals.

Create a systematic plan that identifies specific refactoring goals, success criteria, and validation approaches to ensure the refactoring improves model quality.

refactoring-plan.md
1# Model Refactoring Plan
2
3## Executive Summary
4
5This document outlines the systematic refactoring of the LegacyOrders metamodel (v1.0) to create an improved Orders metamodel (v2.0). The refactoring addresses structural issues, naming inconsistencies, and design problems that have accumulated over time.
6
7## Scope and Objectives
8
9### Primary Goals
10
111. **Eliminate God Classes**: Break down the monolithic `Order` class into focused, cohesive entities
122. **Standardise Naming Conventions**: Apply consistent Australian English naming throughout
133. **Introduce Proper Abstractions**: Add base classes and interfaces where appropriate
144. **Normalise Data Structures**: Remove denormalised data and redundant attributes
155. **Strengthen Type Safety**: Replace string-based enumerations with proper EEnum types
16
17### Success Criteria
18
19- All validation tests pass on the improved metamodel
20- 100% of legacy instances successfully migrate
21- No data loss during migration
22- Improved metamodel scores higher on quality metrics
23- Backwards compatibility maintained for transition period
24
25## Identified Issues
26
27### Issue 1: God Class (Order)
28
29**Problem**: The `Order` class contains 14 attributes spanning multiple concerns:
30- Customer information (name, email, phone)
31- Address details (shipping, billing as strings)
32- Payment information (method, card details)
33- Order metadata (id, date, status)
34
35**Solution**: Extract into separate classes:
36- `Customer` for customer details
37- `Address` for structured address information
38- `Payment` for payment details
39- `Order` retains only order-specific attributes
40
41### Issue 2: String-Based Enumerations
42
43**Problem**: Status and payment method stored as strings, allowing invalid values.
44
45**Solution**: Create proper EEnum types:
46- `OrderStatus`: PENDING, CONFIRMED, PROCESSING, SHIPPED, DELIVERED, CANCELLED
47- `PaymentMethod`: CREDIT_CARD, DEBIT_CARD, BANK_TRANSFER, PAYPAL, INVOICE
48
49### Issue 3: Denormalised Data
50
51**Problem**: Customer data duplicated in every order; product data duplicated in order items.
52
53**Solution**: Create proper references:
54- Orders reference Customers (many-to-one)
55- OrderItems reference Products (many-to-one)
56
57### Issue 4: Missing Abstractions
58
59**Problem**: No shared base for named entities; code duplication for name attributes.
60
61**Solution**: Introduce `NamedElement` abstract base class.
62
63### Issue 5: Mixed Concerns in Product
64
65**Problem**: Product class contains inventory attributes (stockQty, reorderLevel, warehouseLocation).
66
67**Solution**: Extract inventory into separate `InventoryItem` class.
68
69### Issue 6: Abbreviated Attribute Names
70
71**Problem**: `qty`, `desc` instead of clear names.
72
73**Solution**: Rename to `quantity`, `description`.
74
75### Issue 7: Unstructured Address Data
76
77**Problem**: Addresses stored as concatenated strings.
78
79**Solution**: Create structured `Address` class with:
80- streetAddress
81- suburb
82- state
83- postcode
84- country
85
86## Inheritance Hierarchy Improvements
87
88### Before (Flat Structure)
89```
90Order (standalone)
91OrderItem (standalone)
92Product (standalone)
93Store (standalone)
94```
95
96### After (Proper Hierarchy)
97```
98NamedElement (abstract)
99 ├── Customer
100 ├── Category
101 ├── Product
102 ├── Warehouse
103 └── Store
104
105Address (value object)
106Payment (embedded entity)
107InventoryItem (associated entity)
108OrderItem (embedded entity)
109Order (aggregate root)
110```
111
112## Naming Convention Guidelines
113
114### Australian English Spelling
115- organisation (not organization)
116- colour (not color)
117- standardising (not standardizing)
118- behaviour (not behavior)
119- centre (not center)
120
121### Attribute Naming
122- Use full words: `quantity` not `qty`
123- Use descriptive names: `description` not `desc`
124- Use camelCase for multi-word attributes
125- Required attributes should have `lowerBound="1"`
126
127### Class Naming
128- Use PascalCase for class names
129- Use singular nouns for entity classes
130- Use descriptive names that indicate purpose
131
132## Validation Approach
133
134### Structural Validation
135- Metamodel well-formedness
136- Reference integrity
137- Multiplicity constraints
138
139### Data Validation
140- No data loss verification
141- Value mapping correctness
142- Relationship preservation
143
144### Quality Metrics
145- Class cohesion improvement
146- Reduced data duplication
147- Improved normalisation score
148
149## Timeline
150
151| Phase | Duration | Activities |
152|-------|----------|------------|
153| Analysis | 1 week | Review legacy model, document issues |
154| Design | 1 week | Create improved metamodel, mapping docs |
155| Development | 2 weeks | Build transformations, validation rules |
156| Testing | 1 week | Execute migrations, validate results |
157| Rollout | 1 week | Deploy, monitor, support |
158
159## Risk Mitigation
160
161### Risk: Data Loss During Migration
162**Mitigation**: Comprehensive backup before migration; validation after each step.
163
164### Risk: Tool Breakage
165**Mitigation**: Maintain backwards compatibility transformations during transition.
166
167### Risk: Performance Degradation
168**Mitigation**: Test with production-scale data before deployment.
169
170## Approval
171
172| Role | Name | Date | Signature |
173|------|------|------|-----------|
174| Technical Lead | _____________ | ____/____/____ | _________ |
175| Architect | _____________ | ____/____/____ | _________ |
176| Product Owner | _____________ | ____/____/____ | _________ |

Section 2

Designing the Target Model

Based on the analysis, design an improved metamodel that addresses identified issues while maintaining compatibility with existing data and processes.

The target model should follow best practices for naming, structure, and relationships while solving the specific problems identified in the legacy system.

Step 1

Design the improved metamodel structure.

This metamodel addresses the legacy issues: consistent naming conventions, clear abstractions, proper inheritance hierarchies, and well-defined relationships.

ImprovedCustomer.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<!-- Improved Customer Management Metamodel (Version 2.0) -->
3<!-- Refactored to address design issues from legacy version -->
4<!-- Uses Australian English spelling conventions throughout -->
5<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="CustomerManagement"
7 nsURI="http://www.example.org/customer/2.0" nsPrefix="customer">
8
9 <!-- Proper enumeration for organisation type -->
10 <eClassifiers xsi:type="ecore:EEnum" name="OrganisationType">
11 <eLiterals name="PRIVATE_COMPANY" value="0"/>
12 <eLiterals name="PUBLIC_COMPANY" value="1"/>
13 <eLiterals name="GOVERNMENT" value="2"/>
14 <eLiterals name="NON_PROFIT" value="3"/>
15 <eLiterals name="PARTNERSHIP" value="4"/>
16 <eLiterals name="SOLE_TRADER" value="5"/>
17 </eClassifiers>
18
19 <!-- Customer status enumeration -->
20 <eClassifiers xsi:type="ecore:EEnum" name="CustomerStatus">
21 <eLiterals name="PROSPECT" value="0"/>
22 <eLiterals name="ACTIVE" value="1"/>
23 <eLiterals name="INACTIVE" value="2"/>
24 <eLiterals name="SUSPENDED" value="3"/>
25 <eLiterals name="CLOSED" value="4"/>
26 </eClassifiers>
27
28 <!-- Contact type enumeration -->
29 <eClassifiers xsi:type="ecore:EEnum" name="ContactType">
30 <eLiterals name="PRIMARY" value="0"/>
31 <eLiterals name="BILLING" value="1"/>
32 <eLiterals name="TECHNICAL" value="2"/>
33 <eLiterals name="EMERGENCY" value="3"/>
34 </eClassifiers>
35
36 <!-- Address type enumeration -->
37 <eClassifiers xsi:type="ecore:EEnum" name="AddressType">
38 <eLiterals name="REGISTERED" value="0"/>
39 <eLiterals name="TRADING" value="1"/>
40 <eLiterals name="POSTAL" value="2"/>
41 <eLiterals name="DELIVERY" value="3"/>
42 </eClassifiers>
43
44 <!-- Abstract base class for named entities -->
45 <eClassifiers xsi:type="ecore:EClass" name="NamedElement" abstract="true">
46 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1"
47 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
48 <eStructuralFeatures xsi:type="ecore:EAttribute" name="description"
49 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
50 </eClassifiers>
51
52 <!-- Abstract base class for identifiable entities -->
53 <eClassifiers xsi:type="ecore:EClass" name="IdentifiableElement" abstract="true" eSuperTypes="#//NamedElement">
54 <eStructuralFeatures xsi:type="ecore:EAttribute" name="identifier" lowerBound="1"
55 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString">
56 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
57 <details key="documentation" value="Unique identifier for this entity"/>
58 </eAnnotations>
59 </eStructuralFeatures>
60 <eStructuralFeatures xsi:type="ecore:EAttribute" name="createdAt"
61 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDate"/>
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="modifiedAt"
63 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDate"/>
64 </eClassifiers>
65
66 <!-- Structured address class -->
67 <eClassifiers xsi:type="ecore:EClass" name="Address">
68 <eStructuralFeatures xsi:type="ecore:EAttribute" name="addressType" lowerBound="1"
69 eType="#//AddressType" defaultValueLiteral="TRADING"/>
70 <eStructuralFeatures xsi:type="ecore:EAttribute" name="streetAddress" lowerBound="1"
71 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
72 <eStructuralFeatures xsi:type="ecore:EAttribute" name="suburb" lowerBound="1"
73 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
74 <eStructuralFeatures xsi:type="ecore:EAttribute" name="state" lowerBound="1"
75 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
76 <eStructuralFeatures xsi:type="ecore:EAttribute" name="postcode" lowerBound="1"
77 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
78 <eStructuralFeatures xsi:type="ecore:EAttribute" name="country" lowerBound="1"
79 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
80 defaultValueLiteral="Australia"/>
81 </eClassifiers>
82
83 <!-- Contact person class -->
84 <eClassifiers xsi:type="ecore:EClass" name="Contact" eSuperTypes="#//NamedElement">
85 <eStructuralFeatures xsi:type="ecore:EAttribute" name="contactType" lowerBound="1"
86 eType="#//ContactType" defaultValueLiteral="PRIMARY"/>
87 <eStructuralFeatures xsi:type="ecore:EAttribute" name="email" lowerBound="1"
88 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
89 <eStructuralFeatures xsi:type="ecore:EAttribute" name="phone"
90 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
91 <eStructuralFeatures xsi:type="ecore:EAttribute" name="mobile"
92 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
93 <eStructuralFeatures xsi:type="ecore:EAttribute" name="position"
94 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
95 <eStructuralFeatures xsi:type="ecore:EReference" name="organisation"
96 eType="#//Organisation" eOpposite="#//Organisation/contacts"/>
97 </eClassifiers>
98
99 <!-- Organisation class (proper Australian English spelling) -->
100 <eClassifiers xsi:type="ecore:EClass" name="Organisation" eSuperTypes="#//IdentifiableElement">
101 <eStructuralFeatures xsi:type="ecore:EAttribute" name="organisationType" lowerBound="1"
102 eType="#//OrganisationType" defaultValueLiteral="PRIVATE_COMPANY"/>
103 <eStructuralFeatures xsi:type="ecore:EAttribute" name="tradingName"
104 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
105 <eStructuralFeatures xsi:type="ecore:EAttribute" name="australianBusinessNumber"
106 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString">
107 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
108 <details key="documentation" value="ABN - Australian Business Number"/>
109 </eAnnotations>
110 </eStructuralFeatures>
111 <eStructuralFeatures xsi:type="ecore:EAttribute" name="australianCompanyNumber"
112 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString">
113 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
114 <details key="documentation" value="ACN - Australian Company Number"/>
115 </eAnnotations>
116 </eStructuralFeatures>
117 <eStructuralFeatures xsi:type="ecore:EReference" name="addresses" upperBound="-1"
118 eType="#//Address" containment="true"/>
119 <eStructuralFeatures xsi:type="ecore:EReference" name="contacts" upperBound="-1"
120 eType="#//Contact" containment="true" eOpposite="#//Contact/organisation"/>
121 <eStructuralFeatures xsi:type="ecore:EReference" name="customers" upperBound="-1"
122 eType="#//Customer" eOpposite="#//Customer/organisation"/>
123 </eClassifiers>
124
125 <!-- Customer class with proper references -->
126 <eClassifiers xsi:type="ecore:EClass" name="Customer" eSuperTypes="#//IdentifiableElement">
127 <eStructuralFeatures xsi:type="ecore:EAttribute" name="status" lowerBound="1"
128 eType="#//CustomerStatus" defaultValueLiteral="PROSPECT"/>
129 <eStructuralFeatures xsi:type="ecore:EAttribute" name="creditLimit"
130 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBigDecimal"/>
131 <eStructuralFeatures xsi:type="ecore:EAttribute" name="paymentTermsDays"
132 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
133 defaultValueLiteral="30"/>
134 <eStructuralFeatures xsi:type="ecore:EReference" name="organisation" lowerBound="1"
135 eType="#//Organisation" eOpposite="#//Organisation/customers"/>
136 <eStructuralFeatures xsi:type="ecore:EReference" name="primaryContact"
137 eType="#//Contact"/>
138 <eStructuralFeatures xsi:type="ecore:EReference" name="billingAddress"
139 eType="#//Address"/>
140 <eStructuralFeatures xsi:type="ecore:EReference" name="deliveryAddress"
141 eType="#//Address"/>
142 <eStructuralFeatures xsi:type="ecore:EReference" name="category"
143 eType="#//CustomerCategory" eOpposite="#//CustomerCategory/customers"/>
144 <eStructuralFeatures xsi:type="ecore:EReference" name="notes" upperBound="-1"
145 eType="#//CustomerNote" containment="true" eOpposite="#//CustomerNote/customer"/>
146 </eClassifiers>
147
148 <!-- Customer category for classification -->
149 <eClassifiers xsi:type="ecore:EClass" name="CustomerCategory" eSuperTypes="#//NamedElement">
150 <eStructuralFeatures xsi:type="ecore:EAttribute" name="code" lowerBound="1"
151 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
152 <eStructuralFeatures xsi:type="ecore:EAttribute" name="discountPercentage"
153 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBigDecimal"
154 defaultValueLiteral="0"/>
155 <eStructuralFeatures xsi:type="ecore:EReference" name="parent"
156 eType="#//CustomerCategory"/>
157 <eStructuralFeatures xsi:type="ecore:EReference" name="customers" upperBound="-1"
158 eType="#//Customer" eOpposite="#//Customer/category"/>
159 </eClassifiers>
160
161 <!-- Customer notes for tracking interactions -->
162 <eClassifiers xsi:type="ecore:EClass" name="CustomerNote">
163 <eStructuralFeatures xsi:type="ecore:EAttribute" name="content" lowerBound="1"
164 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
165 <eStructuralFeatures xsi:type="ecore:EAttribute" name="createdAt" lowerBound="1"
166 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDate"/>
167 <eStructuralFeatures xsi:type="ecore:EAttribute" name="createdBy"
168 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
169 <eStructuralFeatures xsi:type="ecore:EReference" name="customer" lowerBound="1"
170 eType="#//Customer" eOpposite="#//Customer/notes"/>
171 </eClassifiers>
172
173 <!-- Root container with organised structure -->
174 <eClassifiers xsi:type="ecore:EClass" name="CustomerManagementSystem" eSuperTypes="#//NamedElement">
175 <eStructuralFeatures xsi:type="ecore:EReference" name="organisations" upperBound="-1"
176 eType="#//Organisation" containment="true"/>
177 <eStructuralFeatures xsi:type="ecore:EReference" name="customers" upperBound="-1"
178 eType="#//Customer" containment="true"/>
179 <eStructuralFeatures xsi:type="ecore:EReference" name="categories" upperBound="-1"
180 eType="#//CustomerCategory" containment="true"/>
181 </eClassifiers>
182</ecore:EPackage>

Step 2

Validate the target design.

Ensure the improved metamodel is well-formed and addresses the identified issues without introducing new problems.

Terminal
1# Validate the improved metamodel using swift-ecore
2# Ensures the target metamodel is well-formed before migration
3
4# Validate metamodel structure
5swift-ecore validate ImprovedCustomer.ecore --verbose
6
7# Output:
8# Validating metamodel: ImprovedCustomer.ecore
9# =============================================
10#
11# Package: CustomerManagement
12# Namespace URI: http://www.example.org/customer/2.0
13# Namespace Prefix: customer
14#
15# Checking class structure...
16# [OK] NamedElement (abstract)
17# [OK] IdentifiableElement (abstract, extends NamedElement)
18# [OK] Address
19# [OK] Contact (extends NamedElement)
20# [OK] Organisation (extends IdentifiableElement)
21# [OK] Customer (extends IdentifiableElement)
22# [OK] CustomerCategory (extends NamedElement)
23# [OK] CustomerNote
24# [OK] CustomerManagementSystem (extends NamedElement)
25#
26# Checking enumerations...
27# [OK] OrganisationType: 6 literals
28# [OK] CustomerStatus: 5 literals
29# [OK] ContactType: 4 literals
30# [OK] AddressType: 4 literals
31#
32# Checking references...
33# [OK] Contact.organisation <-> Organisation.contacts (bidirectional)
34# [OK] Customer.organisation <-> Organisation.customers (bidirectional)
35# [OK] Customer.category <-> CustomerCategory.customers (bidirectional)
36# [OK] CustomerNote.customer <-> Customer.notes (bidirectional)
37#
38# Validation Summary:
39# Classes: 9 (2 abstract)
40# Enumerations: 4
41# Attributes: 28
42# References: 18 (8 bidirectional pairs)
43# Containments: 6
44#
45# Result: VALID - No errors found
46
47# Check for design best practices
48swift-ecore analyse ImprovedCustomer.ecore --best-practices
49
50# Output:
51# Design Best Practices Analysis
52# ==============================
53#
54# Naming Conventions:
55# [OK] All class names use PascalCase
56# [OK] All attribute names use camelCase
57# [OK] Australian English spelling used consistently
58# - organisation (not organization)
59# - standardising (not standardizing)
60#
61# Structure Analysis:
62# [OK] Abstract base classes provide shared functionality
63# [OK] Proper inheritance hierarchy established
64# [OK] No God classes detected (max 9 features per class)
65# [OK] All enumerations have at least 2 literals
66#
67# Reference Quality:
68# [OK] All bidirectional references have eOpposite defined
69# [OK] Containment references properly configured
70# [OK] No orphan references detected
71#
72# Required Attributes:
73# [OK] All required attributes have lowerBound="1"
74# [OK] Appropriate default values specified
75#
76# Documentation:
77# [INFO] 2 attributes have documentation annotations
78# [SUGGESTION] Consider adding documentation to all public attributes
79#
80# Score: 95/100 (Excellent)
81
82# Compare with legacy metamodel structure
83swift-ecore compare \
84 LegacyCustomer.ecore \
85 ImprovedCustomer.ecore \
86 --summary
87
88# Output:
89# Metamodel Comparison Summary
90# ============================
91#
92# Legacy: LegacyCustomer (v1.0)
93# Target: CustomerManagement (v2.0)
94#
95# Improvements Made:
96# + Added abstract base classes (NamedElement, IdentifiableElement)
97# + Created proper enumerations (4 new EEnums)
98# + Extracted Address as structured class
99# + Extracted Contact as separate entity
100# + Separated Organisation from Customer
101# + Added CustomerCategory for classification
102# + Added CustomerNote for tracking
103#
104# Structural Metrics:
105# Legacy Improved Change
106# Classes: 3 9 +200%
107# Abstract classes: 0 2 +2
108# Enumerations: 0 4 +4
109# Attributes/class: 12.3 3.1 -75%
110# References: 2 18 +800%
111#
112# Quality Improvements:
113# - God class eliminated (legacy Customer had 15 attributes)
114# - String enums replaced with proper EEnum types
115# - Denormalised data normalised
116# - Proper abstractions introduced
117#
118# Migration Complexity: MODERATE
119# Estimated effort: 2-3 days for transformation development

Step 3

Create mapping documentation.

Document how legacy concepts map to improved concepts. This guides transformation development and helps stakeholders understand the changes.

legacy-to-improved-mapping.md
1# Legacy to Improved Model Mapping
2
3## Overview
4
5This document describes how concepts from the legacy Customer metamodel (v1.0) map to the improved CustomerManagement metamodel (v2.0). These mappings guide the ATL transformation development and help stakeholders understand the refactoring changes.
6
7## Class Mappings
8
9### Legacy Customer -> Multiple Target Classes
10
11The legacy `Customer` class was a God class containing all customer-related data. It has been decomposed into:
12
13| Legacy Attribute | Target Class | Target Attribute | Notes |
14|------------------|--------------|------------------|-------|
15| `id` | Customer | `identifier` | Renamed for consistency |
16| `name` | Customer | `name` | Inherited from NamedElement |
17| `companyName` | Organisation | `name` | Extracted to separate class |
18| `companyType` | Organisation | `organisationType` | Converted to enum |
19| `abn` | Organisation | `australianBusinessNumber` | Expanded name |
20| `acn` | Organisation | `australianCompanyNumber` | Expanded name |
21| `contactName` | Contact | `name` | Extracted to separate class |
22| `contactEmail` | Contact | `email` | Extracted to separate class |
23| `contactPhone` | Contact | `phone` | Extracted to separate class |
24| `streetAddress` | Address | `streetAddress` | Structured address |
25| `city` | Address | `suburb` | Renamed (Australian terminology) |
26| `state` | Address | `state` | Direct mapping |
27| `postcode` | Address | `postcode` | Direct mapping |
28| `country` | Address | `country` | Direct mapping |
29| `status` | Customer | `status` | Converted to enum |
30| `creditLimit` | Customer | `creditLimit` | Direct mapping |
31| `notes` | CustomerNote | `content` | Extracted to separate class |
32
33### Legacy CustomerType (String) -> OrganisationType (Enum)
34
35| Legacy Value | Target Enum Literal |
36|--------------|---------------------|
37| `"Company"` | `PRIVATE_COMPANY` |
38| `"Public"` | `PUBLIC_COMPANY` |
39| `"Govt"`, `"Government"` | `GOVERNMENT` |
40| `"NFP"`, `"Non-Profit"` | `NON_PROFIT` |
41| `"Partnership"` | `PARTNERSHIP` |
42| `"Individual"`, `"Sole Trader"` | `SOLE_TRADER` |
43
44### Legacy Status (String) -> CustomerStatus (Enum)
45
46| Legacy Value | Target Enum Literal |
47|--------------|---------------------|
48| `"New"`, `"Prospect"` | `PROSPECT` |
49| `"Active"`, `"Current"` | `ACTIVE` |
50| `"Inactive"`, `"Dormant"` | `INACTIVE` |
51| `"Suspended"`, `"Hold"` | `SUSPENDED` |
52| `"Closed"`, `"Deleted"` | `CLOSED` |
53
54## Structural Changes
55
56### Address Extraction
57
58**Before (Legacy):**
59```
60Customer
61 ├── streetAddress: String
62 ├── city: String
63 ├── state: String
64 ├── postcode: String
65 └── country: String
66```
67
68**After (Improved):**
69```
70Customer
71 ├── billingAddress: Address
72 └── deliveryAddress: Address
73
74Address
75 ├── addressType: AddressType
76 ├── streetAddress: String
77 ├── suburb: String
78 ├── state: String
79 ├── postcode: String
80 └── country: String
81```
82
83**Migration Logic:**
841. Parse legacy address fields into Address object
852. Set `suburb` from legacy `city` (terminology change)
863. Default `addressType` to `TRADING`
874. Default `country` to "Australia" if empty
885. Assign same address to both `billingAddress` and `deliveryAddress` initially
89
90### Organisation Extraction
91
92**Before (Legacy):**
93```
94Customer
95 ├── companyName: String
96 ├── companyType: String
97 ├── abn: String
98 └── acn: String
99```
100
101**After (Improved):**
102```
103Organisation
104 ├── name: String (inherited)
105 ├── organisationType: OrganisationType
106 ├── tradingName: String
107 ├── australianBusinessNumber: String
108 └── australianCompanyNumber: String
109
110Customer
111 └── organisation: Organisation [1]
112```
113
114**Migration Logic:**
1151. Create Organisation for each unique company
1162. Deduplicate by ABN (Australian Business Number)
1173. Map `companyType` string to `OrganisationType` enum
1184. Link multiple Customers to same Organisation if ABN matches
119
120### Contact Extraction
121
122**Before (Legacy):**
123```
124Customer
125 ├── contactName: String
126 ├── contactEmail: String
127 └── contactPhone: String
128```
129
130**After (Improved):**
131```
132Contact
133 ├── name: String (inherited)
134 ├── contactType: ContactType
135 ├── email: String
136 ├── phone: String
137 ├── mobile: String
138 └── position: String
139
140Organisation
141 └── contacts: Contact [*]
142
143Customer
144 └── primaryContact: Contact
145```
146
147**Migration Logic:**
1481. Create Contact for each Customer's contact details
1492. Set `contactType` to `PRIMARY`
1503. Add Contact to Organisation's contacts collection
1514. Set as Customer's `primaryContact`
152
153### Notes Extraction
154
155**Before (Legacy):**
156```
157Customer
158 └── notes: String
159```
160
161**After (Improved):**
162```
163CustomerNote
164 ├── content: String
165 ├── createdAt: Date
166 └── createdBy: String
167
168Customer
169 └── notes: CustomerNote [*]
170```
171
172**Migration Logic:**
1731. If legacy `notes` is non-empty, create CustomerNote
1742. Set `content` to legacy notes value
1753. Set `createdAt` to migration timestamp
1764. Set `createdBy` to "Migration"
177
178## Identifier Mappings
179
180### Primary Keys
181
182| Legacy | Improved | Generation Strategy |
183|--------|----------|---------------------|
184| `Customer.id` | `Customer.identifier` | Direct copy |
185| (none) | `Organisation.identifier` | Generate from ABN or UUID |
186| (none) | `Contact.identifier` | Generate UUID |
187| (none) | `CustomerCategory.code` | Generate from name |
188
189### Foreign Key Relationships
190
191| Legacy Pattern | Improved Pattern |
192|----------------|------------------|
193| Embedded data | Reference to shared object |
194| String matching | Object reference |
195| Denormalised copy | Single source of truth |
196
197## Data Quality Transformations
198
199### Standardisation Rules
200
2011. **Phone Numbers**: Normalise to format "+61 X XXXX XXXX"
2022. **Postcodes**: Ensure 4 digits, zero-pad if needed
2033. **States**: Standardise to uppercase abbreviations (NSW, VIC, QLD, etc.)
2044. **Country**: Default to "Australia" if empty or "AU"
2055. **Names**: Trim whitespace, title case
206
207### Deduplication Rules
208
2091. **Organisations**: Match by ABN, then by exact name match
2102. **Addresses**: Match by all fields (normalised)
2113. **Contacts**: Match by email within same organisation
212
213## Validation Requirements
214
215### Post-Migration Checks
216
2171. All Customers have Organisation reference
2182. All Customers have at least one Address
2193. All required enum fields have valid values
2204. No orphaned Contacts or Addresses
2215. CustomerNote.customer references are valid
222
223### Data Integrity
224
2251. Total Customer count matches
2262. All ABNs preserved
2273. All email addresses preserved
2284. Credit limits unchanged

Step 4

Design backwards compatibility strategy.

Plan how to maintain compatibility with existing tools and processes during the refactoring transition period.

compatibility-strategy.md
1# Backwards Compatibility Strategy
2
3## Overview
4
5This document outlines the strategy for maintaining backwards compatibility during the transition from the legacy Customer metamodel (v1.0) to the improved CustomerManagement metamodel (v2.0). The goal is to enable gradual migration while minimising disruption to existing tools and processes.
6
7## Compatibility Period
8
9### Timeline
10
11| Phase | Duration | Activities |
12|-------|----------|------------|
13| Preparation | 2 weeks | Deploy compatibility layer, notify stakeholders |
14| Dual Support | 3 months | Support both formats simultaneously |
15| Migration | 1 month | Migrate remaining systems, deprecation warnings |
16| Legacy Sunset | 2 weeks | Final legacy format retirement |
17
18### Exit Criteria
19
20- All client systems migrated to v2.0
21- No legacy format requests for 14 consecutive days
22- Stakeholder sign-off on legacy retirement
23
24## Compatibility Layer Design
25
26### Bidirectional Transformations
27
28Two ATL transformations provide format conversion:
29
301. **LegacyToImproved.atl**: Converts v1.0 to v2.0 format
312. **ImprovedToLegacy.atl**: Converts v2.0 back to v1.0 format
32
33```
34┌─────────────────┐ ┌─────────────────┐
35│ Legacy v1.0 │────▶│ Improved v2.0 │
36│ Customer.xmi │ │ Customer.xmi │
37└─────────────────┘ └─────────────────┘
38 ▲ │
39 │ │
40 └───────────────────────┘
41 ImprovedToLegacy.atl
42```
43
44### API Compatibility
45
46The swift-ecore CLI provides transparent format handling:
47
48```bash
49# Read legacy format, output improved format
50swift-ecore convert legacy-data.xmi \
51 --from-metamodel LegacyCustomer.ecore \
52 --to-metamodel ImprovedCustomer.ecore \
53 --transformation LegacyToImproved.atl \
54 --output improved-data.xmi
55
56# Read improved format, output legacy format
57swift-ecore convert improved-data.xmi \
58 --from-metamodel ImprovedCustomer.ecore \
59 --to-metamodel LegacyCustomer.ecore \
60 --transformation ImprovedToLegacy.atl \
61 --output legacy-data.xmi
62```
63
64## Information Preservation
65
66### Forward Migration (Legacy to Improved)
67
68All legacy data is preserved. Additional fields in v2.0 receive default values:
69
70| New Field | Default Value | Rationale |
71|-----------|---------------|-----------|
72| `Contact.contactType` | `PRIMARY` | Legacy had single contact |
73| `Contact.mobile` | `null` | Not available in legacy |
74| `Contact.position` | `null` | Not available in legacy |
75| `Address.addressType` | `TRADING` | Most common use case |
76| `CustomerNote.createdBy` | `"Migration"` | Audit trail |
77| `CustomerNote.createdAt` | Migration timestamp | Audit trail |
78
79### Backward Migration (Improved to Legacy)
80
81Some information loss is unavoidable when converting to the simpler legacy format:
82
83| Lost Information | Mitigation |
84|------------------|------------|
85| Multiple contacts | Export primary contact only |
86| Multiple addresses | Export first address only |
87| Address types | Discarded (not in legacy) |
88| Contact types | Discarded (not in legacy) |
89| Organisation sharing | Denormalise into each customer |
90| Note metadata | Concatenate into notes string |
91
92### Lossless Round-Trip Guarantee
93
94For data originating in legacy format:
951. Convert to improved format
962. Convert back to legacy format
973. Result matches original (byte-for-byte where possible)
98
99This guarantee does NOT apply to:
100- Data created natively in v2.0 format
101- Manually edited v2.0 data
102
103## Deprecation Warnings
104
105### CLI Warnings
106
107When legacy format is detected:
108
109```
110[DEPRECATION] Legacy Customer format (v1.0) detected.
111 This format will be retired on 2024-06-30.
112 Please migrate to CustomerManagement format (v2.0).
113 Run: swift-ecore migrate --help for migration options.
114```
115
116### Log Messages
117
118All legacy format operations are logged:
119
120```json
121{
122 "timestamp": "2024-03-15T10:30:00Z",
123 "level": "WARN",
124 "message": "Legacy format operation",
125 "format": "LegacyCustomer/1.0",
126 "operation": "read",
127 "source": "client-system-a",
128 "file": "customers-march.xmi"
129}
130```
131
132## Client System Migration
133
134### Migration Checklist
135
136For each client system:
137
138- [ ] Identify all integration points
139- [ ] Assess impact of schema changes
140- [ ] Update data access code
141- [ ] Update validation rules
142- [ ] Test with sample v2.0 data
143- [ ] Migrate production data
144- [ ] Switch to v2.0 format
145- [ ] Remove legacy dependencies
146
147### Support Resources
148
149| Resource | Location |
150|----------|----------|
151| Migration guide | `/docs/migration-v1-to-v2.md` |
152| Sample transformations | `/examples/transforms/` |
153| Test data sets | `/test-data/migration/` |
154| Support channel | #customer-model-migration |
155
156## Rollback Procedures
157
158### Quick Rollback
159
160If critical issues are discovered:
161
162```bash
163# Restore legacy metamodel
164swift-ecore rollback --to-version 1.0 \
165 --environment production \
166 --backup-id backup_20240315_1000
167
168# Convert all v2.0 data back to v1.0
169swift-ecore batch-convert \
170 --source-dir /data/customers/v2/ \
171 --target-dir /data/customers/v1/ \
172 --transformation ImprovedToLegacy.atl
173```
174
175### Rollback Triggers
176
177Automatic rollback if:
178- Error rate exceeds 5% of operations
179- Data corruption detected
180- Critical business process failure
181- Stakeholder emergency request
182
183## Monitoring
184
185### Compatibility Metrics
186
187Track during dual-support period:
188
189| Metric | Alert Threshold |
190|--------|-----------------|
191| Legacy format requests | > 100/day after month 2 |
192| Conversion errors | > 1% of operations |
193| Round-trip mismatches | Any occurrence |
194| Legacy API calls | Track trend (should decline) |
195
196### Dashboard
197
198Monitor migration progress:
199- Systems migrated vs remaining
200- Data volume by format
201- Error rates by system
202- Conversion performance
203
204## Communication Plan
205
206### Stakeholder Notifications
207
208| Milestone | Notification | Audience |
209|-----------|--------------|----------|
210| T-3 months | Announcement | All stakeholders |
211| T-1 month | Reminder | Active legacy users |
212| T-2 weeks | Final warning | Remaining legacy users |
213| T-day | Sunset notice | All stakeholders |
214
215### Documentation Updates
216
217- API documentation updated for v2.0
218- Migration guide published
219- FAQ for common issues
220- Training materials available

Section 3

Creating Migration Transformations

ATL transformations automate the migration from legacy to improved model structures. These transformations must handle data preservation, relationship updates, and structural changes.

Migration transformations are critical for maintaining data integrity during refactoring while enabling systematic application of improvements across all instances.

Step 1

Create the core migration transformation.

This transformation handles the systematic conversion of legacy structures to improved formats, preserving data while applying structural improvements.

MigrateLegacyToImproved.atl
1-- ATL Transformation: Legacy Customer to CustomerManagement v2.0
2-- Migrates legacy customer models to improved metamodel structure
3-- Part of the Model Refactoring Pipeline tutorial
4-- Uses Australian English spelling conventions
5-- @path Legacy=/Legacy/LegacyCustomer.ecore
6-- @path Customer=/Customer/CustomerManagement.ecore
7
8module LegacyCustomer2CustomerManagement;
9create OUT: Customer from IN: Legacy;
10
11-- ===========================================================================
12-- Global Variables for deduplication
13-- ===========================================================================
14
15-- Track created organisations by ABN to avoid duplicates
16helper def: organisationsByABN: Map(String, Customer!Organisation) = Map{};
17
18-- Track created contacts by email to avoid duplicates
19helper def: contactsByEmail: Map(String, Customer!Contact) = Map{};
20
21-- Migration timestamp for audit fields
22helper def: migrationTimestamp: OclAny = OclAny.now();
23
24-- ===========================================================================
25-- Helper: Normalise organisation type string to enum
26-- ===========================================================================
27helper def: normaliseOrganisationType(typeStr: String): Customer!OrganisationType =
28 let normalised: String = typeStr.trim().toUpper() in
29 if normalised = 'COMPANY' or normalised = 'PTY LTD' or normalised = 'PRIVATE' then
30 #PRIVATE_COMPANY
31 else if normalised = 'PUBLIC' or normalised = 'LIMITED' then
32 #PUBLIC_COMPANY
33 else if normalised = 'GOVT' or normalised = 'GOVERNMENT' then
34 #GOVERNMENT
35 else if normalised = 'NFP' or normalised = 'NON-PROFIT' or normalised = 'NONPROFIT' then
36 #NON_PROFIT
37 else if normalised = 'PARTNERSHIP' then
38 #PARTNERSHIP
39 else if normalised = 'INDIVIDUAL' or normalised = 'SOLE TRADER' then
40 #SOLE_TRADER
41 else
42 #PRIVATE_COMPANY
43 endif endif endif endif endif endif;
44
45-- ===========================================================================
46-- Helper: Normalise customer status string to enum
47-- ===========================================================================
48helper def: normaliseCustomerStatus(statusStr: String): Customer!CustomerStatus =
49 let normalised: String = statusStr.trim().toUpper() in
50 if normalised = 'NEW' or normalised = 'PROSPECT' or normalised = 'LEAD' then
51 #PROSPECT
52 else if normalised = 'ACTIVE' or normalised = 'CURRENT' then
53 #ACTIVE
54 else if normalised = 'INACTIVE' or normalised = 'DORMANT' then
55 #INACTIVE
56 else if normalised = 'SUSPENDED' or normalised = 'HOLD' or normalised = 'ON HOLD' then
57 #SUSPENDED
58 else if normalised = 'CLOSED' or normalised = 'DELETED' or normalised = 'ARCHIVED' then
59 #CLOSED
60 else
61 #PROSPECT
62 endif endif endif endif endif;
63
64-- ===========================================================================
65-- Helper: Normalise Australian state abbreviations
66-- ===========================================================================
67helper def: normaliseState(stateStr: String): String =
68 let normalised: String = stateStr.trim().toUpper() in
69 if normalised = 'NEW SOUTH WALES' then 'NSW'
70 else if normalised = 'VICTORIA' then 'VIC'
71 else if normalised = 'QUEENSLAND' then 'QLD'
72 else if normalised = 'SOUTH AUSTRALIA' then 'SA'
73 else if normalised = 'WESTERN AUSTRALIA' then 'WA'
74 else if normalised = 'TASMANIA' then 'TAS'
75 else if normalised = 'NORTHERN TERRITORY' then 'NT'
76 else if normalised = 'AUSTRALIAN CAPITAL TERRITORY' then 'ACT'
77 else normalised
78 endif endif endif endif endif endif endif endif;
79
80-- ===========================================================================
81-- Helper: Normalise postcode to 4 digits
82-- ===========================================================================
83helper def: normalisePostcode(postcodeStr: String): String =
84 let trimmed: String = postcodeStr.trim() in
85 if trimmed.size() < 4 then
86 '0'.repeat(4 - trimmed.size()) + trimmed
87 else
88 trimmed
89 endif;
90
91-- ===========================================================================
92-- Helper: Get unique ABNs from legacy customers
93-- ===========================================================================
94helper def: uniqueABNs: Set(String) =
95 Legacy!Customer.allInstances()
96 ->select(c | not c.abn.oclIsUndefined() and c.abn.size() > 0)
97 ->collect(c | c.abn.trim())
98 ->asSet();
99
100-- ===========================================================================
101-- Helper: Get all legacy customers for a given ABN
102-- ===========================================================================
103helper def: customersForABN(abn: String): Sequence(Legacy!Customer) =
104 Legacy!Customer.allInstances()
105 ->select(c | c.abn.trim() = abn);
106
107-- ===========================================================================
108-- Helper: Generate identifier for organisation
109-- ===========================================================================
110helper def: generateOrgIdentifier(abn: String, name: String): String =
111 if abn.size() > 0 then
112 'ORG-' + abn.replace(' ', '')
113 else
114 'ORG-' + name.toUpper().substring(1, 3.min(name.size())) + '-' +
115 OclAny.newGuid().substring(1, 8)
116 endif;
117
118-- ===========================================================================
119-- Rule: CustomerDatabase -> CustomerManagementSystem
120-- Root transformation with all contained elements
121-- ===========================================================================
122rule CustomerDatabase2System {
123 from
124 db: Legacy!CustomerDatabase
125 to
126 sys: Customer!CustomerManagementSystem (
127 name <- db.name,
128 description <- 'Migrated from legacy system on ' + thisModule.migrationTimestamp.toString(),
129 organisations <- thisModule.uniqueABNs
130 ->collect(abn | thisModule.createOrganisation(
131 thisModule.customersForABN(abn)->first()
132 )),
133 customers <- db.customers,
134 categories <- thisModule.createDefaultCategories()
135 )
136}
137
138-- ===========================================================================
139-- Lazy Rule: Create Organisation from legacy Customer data
140-- Deduplicates by ABN (Australian Business Number)
141-- ===========================================================================
142lazy rule createOrganisation {
143 from
144 c: Legacy!Customer
145 to
146 org: Customer!Organisation (
147 identifier <- thisModule.generateOrgIdentifier(c.abn, c.companyName),
148 name <- c.companyName,
149 description <- 'Organisation for ' + c.companyName,
150 organisationType <- thisModule.normaliseOrganisationType(c.companyType),
151 tradingName <- if c.tradingName.oclIsUndefined() then c.companyName
152 else c.tradingName endif,
153 australianBusinessNumber <- c.abn.replace(' ', ''),
154 australianCompanyNumber <- if c.acn.oclIsUndefined() then ''
155 else c.acn.replace(' ', '') endif,
156 createdAt <- thisModule.migrationTimestamp,
157 modifiedAt <- thisModule.migrationTimestamp,
158 addresses <- Sequence{tradingAddr},
159 contacts <- thisModule.customersForABN(c.abn)
160 ->collect(cust | thisModule.createContact(cust))
161 ),
162 tradingAddr: Customer!Address (
163 addressType <- #TRADING,
164 streetAddress <- c.streetAddress,
165 suburb <- c.city,
166 state <- thisModule.normaliseState(c.state),
167 postcode <- thisModule.normalisePostcode(c.postcode),
168 country <- if c.country.oclIsUndefined() or c.country.size() = 0
169 then 'Australia'
170 else c.country endif
171 )
172}
173
174-- ===========================================================================
175-- Lazy Rule: Create Contact from legacy Customer data
176-- ===========================================================================
177lazy rule createContact {
178 from
179 c: Legacy!Customer
180 to
181 contact: Customer!Contact (
182 name <- c.contactName,
183 description <- 'Contact for ' + c.name,
184 contactType <- #PRIMARY,
185 email <- c.contactEmail,
186 phone <- c.contactPhone,
187 mobile <- if c.contactMobile.oclIsUndefined() then '' else c.contactMobile endif,
188 position <- if c.contactPosition.oclIsUndefined() then '' else c.contactPosition endif
189 )
190}
191
192-- ===========================================================================
193-- Rule: Customer -> Customer
194-- Maps legacy customer to new structure with proper references
195-- ===========================================================================
196rule Customer2Customer {
197 from
198 c: Legacy!Customer
199 to
200 cust: Customer!Customer (
201 identifier <- c.id,
202 name <- c.name,
203 description <- 'Customer account',
204 status <- thisModule.normaliseCustomerStatus(c.status),
205 creditLimit <- c.creditLimit,
206 paymentTermsDays <- if c.paymentTerms.oclIsUndefined() then 30
207 else c.paymentTerms endif,
208 createdAt <- thisModule.migrationTimestamp,
209 modifiedAt <- thisModule.migrationTimestamp,
210 organisation <- thisModule.createOrganisation(
211 Legacy!Customer.allInstances()->any(cust |
212 cust.abn.trim() = c.abn.trim()
213 )
214 ),
215 primaryContact <- thisModule.createContact(c),
216 billingAddress <- billingAddr,
217 deliveryAddress <- deliveryAddr,
218 notes <- if c.notes.oclIsUndefined() or c.notes.size() = 0
219 then Sequence{}
220 else Sequence{custNote}
221 endif
222 ),
223 billingAddr: Customer!Address (
224 addressType <- #BILLING,
225 streetAddress <- c.streetAddress,
226 suburb <- c.city,
227 state <- thisModule.normaliseState(c.state),
228 postcode <- thisModule.normalisePostcode(c.postcode),
229 country <- if c.country.oclIsUndefined() or c.country.size() = 0
230 then 'Australia'
231 else c.country endif
232 ),
233 deliveryAddr: Customer!Address (
234 addressType <- #DELIVERY,
235 streetAddress <- c.streetAddress,
236 suburb <- c.city,
237 state <- thisModule.normaliseState(c.state),
238 postcode <- thisModule.normalisePostcode(c.postcode),
239 country <- if c.country.oclIsUndefined() or c.country.size() = 0
240 then 'Australia'
241 else c.country endif
242 ),
243 custNote: Customer!CustomerNote (
244 content <- c.notes,
245 createdAt <- thisModule.migrationTimestamp,
246 createdBy <- 'Migration'
247 )
248}
249
250-- ===========================================================================
251-- Called Rule: Create default customer categories
252-- ===========================================================================
253rule createDefaultCategories() {
254 to
255 standard: Customer!CustomerCategory (
256 name <- 'Standard',
257 code <- 'STD',
258 description <- 'Standard customer category',
259 discountPercentage <- 0
260 ),
261 premium: Customer!CustomerCategory (
262 name <- 'Premium',
263 code <- 'PRM',
264 description <- 'Premium customer with enhanced discounts',
265 discountPercentage <- 10
266 ),
267 enterprise: Customer!CustomerCategory (
268 name <- 'Enterprise',
269 code <- 'ENT',
270 description <- 'Enterprise customer with volume discounts',
271 discountPercentage <- 15
272 )
273 do {
274 Sequence{standard, premium, enterprise};
275 }
276}

Step 2

Add data quality improvements.

Enhance the migration with data quality improvements: standardising formats, filling missing values, and resolving inconsistencies.

DataQualityImprovements.atl
1-- ATL Transformation: Data Quality Improvements
2-- Enhances migrated CustomerManagement data with quality improvements
3-- Standardises formats, fills missing values, resolves inconsistencies
4-- Part of the Model Refactoring Pipeline tutorial
5-- @path Customer=/Customer/CustomerManagement.ecore
6
7module CustomerQualityImprovements;
8create OUT: Customer refining IN: Customer;
9
10-- ===========================================================================
11-- Data Quality Improvement Rules
12-- Uses refining mode to update elements in place
13-- ===========================================================================
14
15-- ===========================================================================
16-- Helper: Standardise Australian phone number format
17-- Converts various formats to +61 X XXXX XXXX
18-- ===========================================================================
19helper def: standardisePhoneNumber(phone: String): String =
20 let cleaned: String = phone.replace(' ', '').replace('-', '').replace('(', '').replace(')', '') in
21 if cleaned.startsWith('+61') then
22 cleaned
23 else if cleaned.startsWith('0') then
24 '+61 ' + cleaned.substring(2, 2) + ' ' +
25 cleaned.substring(3, 6) + ' ' +
26 cleaned.substring(7, 10)
27 else if cleaned.startsWith('61') then
28 '+' + cleaned.substring(1, 2) + ' ' +
29 cleaned.substring(3, 3) + ' ' +
30 cleaned.substring(4, 7) + ' ' +
31 cleaned.substring(8, 11)
32 else
33 phone
34 endif endif endif;
35
36-- ===========================================================================
37-- Helper: Validate and format ABN (11 digits with spaces)
38-- ===========================================================================
39helper def: formatABN(abn: String): String =
40 let cleaned: String = abn.replace(' ', '') in
41 if cleaned.size() = 11 and cleaned.matches('[0-9]+') then
42 cleaned.substring(1, 2) + ' ' +
43 cleaned.substring(3, 5) + ' ' +
44 cleaned.substring(6, 8) + ' ' +
45 cleaned.substring(9, 11)
46 else
47 abn
48 endif;
49
50-- ===========================================================================
51-- Helper: Validate and format ACN (9 digits with spaces)
52-- ===========================================================================
53helper def: formatACN(acn: String): String =
54 let cleaned: String = acn.replace(' ', '') in
55 if cleaned.size() = 9 and cleaned.matches('[0-9]+') then
56 cleaned.substring(1, 3) + ' ' +
57 cleaned.substring(4, 6) + ' ' +
58 cleaned.substring(7, 9)
59 else
60 acn
61 endif;
62
63-- ===========================================================================
64-- Helper: Title case a name string
65-- ===========================================================================
66helper def: titleCase(str: String): String =
67 str.split(' ')
68 ->collect(word |
69 if word.size() > 0 then
70 word.substring(1, 1).toUpper() +
71 if word.size() > 1 then word.substring(2, word.size()).toLower()
72 else '' endif
73 else
74 ''
75 endif
76 )
77 ->iterate(word; acc: String = '' |
78 if acc.size() = 0 then word
79 else acc + ' ' + word
80 endif
81 );
82
83-- ===========================================================================
84-- Helper: Standardise email to lowercase
85-- ===========================================================================
86helper def: standardiseEmail(email: String): String =
87 email.trim().toLower();
88
89-- ===========================================================================
90-- Helper: Validate Australian postcode (4 digits)
91-- ===========================================================================
92helper def: isValidPostcode(postcode: String): Boolean =
93 postcode.size() = 4 and postcode.matches('[0-9]+');
94
95-- ===========================================================================
96-- Helper: Standardise state abbreviation
97-- ===========================================================================
98helper def: standardiseState(state: String): String =
99 let upper: String = state.trim().toUpper() in
100 if Sequence{'NSW', 'VIC', 'QLD', 'SA', 'WA', 'TAS', 'NT', 'ACT'}->includes(upper) then
101 upper
102 else if upper = 'NEW SOUTH WALES' then 'NSW'
103 else if upper = 'VICTORIA' then 'VIC'
104 else if upper = 'QUEENSLAND' then 'QLD'
105 else if upper = 'SOUTH AUSTRALIA' then 'SA'
106 else if upper = 'WESTERN AUSTRALIA' then 'WA'
107 else if upper = 'TASMANIA' then 'TAS'
108 else if upper = 'NORTHERN TERRITORY' then 'NT'
109 else if upper = 'AUSTRALIAN CAPITAL TERRITORY' then 'ACT'
110 else state
111 endif endif endif endif endif endif endif endif endif;
112
113-- ===========================================================================
114-- Rule: Improve Contact data quality
115-- ===========================================================================
116rule ImproveContact {
117 from
118 c: Customer!Contact (
119 c.email.size() > 0 or c.phone.size() > 0
120 )
121 to
122 c: Customer!Contact (
123 name <- thisModule.titleCase(c.name),
124 email <- thisModule.standardiseEmail(c.email),
125 phone <- if c.phone.size() > 0 then
126 thisModule.standardisePhoneNumber(c.phone)
127 else c.phone endif,
128 mobile <- if c.mobile.size() > 0 then
129 thisModule.standardisePhoneNumber(c.mobile)
130 else c.mobile endif,
131 position <- thisModule.titleCase(c.position)
132 )
133}
134
135-- ===========================================================================
136-- Rule: Improve Organisation data quality
137-- ===========================================================================
138rule ImproveOrganisation {
139 from
140 o: Customer!Organisation
141 to
142 o: Customer!Organisation (
143 australianBusinessNumber <- if o.australianBusinessNumber.size() > 0 then
144 thisModule.formatABN(o.australianBusinessNumber)
145 else o.australianBusinessNumber endif,
146 australianCompanyNumber <- if o.australianCompanyNumber.size() > 0 then
147 thisModule.formatACN(o.australianCompanyNumber)
148 else o.australianCompanyNumber endif
149 )
150}
151
152-- ===========================================================================
153-- Rule: Improve Address data quality
154-- ===========================================================================
155rule ImproveAddress {
156 from
157 a: Customer!Address
158 to
159 a: Customer!Address (
160 streetAddress <- thisModule.titleCase(a.streetAddress),
161 suburb <- thisModule.titleCase(a.suburb),
162 state <- thisModule.standardiseState(a.state),
163 postcode <- if not thisModule.isValidPostcode(a.postcode) then
164 a.postcode.padStart(4, '0')
165 else a.postcode endif,
166 country <- if a.country.size() = 0 or a.country.toUpper() = 'AU' then
167 'Australia'
168 else a.country endif
169 )
170}
171
172-- ===========================================================================
173-- Rule: Standardise Customer data
174-- ===========================================================================
175rule ImproveCustomer {
176 from
177 c: Customer!Customer
178 to
179 c: Customer!Customer (
180 name <- thisModule.titleCase(c.name),
181 -- Ensure payment terms has valid value
182 paymentTermsDays <- if c.paymentTermsDays <= 0 then 30
183 else c.paymentTermsDays endif,
184 -- Ensure credit limit is non-negative
185 creditLimit <- if c.creditLimit < 0 then 0
186 else c.creditLimit endif
187 )
188}
189
190-- ===========================================================================
191-- Rule: Standardise CustomerCategory codes
192-- ===========================================================================
193rule ImproveCategory {
194 from
195 cat: Customer!CustomerCategory
196 to
197 cat: Customer!CustomerCategory (
198 code <- cat.code.toUpper(),
199 name <- thisModule.titleCase(cat.name),
200 -- Ensure discount is within valid range
201 discountPercentage <- if cat.discountPercentage < 0 then 0
202 else if cat.discountPercentage > 100 then 100
203 else cat.discountPercentage
204 endif endif
205 )
206}
207
208-- ===========================================================================
209-- Rule: Add audit timestamps where missing
210-- ===========================================================================
211rule AddAuditTimestamps {
212 from
213 e: Customer!IdentifiableElement (
214 e.createdAt.oclIsUndefined()
215 )
216 to
217 e: Customer!IdentifiableElement (
218 createdAt <- OclAny.now(),
219 modifiedAt <- OclAny.now()
220 )
221}
222
223-- ===========================================================================
224-- Validation Queries (for reporting)
225-- ===========================================================================
226
227-- Count contacts with invalid phone numbers
228helper def: invalidPhoneContacts: Sequence(Customer!Contact) =
229 Customer!Contact.allInstances()
230 ->select(c | c.phone.size() > 0 and not c.phone.startsWith('+61'));
231
232-- Count organisations with invalid ABN
233helper def: invalidABNOrganisations: Sequence(Customer!Organisation) =
234 Customer!Organisation.allInstances()
235 ->select(o | o.australianBusinessNumber.size() > 0 and
236 o.australianBusinessNumber.replace(' ', '').size() <> 11);
237
238-- Count addresses with invalid postcode
239helper def: invalidPostcodeAddresses: Sequence(Customer!Address) =
240 Customer!Address.allInstances()
241 ->select(a | not thisModule.isValidPostcode(a.postcode));
242
243-- Generate quality improvement report
244helper def: qualityReport: String =
245 'Data Quality Improvement Report\n' +
246 '================================\n\n' +
247 'Corrections Applied:\n' +
248 ' Phone numbers standardised: ' +
249 Customer!Contact.allInstances()->select(c | c.phone.startsWith('+61'))->size().toString() + '\n' +
250 ' ABNs formatted: ' +
251 Customer!Organisation.allInstances()->size().toString() + '\n' +
252 ' Addresses improved: ' +
253 Customer!Address.allInstances()->size().toString() + '\n' +
254 '\nRemaining Issues:\n' +
255 ' Invalid phone numbers: ' + thisModule.invalidPhoneContacts->size().toString() + '\n' +
256 ' Invalid ABNs: ' + thisModule.invalidABNOrganisations->size().toString() + '\n' +
257 ' Invalid postcodes: ' + thisModule.invalidPostcodeAddresses->size().toString() + '\n';

Step 3

Create validation transformations.

Build transformations that validate migration results by checking data integrity, relationship consistency, and business rule compliance.

ValidationTransforms.atl
1-- ATL Validation Transformation: Migration Results Validation
2-- Validates data integrity, relationship consistency, and business rules
3-- Part of the Model Refactoring Pipeline tutorial
4-- @path Customer=/Customer/CustomerManagement.ecore
5
6module CustomerMigrationValidation;
7create OUT: Customer refining IN: Customer;
8
9-- ===========================================================================
10-- Validation Helper Functions
11-- ===========================================================================
12
13-- ===========================================================================
14-- Structural Validation: Orphaned References
15-- ===========================================================================
16
17-- Check for customers without organisation
18helper def: customersWithoutOrganisation: Sequence(Customer!Customer) =
19 Customer!Customer.allInstances()
20 ->select(c | c.organisation.oclIsUndefined());
21
22-- Check for contacts without organisation
23helper def: orphanedContacts: Sequence(Customer!Contact) =
24 Customer!Contact.allInstances()
25 ->select(c | c.organisation.oclIsUndefined());
26
27-- Check for customers without primary contact
28helper def: customersWithoutPrimaryContact: Sequence(Customer!Customer) =
29 Customer!Customer.allInstances()
30 ->select(c | c.primaryContact.oclIsUndefined());
31
32-- Check for customers without billing address
33helper def: customersWithoutBillingAddress: Sequence(Customer!Customer) =
34 Customer!Customer.allInstances()
35 ->select(c | c.billingAddress.oclIsUndefined());
36
37-- ===========================================================================
38-- Data Integrity Validation
39-- ===========================================================================
40
41-- Check for duplicate ABNs (should be unique per organisation)
42helper def: duplicateABNs: Set(String) =
43 let allABNs: Sequence(String) = Customer!Organisation.allInstances()
44 ->select(o | o.australianBusinessNumber.size() > 0)
45 ->collect(o | o.australianBusinessNumber.replace(' ', '')) in
46 allABNs->select(abn | allABNs->count(abn) > 1)->asSet();
47
48-- Check for duplicate customer identifiers
49helper def: duplicateCustomerIds: Set(String) =
50 let allIds: Sequence(String) = Customer!Customer.allInstances()
51 ->collect(c | c.identifier) in
52 allIds->select(id | allIds->count(id) > 1)->asSet();
53
54-- Check for duplicate contact emails within organisation
55helper def: duplicateContactEmails: Sequence(Customer!Contact) =
56 Customer!Contact.allInstances()
57 ->select(c |
58 c.organisation.contacts
59 ->select(other | other <> c and other.email = c.email)
60 ->size() > 0
61 );
62
63-- ===========================================================================
64-- Business Rule Validation
65-- ===========================================================================
66
67-- Check for invalid credit limits (negative values)
68helper def: invalidCreditLimits: Sequence(Customer!Customer) =
69 Customer!Customer.allInstances()
70 ->select(c | not c.creditLimit.oclIsUndefined() and c.creditLimit < 0);
71
72-- Check for invalid payment terms (too short or too long)
73helper def: invalidPaymentTerms: Sequence(Customer!Customer) =
74 Customer!Customer.allInstances()
75 ->select(c | c.paymentTermsDays < 0 or c.paymentTermsDays > 365);
76
77-- Check for invalid discount percentages
78helper def: invalidDiscounts: Sequence(Customer!CustomerCategory) =
79 Customer!CustomerCategory.allInstances()
80 ->select(cat | cat.discountPercentage < 0 or cat.discountPercentage > 100);
81
82-- Check for missing required fields in organisation
83helper def: incompleteOrganisations: Sequence(Customer!Organisation) =
84 Customer!Organisation.allInstances()
85 ->select(o |
86 o.name.oclIsUndefined() or o.name.size() = 0 or
87 o.identifier.oclIsUndefined() or o.identifier.size() = 0
88 );
89
90-- ===========================================================================
91-- Address Validation
92-- ===========================================================================
93
94-- Check for invalid Australian postcodes
95helper def: invalidPostcodes: Sequence(Customer!Address) =
96 Customer!Address.allInstances()
97 ->select(a |
98 a.postcode.size() <> 4 or
99 not a.postcode.matches('[0-9]+')
100 );
101
102-- Check for invalid Australian states
103helper def: invalidStates: Sequence(Customer!Address) =
104 let validStates: Set(String) = Set{'NSW', 'VIC', 'QLD', 'SA', 'WA', 'TAS', 'NT', 'ACT'} in
105 Customer!Address.allInstances()
106 ->select(a | not validStates->includes(a.state.toUpper()));
107
108-- Check for missing required address fields
109helper def: incompleteAddresses: Sequence(Customer!Address) =
110 Customer!Address.allInstances()
111 ->select(a |
112 a.streetAddress.oclIsUndefined() or a.streetAddress.size() = 0 or
113 a.suburb.oclIsUndefined() or a.suburb.size() = 0 or
114 a.state.oclIsUndefined() or a.state.size() = 0 or
115 a.postcode.oclIsUndefined() or a.postcode.size() = 0
116 );
117
118-- ===========================================================================
119-- Contact Validation
120-- ===========================================================================
121
122-- Check for invalid email format
123helper def: invalidEmails: Sequence(Customer!Contact) =
124 Customer!Contact.allInstances()
125 ->select(c |
126 c.email.size() > 0 and
127 not c.email.matches('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}')
128 );
129
130-- Check for contacts without name
131helper def: unnamedContacts: Sequence(Customer!Contact) =
132 Customer!Contact.allInstances()
133 ->select(c | c.name.oclIsUndefined() or c.name.size() = 0);
134
135-- ===========================================================================
136-- Cross-Reference Validation
137-- ===========================================================================
138
139-- Check that primary contact belongs to customer's organisation
140helper def: misalignedPrimaryContacts: Sequence(Customer!Customer) =
141 Customer!Customer.allInstances()
142 ->select(c |
143 not c.primaryContact.oclIsUndefined() and
144 not c.organisation.oclIsUndefined() and
145 c.primaryContact.organisation <> c.organisation
146 );
147
148-- Check that category references are valid
149helper def: invalidCategoryReferences: Sequence(Customer!Customer) =
150 Customer!Customer.allInstances()
151 ->select(c |
152 not c.category.oclIsUndefined() and
153 not Customer!CustomerCategory.allInstances()->includes(c.category)
154 );
155
156-- ===========================================================================
157-- Validation Summary Report
158-- ===========================================================================
159
160helper def: validationSummary: String =
161 'Migration Validation Report\n' +
162 '===========================\n\n' +
163
164 '## Structural Validation\n' +
165 ' Customers without organisation: ' + thisModule.customersWithoutOrganisation->size().toString() + '\n' +
166 ' Orphaned contacts: ' + thisModule.orphanedContacts->size().toString() + '\n' +
167 ' Customers without primary contact: ' + thisModule.customersWithoutPrimaryContact->size().toString() + '\n' +
168 ' Customers without billing address: ' + thisModule.customersWithoutBillingAddress->size().toString() + '\n\n' +
169
170 '## Data Integrity\n' +
171 ' Duplicate ABNs: ' + thisModule.duplicateABNs->size().toString() + '\n' +
172 ' Duplicate customer IDs: ' + thisModule.duplicateCustomerIds->size().toString() + '\n' +
173 ' Duplicate contact emails: ' + thisModule.duplicateContactEmails->size().toString() + '\n\n' +
174
175 '## Business Rules\n' +
176 ' Invalid credit limits: ' + thisModule.invalidCreditLimits->size().toString() + '\n' +
177 ' Invalid payment terms: ' + thisModule.invalidPaymentTerms->size().toString() + '\n' +
178 ' Invalid discounts: ' + thisModule.invalidDiscounts->size().toString() + '\n' +
179 ' Incomplete organisations: ' + thisModule.incompleteOrganisations->size().toString() + '\n\n' +
180
181 '## Address Validation\n' +
182 ' Invalid postcodes: ' + thisModule.invalidPostcodes->size().toString() + '\n' +
183 ' Invalid states: ' + thisModule.invalidStates->size().toString() + '\n' +
184 ' Incomplete addresses: ' + thisModule.incompleteAddresses->size().toString() + '\n\n' +
185
186 '## Contact Validation\n' +
187 ' Invalid emails: ' + thisModule.invalidEmails->size().toString() + '\n' +
188 ' Unnamed contacts: ' + thisModule.unnamedContacts->size().toString() + '\n\n' +
189
190 '## Cross-Reference Validation\n' +
191 ' Misaligned primary contacts: ' + thisModule.misalignedPrimaryContacts->size().toString() + '\n' +
192 ' Invalid category references: ' + thisModule.invalidCategoryReferences->size().toString() + '\n\n';
193
194helper def: overallStatus: String =
195 let totalIssues: Integer =
196 thisModule.customersWithoutOrganisation->size() +
197 thisModule.orphanedContacts->size() +
198 thisModule.duplicateABNs->size() +
199 thisModule.duplicateCustomerIds->size() +
200 thisModule.invalidCreditLimits->size() +
201 thisModule.incompleteOrganisations->size() +
202 thisModule.invalidEmails->size() +
203 thisModule.misalignedPrimaryContacts->size() in
204 if totalIssues = 0 then
205 'VALIDATION PASSED - No critical issues found'
206 else
207 'VALIDATION FAILED - ' + totalIssues.toString() + ' critical issues found'
208 endif;
209
210-- ===========================================================================
211-- Entry Point: Run Validation
212-- ===========================================================================
213entrypoint rule RunValidation() {
214 do {
215 thisModule.validationSummary.println();
216 '\n'.println();
217 thisModule.overallStatus.println();
218 }
219}

Step 4

Test transformations with sample data.

Execute transformations with the legacy sample data to verify they produce correct results and handle edge cases appropriately.

Terminal
1# Test the migration transformations with sample data
2# Verifies transformations produce correct results
3
4# Create test output directory
5mkdir -p test-output
6
7echo "=============================================="
8echo " Testing Migration Transformations"
9echo "=============================================="
10
11# Step 1: Run the core migration transformation
12echo ""
13echo "Step 1: Testing core migration transformation..."
14swift-atl transform LegacyCustomer2CustomerManagement.atl \
15 --source legacy-customer-data.xmi \
16 --source-metamodel LegacyCustomer.ecore \
17 --target test-output/migrated-customers.xmi \
18 --target-metamodel ImprovedCustomer.ecore \
19 --verbose
20
21# Output:
22# [INFO] Loading source metamodel: LegacyCustomer.ecore
23# [INFO] Loading target metamodel: ImprovedCustomer.ecore
24# [INFO] Loading source model: legacy-customer-data.xmi
25# [INFO] Compiling transformation: LegacyCustomer2CustomerManagement.atl
26# [INFO] Executing transformation...
27#
28# [PROGRESS] Processing CustomerDatabase: 1
29# [PROGRESS] Processing Customer elements: 25
30# [PROGRESS] Creating Organisation elements: 18 (deduplicated)
31# [PROGRESS] Creating Contact elements: 25
32# [PROGRESS] Creating Address elements: 50
33#
34# [INFO] Transformation completed in 0.62 seconds
35# [INFO] Output saved to: test-output/migrated-customers.xmi
36
37# Step 2: Validate the migrated model structure
38echo ""
39echo "Step 2: Validating migrated model structure..."
40swift-ecore validate test-output/migrated-customers.xmi \
41 --metamodel ImprovedCustomer.ecore
42
43# Output:
44# Validation Results:
45# [OK] Model conforms to CustomerManagement metamodel
46# [OK] All references resolved
47# [OK] All containment constraints satisfied
48# [OK] All multiplicity constraints satisfied
49
50# Step 3: Run data quality improvements
51echo ""
52echo "Step 3: Applying data quality improvements..."
53swift-atl transform CustomerQualityImprovements.atl \
54 --source test-output/migrated-customers.xmi \
55 --metamodel ImprovedCustomer.ecore \
56 --target test-output/improved-customers.xmi \
57 --refining \
58 --verbose
59
60# Output:
61# [INFO] Running in refining mode
62# [INFO] Applying quality improvements...
63#
64# [PROGRESS] Improving Contact elements: 25
65# [PROGRESS] Improving Organisation elements: 18
66# [PROGRESS] Improving Address elements: 50
67# [PROGRESS] Improving Customer elements: 25
68#
69# Improvements Applied:
70# Phone numbers standardised: 22
71# ABNs formatted: 18
72# Emails normalised: 25
73# States standardised: 47
74# Postcodes corrected: 3
75#
76# [INFO] Quality improvements completed
77
78# Step 4: Run validation transformation
79echo ""
80echo "Step 4: Running validation checks..."
81swift-atl transform CustomerMigrationValidation.atl \
82 --source test-output/improved-customers.xmi \
83 --metamodel ImprovedCustomer.ecore \
84 --validate-only
85
86# Output:
87# Migration Validation Report
88# ===========================
89#
90# ## Structural Validation
91# Customers without organisation: 0
92# Orphaned contacts: 0
93# Customers without primary contact: 0
94# Customers without billing address: 0
95#
96# ## Data Integrity
97# Duplicate ABNs: 0
98# Duplicate customer IDs: 0
99# Duplicate contact emails: 0
100#
101# ## Business Rules
102# Invalid credit limits: 0
103# Invalid payment terms: 0
104# Invalid discounts: 0
105# Incomplete organisations: 0
106#
107# ## Address Validation
108# Invalid postcodes: 0
109# Invalid states: 0
110# Incomplete addresses: 0
111#
112# ## Contact Validation
113# Invalid emails: 0
114# Unnamed contacts: 0
115#
116# ## Cross-Reference Validation
117# Misaligned primary contacts: 0
118# Invalid category references: 0
119#
120# VALIDATION PASSED - No critical issues found
121
122# Step 5: Compare element counts
123echo ""
124echo "Step 5: Comparing element counts..."
125swift-ecore compare-counts \
126 legacy-customer-data.xmi \
127 test-output/improved-customers.xmi
128
129# Output:
130# Element Count Comparison
131# ========================
132#
133# Source (Legacy):
134# CustomerDatabase: 1
135# Customer: 25
136# Total: 26
137#
138# Target (Improved):
139# CustomerManagementSystem: 1
140# Organisation: 18
141# Customer: 25
142# Contact: 25
143# Address: 50
144# CustomerNote: 12
145# CustomerCategory: 3
146# Total: 134
147#
148# Migration Summary:
149# Customers preserved: 25/25 (100%)
150# Organisations created: 18 (deduplicated from 25)
151# Data expanded: +108 elements (normalisation)
152
153# Step 6: Verify round-trip capability
154echo ""
155echo "Step 6: Testing backwards compatibility..."
156swift-atl transform ImprovedToLegacy.atl \
157 --source test-output/improved-customers.xmi \
158 --source-metamodel ImprovedCustomer.ecore \
159 --target test-output/roundtrip-legacy.xmi \
160 --target-metamodel LegacyCustomer.ecore
161
162swift-ecore diff \
163 legacy-customer-data.xmi \
164 test-output/roundtrip-legacy.xmi \
165 --ignore-timestamps
166
167# Output:
168# Difference Report
169# =================
170# Files are semantically equivalent
171# (timestamps and formatting differences only)
172
173echo ""
174echo "=============================================="
175echo " All Transformation Tests PASSED"
176echo "=============================================="

Section 4

Batch Instance Migration

Apply migration transformations to all legacy instances systematically. This requires careful orchestration to handle large datasets while maintaining data integrity.

Batch migration processes must be robust, resumable, and provide comprehensive logging to track migration progress and identify any issues.

Step 1

Create migration orchestration scripts.

These scripts orchestrate the migration of multiple instance files, providing progress tracking, error handling, and result validation.

migrate-all-instances.sh
1#!/bin/bash
2# Migration orchestration script for batch instance migration
3# Handles multiple instance files with progress tracking and error handling
4
5set -e
6
7# Configuration
8SOURCE_DIR="${1:-./legacy-data}"
9TARGET_DIR="${2:-./migrated-data}"
10LOG_DIR="./migration-logs"
11TIMESTAMP=$(date +%Y%m%d_%H%M%S)
12LOG_FILE="${LOG_DIR}/migration_${TIMESTAMP}.log"
13
14# Metamodel paths
15LEGACY_METAMODEL="./metamodels/LegacyCustomer.ecore"
16IMPROVED_METAMODEL="./metamodels/ImprovedCustomer.ecore"
17
18# Transformation paths
19MIGRATION_ATL="./transforms/LegacyCustomer2CustomerManagement.atl"
20QUALITY_ATL="./transforms/CustomerQualityImprovements.atl"
21VALIDATION_ATL="./transforms/CustomerMigrationValidation.atl"
22
23# Counters
24TOTAL_FILES=0
25SUCCESS_COUNT=0
26FAILURE_COUNT=0
27SKIPPED_COUNT=0
28
29# Colours for output
30RED='\033[0;31m'
31GREEN='\033[0;32m'
32YELLOW='\033[1;33m'
33NC='\033[0m' # No Colour
34
35# Create directories
36mkdir -p "${TARGET_DIR}"
37mkdir -p "${LOG_DIR}"
38mkdir -p "${TARGET_DIR}/backup"
39
40# Log function
41log() {
42 echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
43}
44
45# Error handler
46handle_error() {
47 local file="$1"
48 local error="$2"
49 log "ERROR: Failed to migrate ${file}: ${error}"
50 echo "${file}" >> "${LOG_DIR}/failed_files_${TIMESTAMP}.txt"
51 ((FAILURE_COUNT++))
52}
53
54# Migrate single file
55migrate_file() {
56 local source_file="$1"
57 local filename=$(basename "${source_file}")
58 local target_file="${TARGET_DIR}/${filename%.xmi}-migrated.xmi"
59 local temp_file="${TARGET_DIR}/.temp_${filename}"
60
61 log "Processing: ${filename}"
62
63 # Step 1: Run core migration
64 if ! swift-atl transform "${MIGRATION_ATL}" \
65 --source "${source_file}" \
66 --source-metamodel "${LEGACY_METAMODEL}" \
67 --target "${temp_file}" \
68 --target-metamodel "${IMPROVED_METAMODEL}" \
69 --quiet 2>> "${LOG_FILE}"; then
70 handle_error "${filename}" "Migration transformation failed"
71 return 1
72 fi
73
74 # Step 2: Apply quality improvements
75 if ! swift-atl transform "${QUALITY_ATL}" \
76 --source "${temp_file}" \
77 --metamodel "${IMPROVED_METAMODEL}" \
78 --target "${target_file}" \
79 --refining \
80 --quiet 2>> "${LOG_FILE}"; then
81 handle_error "${filename}" "Quality improvement failed"
82 rm -f "${temp_file}"
83 return 1
84 fi
85
86 # Clean up temp file
87 rm -f "${temp_file}"
88
89 # Step 3: Validate result
90 if ! swift-atl transform "${VALIDATION_ATL}" \
91 --source "${target_file}" \
92 --metamodel "${IMPROVED_METAMODEL}" \
93 --validate-only \
94 --quiet 2>> "${LOG_FILE}"; then
95 log "WARNING: Validation issues in ${filename}"
96 echo "${filename}" >> "${LOG_DIR}/validation_warnings_${TIMESTAMP}.txt"
97 fi
98
99 log "SUCCESS: ${filename} -> ${target_file}"
100 ((SUCCESS_COUNT++))
101 return 0
102}
103
104# Main execution
105echo "=============================================="
106echo " Batch Migration Orchestration"
107echo "=============================================="
108echo ""
109echo "Source directory: ${SOURCE_DIR}"
110echo "Target directory: ${TARGET_DIR}"
111echo "Log file: ${LOG_FILE}"
112echo ""
113
114log "Starting batch migration"
115log "Configuration:"
116log " Source: ${SOURCE_DIR}"
117log " Target: ${TARGET_DIR}"
118log " Legacy metamodel: ${LEGACY_METAMODEL}"
119log " Improved metamodel: ${IMPROVED_METAMODEL}"
120
121# Validate prerequisites
122if [ ! -d "${SOURCE_DIR}" ]; then
123 echo -e "${RED}ERROR: Source directory not found: ${SOURCE_DIR}${NC}"
124 exit 1
125fi
126
127if [ ! -f "${LEGACY_METAMODEL}" ]; then
128 echo -e "${RED}ERROR: Legacy metamodel not found: ${LEGACY_METAMODEL}${NC}"
129 exit 1
130fi
131
132if [ ! -f "${IMPROVED_METAMODEL}" ]; then
133 echo -e "${RED}ERROR: Improved metamodel not found: ${IMPROVED_METAMODEL}${NC}"
134 exit 1
135fi
136
137# Count files
138TOTAL_FILES=$(find "${SOURCE_DIR}" -name "*.xmi" -type f | wc -l | tr -d ' ')
139log "Found ${TOTAL_FILES} XMI files to migrate"
140
141if [ "${TOTAL_FILES}" -eq 0 ]; then
142 echo -e "${YELLOW}No XMI files found in ${SOURCE_DIR}${NC}"
143 exit 0
144fi
145
146# Process each file
147CURRENT=0
148for source_file in "${SOURCE_DIR}"/*.xmi; do
149 if [ -f "${source_file}" ]; then
150 ((CURRENT++))
151 echo -ne "\rProgress: ${CURRENT}/${TOTAL_FILES} "
152
153 # Check if already migrated
154 filename=$(basename "${source_file}")
155 target_file="${TARGET_DIR}/${filename%.xmi}-migrated.xmi"
156 if [ -f "${target_file}" ]; then
157 log "SKIPPED: ${filename} (already migrated)"
158 ((SKIPPED_COUNT++))
159 continue
160 fi
161
162 # Migrate the file
163 migrate_file "${source_file}" || true
164 fi
165done
166
167echo ""
168echo ""
169echo "=============================================="
170echo " Migration Complete"
171echo "=============================================="
172echo ""
173echo -e "Total files: ${TOTAL_FILES}"
174echo -e "${GREEN}Successful: ${SUCCESS_COUNT}${NC}"
175echo -e "${YELLOW}Skipped: ${SKIPPED_COUNT}${NC}"
176echo -e "${RED}Failed: ${FAILURE_COUNT}${NC}"
177echo ""
178echo "Log file: ${LOG_FILE}"
179
180if [ "${FAILURE_COUNT}" -gt 0 ]; then
181 echo -e "${RED}Failed files listed in: ${LOG_DIR}/failed_files_${TIMESTAMP}.txt${NC}"
182fi
183
184log "Migration completed. Success: ${SUCCESS_COUNT}, Failed: ${FAILURE_COUNT}, Skipped: ${SKIPPED_COUNT}"
185
186# Exit with error if any failures
187if [ "${FAILURE_COUNT}" -gt 0 ]; then
188 exit 1
189fi

Step 2

Set up validation pipelines.

Create automated validation that checks migration results for completeness, correctness, and data integrity across all migrated instances.

validate-migration.sh
1#!/bin/bash
2# Validation pipeline for migrated model instances
3# Performs comprehensive checks on all migrated files
4
5set -e
6
7# Configuration
8MIGRATED_DIR="${1:-./migrated-data}"
9REPORT_DIR="./validation-reports"
10TIMESTAMP=$(date +%Y%m%d_%H%M%S)
11REPORT_FILE="${REPORT_DIR}/validation_report_${TIMESTAMP}.md"
12
13# Metamodel path
14IMPROVED_METAMODEL="./metamodels/ImprovedCustomer.ecore"
15VALIDATION_ATL="./transforms/CustomerMigrationValidation.atl"
16
17# Counters
18TOTAL_FILES=0
19PASSED_COUNT=0
20WARNING_COUNT=0
21FAILED_COUNT=0
22
23# Create directories
24mkdir -p "${REPORT_DIR}"
25
26# Initialise report
27cat > "${REPORT_FILE}" << EOF
28# Migration Validation Report
29
30**Generated:** $(date '+%Y-%m-%d %H:%M:%S')
31**Directory:** ${MIGRATED_DIR}
32
33---
34
35## Summary
36
37| Metric | Count |
38|--------|-------|
39EOF
40
41echo "=============================================="
42echo " Migration Validation Pipeline"
43echo "=============================================="
44echo ""
45echo "Validating files in: ${MIGRATED_DIR}"
46echo "Report: ${REPORT_FILE}"
47echo ""
48
49# Validate metamodel first
50echo "Checking metamodel..."
51if ! swift-ecore validate "${IMPROVED_METAMODEL}" --quiet; then
52 echo "ERROR: Metamodel validation failed"
53 exit 1
54fi
55echo " Metamodel: OK"
56
57# Process each migrated file
58echo ""
59echo "Validating migrated instances..."
60
61for file in "${MIGRATED_DIR}"/*-migrated.xmi; do
62 if [ -f "${file}" ]; then
63 ((TOTAL_FILES++))
64 filename=$(basename "${file}")
65 echo -n " ${filename}: "
66
67 # Step 1: Structural validation
68 if ! swift-ecore validate "${file}" \
69 --metamodel "${IMPROVED_METAMODEL}" \
70 --quiet 2>/dev/null; then
71 echo "FAILED (structural)"
72 ((FAILED_COUNT++))
73 echo "| ${filename} | FAILED | Structural validation error |" >> "${REPORT_FILE}.details"
74 continue
75 fi
76
77 # Step 2: Run ATL validation transformation
78 validation_output=$(swift-atl transform "${VALIDATION_ATL}" \
79 --source "${file}" \
80 --metamodel "${IMPROVED_METAMODEL}" \
81 --validate-only \
82 --quiet 2>&1) || true
83
84 # Check validation result
85 if echo "${validation_output}" | grep -q "VALIDATION PASSED"; then
86 echo "PASSED"
87 ((PASSED_COUNT++))
88 echo "| ${filename} | PASSED | All checks passed |" >> "${REPORT_FILE}.details"
89 elif echo "${validation_output}" | grep -q "VALIDATION FAILED"; then
90 # Extract issue count
91 issue_count=$(echo "${validation_output}" | grep -o '[0-9]* critical issues' | head -1)
92 echo "WARNING (${issue_count})"
93 ((WARNING_COUNT++))
94 echo "| ${filename} | WARNING | ${issue_count} |" >> "${REPORT_FILE}.details"
95 else
96 echo "FAILED (unknown)"
97 ((FAILED_COUNT++))
98 echo "| ${filename} | FAILED | Unknown validation error |" >> "${REPORT_FILE}.details"
99 fi
100 fi
101done
102
103# Calculate percentages
104if [ "${TOTAL_FILES}" -gt 0 ]; then
105 PASS_PERCENT=$((PASSED_COUNT * 100 / TOTAL_FILES))
106 WARN_PERCENT=$((WARNING_COUNT * 100 / TOTAL_FILES))
107 FAIL_PERCENT=$((FAILED_COUNT * 100 / TOTAL_FILES))
108else
109 PASS_PERCENT=0
110 WARN_PERCENT=0
111 FAIL_PERCENT=0
112fi
113
114# Complete the report
115cat >> "${REPORT_FILE}" << EOF
116| Total Files | ${TOTAL_FILES} |
117| Passed | ${PASSED_COUNT} (${PASS_PERCENT}%) |
118| Warnings | ${WARNING_COUNT} (${WARN_PERCENT}%) |
119| Failed | ${FAILED_COUNT} (${FAIL_PERCENT}%) |
120
121---
122
123## Detailed Results
124
125| File | Status | Details |
126|------|--------|---------|
127EOF
128
129if [ -f "${REPORT_FILE}.details" ]; then
130 cat "${REPORT_FILE}.details" >> "${REPORT_FILE}"
131 rm "${REPORT_FILE}.details"
132fi
133
134# Add validation criteria section
135cat >> "${REPORT_FILE}" << EOF
136
137---
138
139## Validation Criteria
140
141### Structural Validation
142- Model conforms to CustomerManagement metamodel
143- All references are resolved
144- Containment constraints satisfied
145- Multiplicity constraints satisfied
146
147### Data Integrity
148- No duplicate ABNs across organisations
149- No duplicate customer identifiers
150- No duplicate contact emails within organisation
151
152### Business Rules
153- Credit limits are non-negative
154- Payment terms between 0 and 365 days
155- Discount percentages between 0 and 100
156
157### Address Validation
158- Australian postcodes are 4 digits
159- States are valid Australian state abbreviations
160- Required address fields are populated
161
162### Contact Validation
163- Email addresses have valid format
164- All contacts have names
165
166### Cross-Reference Validation
167- Primary contacts belong to customer's organisation
168- Category references are valid
169
170---
171
172## Recommendations
173
174EOF
175
176if [ "${FAILED_COUNT}" -gt 0 ]; then
177 cat >> "${REPORT_FILE}" << EOF
178### Critical Issues
179- ${FAILED_COUNT} files failed validation and require investigation
180- Review failed files in detail before proceeding to production
181- Consider re-running migration with fixes
182
183EOF
184fi
185
186if [ "${WARNING_COUNT}" -gt 0 ]; then
187 cat >> "${REPORT_FILE}" << EOF
188### Warnings
189- ${WARNING_COUNT} files have validation warnings
190- Review warnings to determine if manual intervention needed
191- Warnings may indicate data quality issues from source
192
193EOF
194fi
195
196cat >> "${REPORT_FILE}" << EOF
197### Next Steps
1981. Address any failed validations
1992. Review warnings for potential data issues
2003. Run comprehensive QA tests
2014. Prepare for production deployment
202
203EOF
204
205# Print summary
206echo ""
207echo "=============================================="
208echo " Validation Complete"
209echo "=============================================="
210echo ""
211echo "Total files validated: ${TOTAL_FILES}"
212echo " Passed: ${PASSED_COUNT} (${PASS_PERCENT}%)"
213echo " Warnings: ${WARNING_COUNT} (${WARN_PERCENT}%)"
214echo " Failed: ${FAILED_COUNT} (${FAIL_PERCENT}%)"
215echo ""
216echo "Full report: ${REPORT_FILE}"
217
218# Determine exit status
219if [ "${FAILED_COUNT}" -gt 0 ]; then
220 echo ""
221 echo "WARNING: Some files failed validation. Review before proceeding."
222 exit 1
223elif [ "${WARNING_COUNT}" -gt 0 ]; then
224 echo ""
225 echo "NOTE: Some files have warnings. Review recommended."
226 exit 0
227else
228 echo ""
229 echo "All validations passed successfully."
230 exit 0
231fi

Step 3

Execute batch migration.

Run the complete batch migration process, converting all legacy instances to the improved format while tracking progress and validating results.

Terminal
1# Execute the complete batch migration process
2# Orchestrates migration, validation, and reporting
3
4echo "=============================================="
5echo " Full Batch Migration Execution"
6echo "=============================================="
7echo ""
8
9# Configuration
10SOURCE_DIR="./legacy-data"
11TARGET_DIR="./migrated-data"
12BACKUP_DIR="./backups/pre-migration-$(date +%Y%m%d_%H%M%S)"
13
14# Step 1: Create backup of source data
15echo "Step 1: Creating backup of source data..."
16mkdir -p "${BACKUP_DIR}"
17cp -r "${SOURCE_DIR}"/* "${BACKUP_DIR}/"
18echo " Backup created: ${BACKUP_DIR}"
19
20# Generate backup manifest
21swift-ecore generate-manifest "${BACKUP_DIR}" \
22 --output "${BACKUP_DIR}/manifest.json"
23
24# Output:
25# {
26# "backupId": "backup_20240315_100000",
27# "timestamp": "2024-03-15T10:00:00Z",
28# "sourceDirectory": "./legacy-data",
29# "fileCount": 25,
30# "totalSize": "2.5 MB",
31# "checksums": {
32# "customers-batch-001.xmi": "sha256:abc123...",
33# "customers-batch-002.xmi": "sha256:def456...",
34# ...
35# }
36# }
37
38echo " Manifest generated"
39
40# Step 2: Validate source data
41echo ""
42echo "Step 2: Validating source data..."
43swift-ecore batch-validate "${SOURCE_DIR}" \
44 --metamodel metamodels/LegacyCustomer.ecore \
45 --report source-validation.json
46
47# Output:
48# Validating 25 files...
49# [OK] customers-batch-001.xmi
50# [OK] customers-batch-002.xmi
51# ...
52# Validation complete: 25/25 files valid
53
54# Step 3: Run migration
55echo ""
56echo "Step 3: Running batch migration..."
57./migrate-all-instances.sh "${SOURCE_DIR}" "${TARGET_DIR}"
58
59# Output:
60# ==============================================
61# Batch Migration Orchestration
62# ==============================================
63#
64# Source directory: ./legacy-data
65# Target directory: ./migrated-data
66#
67# Found 25 XMI files to migrate
68# Progress: 25/25
69#
70# ==============================================
71# Migration Complete
72# ==============================================
73#
74# Total files: 25
75# Successful: 25
76# Skipped: 0
77# Failed: 0
78
79# Step 4: Run validation pipeline
80echo ""
81echo "Step 4: Running validation pipeline..."
82./validate-migration.sh "${TARGET_DIR}"
83
84# Output:
85# ==============================================
86# Migration Validation Pipeline
87# ==============================================
88#
89# Validating files in: ./migrated-data
90#
91# Checking metamodel...
92# Metamodel: OK
93#
94# Validating migrated instances...
95# customers-batch-001-migrated.xmi: PASSED
96# customers-batch-002-migrated.xmi: PASSED
97# ...
98#
99# ==============================================
100# Validation Complete
101# ==============================================
102#
103# Total files validated: 25
104# Passed: 25 (100%)
105# Warnings: 0 (0%)
106# Failed: 0 (0%)
107
108# Step 5: Generate migration statistics
109echo ""
110echo "Step 5: Generating migration statistics..."
111swift-ecore statistics "${TARGET_DIR}" \
112 --metamodel metamodels/ImprovedCustomer.ecore \
113 --format json \
114 --output migration-statistics.json
115
116# Output:
117# Migration Statistics
118# ====================
119#
120# Source Metrics:
121# Files processed: 25
122# Total legacy elements: 650
123#
124# Target Metrics:
125# Files created: 25
126# CustomerManagementSystem: 25
127# Organisation: 450 (deduplicated from 650)
128# Customer: 650
129# Contact: 650
130# Address: 1300
131# CustomerNote: 312
132# CustomerCategory: 3
133# Total elements: 3390
134#
135# Transformation Metrics:
136# Total processing time: 15.3 seconds
137# Average time per file: 0.61 seconds
138# Elements created per second: 221
139
140# Step 6: Verify data integrity
141echo ""
142echo "Step 6: Verifying data integrity..."
143swift-ecore reconcile \
144 --source "${BACKUP_DIR}" \
145 --source-metamodel metamodels/LegacyCustomer.ecore \
146 --target "${TARGET_DIR}" \
147 --target-metamodel metamodels/ImprovedCustomer.ecore \
148 --report reconciliation-report.json
149
150# Output:
151# Data Reconciliation Report
152# ==========================
153#
154# Source Records: 650 customers
155# Target Records: 650 customers
156# Match Rate: 100%
157#
158# Field Verification:
159# Customer IDs: 650/650 matched
160# Organisation ABNs: 450/450 matched
161# Email addresses: 650/650 matched
162# Phone numbers: 650/650 matched (after normalisation)
163#
164# Data Integrity: VERIFIED
165
166echo ""
167echo "=============================================="
168echo " Batch Migration Successfully Completed"
169echo "=============================================="
170echo ""
171echo "Summary:"
172echo " Source files: 25"
173echo " Migrated files: 25"
174echo " Validation status: All passed"
175echo " Data integrity: Verified"
176echo ""
177echo "Output locations:"
178echo " Migrated data: ${TARGET_DIR}"
179echo " Backup: ${BACKUP_DIR}"
180echo " Reports: ./validation-reports/"
181echo " Statistics: ./migration-statistics.json"

Step 4

Generate migration reports.

Create comprehensive reports that document migration results, statistics, and any issues encountered during the process.

Terminal
1# Generate comprehensive migration reports
2# Creates documentation of migration results and metrics
3
4REPORT_DIR="./reports"
5TIMESTAMP=$(date +%Y%m%d_%H%M%S)
6
7mkdir -p "${REPORT_DIR}"
8
9echo "=============================================="
10echo " Generating Migration Reports"
11echo "=============================================="
12
13# Report 1: Executive Summary
14echo ""
15echo "Generating executive summary..."
16swift-ecore report executive-summary \
17 --source-dir ./legacy-data \
18 --target-dir ./migrated-data \
19 --output "${REPORT_DIR}/executive-summary-${TIMESTAMP}.pdf"
20
21# Output:
22# Executive Summary Report Generated
23# ==================================
24#
25# Project: Customer Management Model Migration
26# Date: 2024-03-15
27# Status: COMPLETED
28#
29# Key Metrics:
30# - 650 customer records migrated
31# - 450 organisations created (31% deduplication)
32# - 100% data integrity verified
33# - 0 critical issues
34#
35# Quality Improvements:
36# - Phone numbers standardised: 95%
37# - ABN/ACN formatted: 100%
38# - Addresses structured: 100%
39#
40# Recommendations:
41# - Proceed with staging deployment
42# - Schedule production migration
43
44# Report 2: Technical Details
45echo ""
46echo "Generating technical report..."
47swift-ecore report technical \
48 --source-metamodel metamodels/LegacyCustomer.ecore \
49 --target-metamodel metamodels/ImprovedCustomer.ecore \
50 --migration-log ./migration-logs/migration_*.log \
51 --output "${REPORT_DIR}/technical-report-${TIMESTAMP}.md"
52
53# Output:
54# Technical Migration Report
55# ==========================
56#
57# ## Metamodel Comparison
58#
59# | Aspect | Legacy | Improved | Change |
60# |--------|--------|----------|--------|
61# | Classes | 2 | 9 | +350% |
62# | Attributes | 24 | 32 | +33% |
63# | References | 3 | 18 | +500% |
64# | Enumerations | 0 | 4 | +4 |
65#
66# ## Transformation Statistics
67#
68# - Rules executed: 6
69# - Lazy rules invoked: 450
70# - Helper calls: 12,500
71# - Average transformation time: 0.61s/file
72#
73# ## Data Quality Metrics
74#
75# | Metric | Before | After | Improvement |
76# |--------|--------|-------|-------------|
77# | Normalisation score | 0.35 | 0.95 | +171% |
78# | Duplicate data | 45% | 2% | -96% |
79# | Type safety | 60% | 100% | +67% |
80
81# Report 3: Data Reconciliation
82echo ""
83echo "Generating reconciliation report..."
84swift-mtl transform reconciliation-template.mtl \
85 --model migration-statistics.json \
86 --output "${REPORT_DIR}/reconciliation-${TIMESTAMP}.html"
87
88# Output:
89# Data Reconciliation Report (HTML)
90# =================================
91# Interactive report with:
92# - Record-by-record comparison
93# - Field mapping visualisation
94# - Discrepancy highlighting
95# - Filter and search capabilities
96
97# Report 4: Validation Summary
98echo ""
99echo "Generating validation summary..."
100swift-ecore report validation \
101 --validation-results ./validation-reports/*.md \
102 --output "${REPORT_DIR}/validation-summary-${TIMESTAMP}.md"
103
104# Output:
105# Validation Summary
106# ==================
107#
108# Total Files: 25
109# Passed: 25 (100%)
110# Warnings: 0 (0%)
111# Failed: 0 (0%)
112#
113# Checks Performed:
114# - Structural validation: 25/25 passed
115# - Reference integrity: 25/25 passed
116# - Business rules: 25/25 passed
117# - Data quality: 25/25 passed
118
119# Report 5: Change Log
120echo ""
121echo "Generating change log..."
122swift-ecore diff-report \
123 --source-dir ./legacy-data \
124 --source-metamodel metamodels/LegacyCustomer.ecore \
125 --target-dir ./migrated-data \
126 --target-metamodel metamodels/ImprovedCustomer.ecore \
127 --output "${REPORT_DIR}/change-log-${TIMESTAMP}.csv"
128
129# Output:
130# change-log-20240315_100000.csv
131# ==============================
132# RecordID,ChangeType,SourcePath,TargetPath,OldValue,NewValue
133# CUST-001,SPLIT,Customer.companyName,Organisation.name,Acme Corp,Acme Corp
134# CUST-001,NORMALISE,Customer.phone,Contact.phone,0412345678,+61 4 1234 5678
135# CUST-001,ENUM,Customer.status,Customer.status,Active,ACTIVE
136# ...
137
138# Report 6: Stakeholder Notification
139echo ""
140echo "Generating stakeholder notification..."
141swift-mtl transform notification-template.mtl \
142 --model "${REPORT_DIR}/executive-summary-${TIMESTAMP}.pdf" \
143 --output "${REPORT_DIR}/stakeholder-email-${TIMESTAMP}.html"
144
145# Output:
146# Subject: Customer Management Model Migration - Completed Successfully
147#
148# Dear Stakeholders,
149#
150# The model migration has been completed successfully.
151#
152# Key Highlights:
153# - All 650 customer records migrated
154# - 100% data integrity verified
155# - No critical issues encountered
156#
157# Please review the attached reports for details.
158# Staging deployment is scheduled for [date].
159#
160# Regards,
161# Migration Team
162
163echo ""
164echo "=============================================="
165echo " Reports Generated Successfully"
166echo "=============================================="
167echo ""
168echo "Reports available in: ${REPORT_DIR}/"
169echo ""
170ls -la "${REPORT_DIR}/"
171
172# Output:
173# total 256
174# -rw-r--r-- 1 user staff 45K executive-summary-20240315_100000.pdf
175# -rw-r--r-- 1 user staff 32K technical-report-20240315_100000.md
176# -rw-r--r-- 1 user staff 78K reconciliation-20240315_100000.html
177# -rw-r--r-- 1 user staff 12K validation-summary-20240315_100000.md
178# -rw-r--r-- 1 user staff 89K change-log-20240315_100000.csv
179# -rw-r--r-- 1 user staff 8K stakeholder-email-20240315_100000.html

Section 5

Quality Assurance and Rollout

Validate the refactored models thoroughly before deploying them to production. Quality assurance ensures that refactoring improves the system without introducing regressions.

A systematic rollout plan manages the transition from legacy to improved models while minimising disruption to existing processes and users.

Step 1

Perform comprehensive quality validation.

Execute extensive quality assurance tests that validate model structure, data integrity, business rules, and system integration.

comprehensive-qa.sh
1#!/bin/bash
2# Comprehensive Quality Assurance for model migration
3# Executes extensive validation and testing
4
5set -e
6
7# Configuration
8MIGRATED_DIR="./migrated-data"
9METAMODEL="./metamodels/ImprovedCustomer.ecore"
10QA_REPORT_DIR="./qa-reports"
11TIMESTAMP=$(date +%Y%m%d_%H%M%S)
12
13# Test results
14TESTS_PASSED=0
15TESTS_FAILED=0
16TESTS_SKIPPED=0
17
18mkdir -p "${QA_REPORT_DIR}"
19
20# Test execution function
21run_test() {
22 local test_name="$1"
23 local test_command="$2"
24
25 echo -n " ${test_name}: "
26 if eval "${test_command}" > /dev/null 2>&1; then
27 echo "PASSED"
28 ((TESTS_PASSED++))
29 return 0
30 else
31 echo "FAILED"
32 ((TESTS_FAILED++))
33 return 1
34 fi
35}
36
37echo "=============================================="
38echo " Comprehensive Quality Assurance"
39echo "=============================================="
40echo ""
41echo "Target directory: ${MIGRATED_DIR}"
42echo "Report directory: ${QA_REPORT_DIR}"
43echo ""
44
45# Section 1: Metamodel Validation
46echo "1. Metamodel Validation"
47echo "------------------------"
48run_test "Schema well-formedness" \
49 "swift-ecore validate ${METAMODEL}"
50run_test "Namespace URI valid" \
51 "swift-ecore check-nsuri ${METAMODEL}"
52run_test "No circular containments" \
53 "swift-ecore check-containment ${METAMODEL}"
54run_test "Reference opposites consistent" \
55 "swift-ecore check-opposites ${METAMODEL}"
56echo ""
57
58# Section 2: Instance Validation
59echo "2. Instance Validation"
60echo "----------------------"
61for file in "${MIGRATED_DIR}"/*-migrated.xmi; do
62 if [ -f "${file}" ]; then
63 filename=$(basename "${file}")
64 run_test "Valid: ${filename}" \
65 "swift-ecore validate ${file} --metamodel ${METAMODEL}"
66 fi
67done
68echo ""
69
70# Section 3: Data Integrity Tests
71echo "3. Data Integrity Tests"
72echo "-----------------------"
73run_test "No orphaned references" \
74 "swift-ecore check-references ${MIGRATED_DIR} --metamodel ${METAMODEL}"
75run_test "All required fields populated" \
76 "swift-ecore check-required ${MIGRATED_DIR} --metamodel ${METAMODEL}"
77run_test "No duplicate identifiers" \
78 "swift-ecore check-unique ${MIGRATED_DIR} --field identifier"
79run_test "Referential integrity verified" \
80 "swift-ecore check-integrity ${MIGRATED_DIR} --metamodel ${METAMODEL}"
81echo ""
82
83# Section 4: Business Rule Tests
84echo "4. Business Rule Tests"
85echo "----------------------"
86
87# Test: All organisations have valid ABN format
88run_test "ABN format valid" \
89 "swift-ecore query ${MIGRATED_DIR} \
90 --expression 'Organisation.allInstances()->forAll(o |
91 o.australianBusinessNumber.size() = 0 or
92 o.australianBusinessNumber.replace(\" \", \"\").size() = 11)' \
93 --metamodel ${METAMODEL}"
94
95# Test: All customers have credit limit >= 0
96run_test "Credit limits non-negative" \
97 "swift-ecore query ${MIGRATED_DIR} \
98 --expression 'Customer.allInstances()->forAll(c |
99 c.creditLimit.oclIsUndefined() or c.creditLimit >= 0)' \
100 --metamodel ${METAMODEL}"
101
102# Test: Payment terms within range
103run_test "Payment terms valid (0-365)" \
104 "swift-ecore query ${MIGRATED_DIR} \
105 --expression 'Customer.allInstances()->forAll(c |
106 c.paymentTermsDays >= 0 and c.paymentTermsDays <= 365)' \
107 --metamodel ${METAMODEL}"
108
109# Test: Email format valid
110run_test "Email addresses valid" \
111 "swift-ecore query ${MIGRATED_DIR} \
112 --expression 'Contact.allInstances()->forAll(c |
113 c.email.matches(\"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}\"))' \
114 --metamodel ${METAMODEL}"
115
116# Test: Australian postcodes valid
117run_test "Postcodes valid (4 digits)" \
118 "swift-ecore query ${MIGRATED_DIR} \
119 --expression 'Address.allInstances()->forAll(a |
120 a.postcode.matches(\"[0-9]{4}\"))' \
121 --metamodel ${METAMODEL}"
122echo ""
123
124# Section 5: Performance Tests
125echo "5. Performance Tests"
126echo "--------------------"
127
128# Test: Load time acceptable
129run_test "Load time < 5 seconds" \
130 "timeout 5 swift-ecore load ${MIGRATED_DIR} --metamodel ${METAMODEL}"
131
132# Test: Query performance
133run_test "Query performance < 2 seconds" \
134 "timeout 2 swift-ecore query ${MIGRATED_DIR} \
135 --expression 'Customer.allInstances()->size()' \
136 --metamodel ${METAMODEL}"
137
138# Test: Memory usage reasonable
139run_test "Memory usage < 512MB" \
140 "swift-ecore memory-check ${MIGRATED_DIR} --max-mb 512"
141echo ""
142
143# Section 6: Regression Tests
144echo "6. Regression Tests"
145echo "-------------------"
146
147# Test: Round-trip preservation
148run_test "Round-trip data preservation" \
149 "./test-roundtrip.sh ${MIGRATED_DIR}"
150
151# Test: Backward compatibility
152run_test "Legacy format export works" \
153 "swift-atl transform ImprovedToLegacy.atl \
154 --source ${MIGRATED_DIR}/customers-batch-001-migrated.xmi \
155 --source-metamodel ${METAMODEL} \
156 --target /tmp/legacy-test.xmi \
157 --target-metamodel metamodels/LegacyCustomer.ecore"
158
159# Test: Comparision with expected output
160run_test "Output matches expected" \
161 "swift-ecore diff ${MIGRATED_DIR} ./expected-output --ignore-timestamps"
162echo ""
163
164# Section 7: Integration Tests
165echo "7. Integration Tests"
166echo "--------------------"
167run_test "API endpoint accessible" \
168 "curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/health | grep -q 200"
169run_test "Model can be served via API" \
170 "swift-ecore serve ${MIGRATED_DIR} --port 8081 --test-mode"
171run_test "Query API functional" \
172 "curl -s http://localhost:8081/query -d 'Customer.allInstances()->size()'"
173echo ""
174
175# Generate QA Report
176echo "Generating QA Report..."
177TOTAL_TESTS=$((TESTS_PASSED + TESTS_FAILED + TESTS_SKIPPED))
178PASS_RATE=$((TESTS_PASSED * 100 / TOTAL_TESTS))
179
180cat > "${QA_REPORT_DIR}/qa-report-${TIMESTAMP}.md" << EOF
181# Comprehensive QA Report
182
183**Generated:** $(date '+%Y-%m-%d %H:%M:%S')
184
185## Summary
186
187| Metric | Value |
188|--------|-------|
189| Total Tests | ${TOTAL_TESTS} |
190| Passed | ${TESTS_PASSED} |
191| Failed | ${TESTS_FAILED} |
192| Skipped | ${TESTS_SKIPPED} |
193| Pass Rate | ${PASS_RATE}% |
194
195## Test Categories
196
1971. Metamodel Validation: Ensures schema correctness
1982. Instance Validation: Verifies all migrated files
1993. Data Integrity: Checks references and constraints
2004. Business Rules: Validates domain logic
2015. Performance: Tests load times and resource usage
2026. Regression: Ensures no functionality lost
2037. Integration: Tests system interactions
204
205## Recommendation
206
207EOF
208
209if [ "${TESTS_FAILED}" -eq 0 ]; then
210 echo "**APPROVED FOR PRODUCTION**" >> "${QA_REPORT_DIR}/qa-report-${TIMESTAMP}.md"
211 echo "" >> "${QA_REPORT_DIR}/qa-report-${TIMESTAMP}.md"
212 echo "All quality gates passed. Migration is ready for production deployment." >> "${QA_REPORT_DIR}/qa-report-${TIMESTAMP}.md"
213else
214 echo "**NOT APPROVED - ISSUES FOUND**" >> "${QA_REPORT_DIR}/qa-report-${TIMESTAMP}.md"
215 echo "" >> "${QA_REPORT_DIR}/qa-report-${TIMESTAMP}.md"
216 echo "${TESTS_FAILED} test(s) failed. Address issues before proceeding." >> "${QA_REPORT_DIR}/qa-report-${TIMESTAMP}.md"
217fi
218
219echo ""
220echo "=============================================="
221echo " QA Complete"
222echo "=============================================="
223echo ""
224echo "Total tests: ${TOTAL_TESTS}"
225echo " Passed: ${TESTS_PASSED}"
226echo " Failed: ${TESTS_FAILED}"
227echo " Skipped: ${TESTS_SKIPPED}"
228echo " Pass rate: ${PASS_RATE}%"
229echo ""
230echo "Report: ${QA_REPORT_DIR}/qa-report-${TIMESTAMP}.md"
231
232if [ "${TESTS_FAILED}" -gt 0 ]; then
233 echo ""
234 echo "WARNING: Some tests failed. Review before proceeding."
235 exit 1
236fi

Step 2

Create rollback procedures.

Develop reliable rollback procedures that can quickly restore legacy models if issues are discovered after deployment.

rollback-procedures.sh
1#!/bin/bash
2# Rollback procedures for model migration
3# Restores legacy model state if issues are discovered
4
5set -e
6
7# Configuration
8BACKUP_BASE_DIR="./backups"
9PROD_DATA_DIR="./production-data"
10LEGACY_METAMODEL="./metamodels/LegacyCustomer.ecore"
11IMPROVED_METAMODEL="./metamodels/ImprovedCustomer.ecore"
12
13# Colours
14RED='\033[0;31m'
15GREEN='\033[0;32m'
16YELLOW='\033[1;33m'
17NC='\033[0m'
18
19# Functions
20list_backups() {
21 echo "Available backups:"
22 echo ""
23 ls -la "${BACKUP_BASE_DIR}" | grep "^d" | grep -v "^\." | awk '{print " " $NF}'
24 echo ""
25}
26
27verify_backup() {
28 local backup_dir="$1"
29
30 echo "Verifying backup integrity..."
31
32 # Check manifest exists
33 if [ ! -f "${backup_dir}/manifest.json" ]; then
34 echo -e "${RED}ERROR: Manifest not found${NC}"
35 return 1
36 fi
37
38 # Verify checksums
39 swift-ecore verify-checksums "${backup_dir}" \
40 --manifest "${backup_dir}/manifest.json"
41
42 echo -e "${GREEN}Backup integrity verified${NC}"
43}
44
45rollback_to_backup() {
46 local backup_dir="$1"
47
48 echo ""
49 echo "=============================================="
50 echo " INITIATING ROLLBACK"
51 echo "=============================================="
52 echo ""
53 echo -e "${YELLOW}WARNING: This will restore production to legacy format${NC}"
54 echo ""
55 echo "Backup: ${backup_dir}"
56 echo "Target: ${PROD_DATA_DIR}"
57 echo ""
58
59 # Confirm rollback
60 read -p "Type 'ROLLBACK' to confirm: " confirm
61 if [ "${confirm}" != "ROLLBACK" ]; then
62 echo "Rollback cancelled."
63 exit 0
64 fi
65
66 # Step 1: Create safety backup of current state
67 echo ""
68 echo "Step 1: Creating safety backup of current state..."
69 SAFETY_BACKUP="${BACKUP_BASE_DIR}/pre-rollback-$(date +%Y%m%d_%H%M%S)"
70 mkdir -p "${SAFETY_BACKUP}"
71 cp -r "${PROD_DATA_DIR}"/* "${SAFETY_BACKUP}/"
72 swift-ecore generate-manifest "${SAFETY_BACKUP}" \
73 --output "${SAFETY_BACKUP}/manifest.json"
74 echo " Safety backup: ${SAFETY_BACKUP}"
75
76 # Step 2: Verify source backup
77 echo ""
78 echo "Step 2: Verifying source backup..."
79 verify_backup "${backup_dir}"
80
81 # Step 3: Stop dependent services
82 echo ""
83 echo "Step 3: Stopping dependent services..."
84 # swift-ecore service stop customer-api
85 # swift-ecore service stop reporting-service
86 echo " Services stopped (simulated)"
87
88 # Step 4: Restore legacy metamodel
89 echo ""
90 echo "Step 4: Restoring legacy metamodel..."
91 swift-ecore deploy "${backup_dir}/LegacyCustomer.ecore" \
92 --environment production \
93 --register-nsuri \
94 --atomic
95
96 # Output:
97 # [INFO] Deploying metamodel to production
98 # [INFO] Registered nsURI: http://www.example.org/legacy/customer/1.0
99 # [INFO] Metamodel deployed successfully
100
101 # Step 5: Restore legacy data
102 echo ""
103 echo "Step 5: Restoring legacy model data..."
104 rm -rf "${PROD_DATA_DIR:?}"/*
105 cp -r "${backup_dir}"/*.xmi "${PROD_DATA_DIR}/"
106 echo " Data restored: $(ls -1 ${PROD_DATA_DIR}/*.xmi | wc -l) files"
107
108 # Step 6: Validate restored data
109 echo ""
110 echo "Step 6: Validating restored data..."
111 swift-ecore batch-validate "${PROD_DATA_DIR}" \
112 --metamodel "${LEGACY_METAMODEL}"
113
114 # Output:
115 # Validating files...
116 # [OK] All files valid
117
118 # Step 7: Restart services with legacy configuration
119 echo ""
120 echo "Step 7: Restarting services with legacy configuration..."
121 # swift-ecore service start customer-api --config legacy
122 # swift-ecore service start reporting-service --config legacy
123 echo " Services restarted (simulated)"
124
125 # Step 8: Run health checks
126 echo ""
127 echo "Step 8: Running health checks..."
128 swift-ecore health-check \
129 --environment production \
130 --timeout 60
131
132 # Output:
133 # Health Check Results
134 # ====================
135 # Metamodel registration: OK
136 # Model loading: OK
137 # Reference resolution: OK
138 # Query performance: OK
139 #
140 # Status: HEALTHY
141
142 # Step 9: Log rollback event
143 ROLLBACK_ID="rollback_$(date +%Y%m%d_%H%M%S)"
144 swift-ecore audit-log \
145 --event "PRODUCTION_ROLLBACK" \
146 --rollback-id "${ROLLBACK_ID}" \
147 --reason "Post-migration issue detected" \
148 --backup-used "${backup_dir}"
149
150 echo ""
151 echo "=============================================="
152 echo -e " ${GREEN}ROLLBACK COMPLETED SUCCESSFULLY${NC}"
153 echo "=============================================="
154 echo ""
155 echo "Rollback ID: ${ROLLBACK_ID}"
156 echo "Restored to: Legacy Customer v1.0"
157 echo "Backup used: ${backup_dir}"
158 echo "Safety backup: ${SAFETY_BACKUP}"
159 echo ""
160 echo -e "${YELLOW}IMPORTANT:${NC}"
161 echo " 1. Investigate root cause of the issue"
162 echo " 2. Update migration transformations"
163 echo " 3. Re-test before attempting migration again"
164 echo " 4. Notify stakeholders of the rollback"
165}
166
167convert_back_to_legacy() {
168 echo ""
169 echo "Converting improved format back to legacy format..."
170 echo ""
171
172 # Use ATL transformation for conversion
173 swift-atl batch-transform ImprovedToLegacy.atl \
174 --source-dir "${PROD_DATA_DIR}" \
175 --source-metamodel "${IMPROVED_METAMODEL}" \
176 --target-dir "${PROD_DATA_DIR}-legacy" \
177 --target-metamodel "${LEGACY_METAMODEL}" \
178 --verbose
179
180 # Output:
181 # [INFO] Converting 25 files...
182 # [PROGRESS] customers-batch-001-migrated.xmi -> customers-batch-001.xmi
183 # [PROGRESS] customers-batch-002-migrated.xmi -> customers-batch-002.xmi
184 # ...
185 # [INFO] Conversion complete: 25/25 files
186
187 # Validate converted files
188 swift-ecore batch-validate "${PROD_DATA_DIR}-legacy" \
189 --metamodel "${LEGACY_METAMODEL}"
190
191 echo ""
192 echo "Conversion complete. Legacy files in: ${PROD_DATA_DIR}-legacy"
193}
194
195# Main script
196case "${1:-}" in
197 list)
198 list_backups
199 ;;
200 verify)
201 if [ -z "${2:-}" ]; then
202 echo "Usage: $0 verify <backup-directory>"
203 exit 1
204 fi
205 verify_backup "$2"
206 ;;
207 rollback)
208 if [ -z "${2:-}" ]; then
209 echo "Usage: $0 rollback <backup-directory>"
210 echo ""
211 list_backups
212 exit 1
213 fi
214 rollback_to_backup "$2"
215 ;;
216 convert)
217 convert_back_to_legacy
218 ;;
219 *)
220 echo "Model Migration Rollback Procedures"
221 echo "===================================="
222 echo ""
223 echo "Usage: $0 <command> [options]"
224 echo ""
225 echo "Commands:"
226 echo " list List available backups"
227 echo " verify <dir> Verify backup integrity"
228 echo " rollback <dir> Perform full rollback to backup"
229 echo " convert Convert improved data to legacy format"
230 echo ""
231 echo "Examples:"
232 echo " $0 list"
233 echo " $0 verify ./backups/pre-migration-20240315_100000"
234 echo " $0 rollback ./backups/pre-migration-20240315_100000"
235 echo ""
236 ;;
237esac

Step 3

Update dependent tools and processes.

Systematically update all tools, scripts, and processes that depend on the model structure to work with the improved metamodel.

update-toolchain.sh
1#!/bin/bash
2# Update dependent tools and processes for new metamodel
3# Systematically updates all tools that depend on the model structure
4
5set -e
6
7# Configuration
8NEW_METAMODEL="./metamodels/ImprovedCustomer.ecore"
9TOOLS_DIR="./tools"
10TEMPLATES_DIR="./templates"
11SCRIPTS_DIR="./scripts"
12CONFIG_DIR="./config"
13
14echo "=============================================="
15echo " Updating Toolchain for New Metamodel"
16echo "=============================================="
17echo ""
18
19# Step 1: Regenerate code from new metamodel
20echo "Step 1: Regenerating code from metamodel..."
21swift-ecore generate \
22 --metamodel "${NEW_METAMODEL}" \
23 --output "${TOOLS_DIR}/generated" \
24 --language swift \
25 --include-validators \
26 --include-serializers
27
28# Output:
29# Generating Swift code from CustomerManagement.ecore
30# ===================================================
31# Generated files:
32# - CustomerManagement/Organisation.swift
33# - CustomerManagement/Customer.swift
34# - CustomerManagement/Contact.swift
35# - CustomerManagement/Address.swift
36# - CustomerManagement/CustomerCategory.swift
37# - CustomerManagement/CustomerNote.swift
38# - CustomerManagement/OrganisationType.swift
39# - CustomerManagement/CustomerStatus.swift
40# - CustomerManagement/ContactType.swift
41# - CustomerManagement/AddressType.swift
42# - CustomerManagement/Validators.swift
43# - CustomerManagement/XMISerializer.swift
44# - CustomerManagement/JSONSerializer.swift
45#
46# Generated 13 files in ./tools/generated/
47
48echo " Code generation complete"
49
50# Step 2: Update MTL templates
51echo ""
52echo "Step 2: Updating MTL templates..."
53
54# Update report templates
55for template in "${TEMPLATES_DIR}"/*.mtl; do
56 if [ -f "${template}" ]; then
57 filename=$(basename "${template}")
58 echo " Updating: ${filename}"
59
60 # Replace legacy class references
61 sed -i.bak \
62 -e 's/Legacy!Customer/Customer!Customer/g' \
63 -e 's/\.companyName/.organisation.name/g' \
64 -e 's/\.contactName/.primaryContact.name/g' \
65 -e 's/\.contactEmail/.primaryContact.email/g' \
66 -e 's/\.city/.suburb/g' \
67 -e 's/\.abn/.organisation.australianBusinessNumber/g' \
68 "${template}"
69 fi
70done
71
72swift-mtl validate "${TEMPLATES_DIR}" \
73 --metamodel "${NEW_METAMODEL}"
74
75# Output:
76# Validating MTL templates...
77# [OK] customer-report.mtl
78# [OK] invoice-template.mtl
79# [OK] export-csv.mtl
80# All templates valid
81
82echo " Templates updated"
83
84# Step 3: Update ATL transformations
85echo ""
86echo "Step 3: Updating ATL transformations..."
87
88# Recompile all transformations
89for atl_file in "${TOOLS_DIR}"/transforms/*.atl; do
90 if [ -f "${atl_file}" ]; then
91 filename=$(basename "${atl_file}")
92 echo " Compiling: ${filename}"
93
94 swift-atl compile "${atl_file}" \
95 --metamodels "${CONFIG_DIR}/metamodels.json" \
96 --output "${TOOLS_DIR}/transforms/compiled/"
97 fi
98done
99
100# Output:
101# Compiling ATL transformations...
102# [OK] LegacyCustomer2CustomerManagement.atl
103# [OK] CustomerQualityImprovements.atl
104# [OK] CustomerMigrationValidation.atl
105# [OK] ImprovedToLegacy.atl
106# All transformations compiled
107
108echo " Transformations compiled"
109
110# Step 4: Update configuration files
111echo ""
112echo "Step 4: Updating configuration files..."
113
114# Update metamodel registry
115cat > "${CONFIG_DIR}/metamodel-registry.json" << EOF
116{
117 "metamodels": {
118 "CustomerManagement": {
119 "version": "2.0",
120 "nsURI": "http://www.example.org/customer/2.0",
121 "file": "metamodels/ImprovedCustomer.ecore",
122 "status": "active"
123 },
124 "LegacyCustomer": {
125 "version": "1.0",
126 "nsURI": "http://www.example.org/legacy/customer/1.0",
127 "file": "metamodels/LegacyCustomer.ecore",
128 "status": "deprecated",
129 "deprecationDate": "2024-06-30"
130 }
131 },
132 "defaultMetamodel": "CustomerManagement"
133}
134EOF
135echo " Metamodel registry updated"
136
137# Update API configuration
138cat > "${CONFIG_DIR}/api-config.json" << EOF
139{
140 "endpoints": {
141 "customers": {
142 "metamodel": "CustomerManagement",
143 "rootClass": "CustomerManagementSystem",
144 "supportedFormats": ["xmi", "json"]
145 },
146 "organisations": {
147 "metamodel": "CustomerManagement",
148 "rootClass": "Organisation",
149 "supportedFormats": ["xmi", "json"]
150 },
151 "legacy": {
152 "metamodel": "LegacyCustomer",
153 "rootClass": "CustomerDatabase",
154 "supportedFormats": ["xmi"],
155 "deprecated": true,
156 "sunset": "2024-06-30"
157 }
158 }
159}
160EOF
161echo " API configuration updated"
162
163# Step 5: Update validation rules
164echo ""
165echo "Step 5: Updating validation rules..."
166
167swift-ecore extract-constraints "${NEW_METAMODEL}" \
168 --output "${CONFIG_DIR}/validation-rules.json"
169
170# Output:
171# Extracted validation rules:
172# - Organisation.australianBusinessNumber: pattern [0-9 ]{11,14}
173# - Customer.status: enum CustomerStatus
174# - Contact.email: pattern email
175# - Address.postcode: pattern [0-9]{4}
176# - Address.state: enum ['NSW','VIC','QLD','SA','WA','TAS','NT','ACT']
177
178echo " Validation rules extracted"
179
180# Step 6: Update query library
181echo ""
182echo "Step 6: Updating query library..."
183
184# Generate standard queries for new structure
185swift-ecore generate-queries "${NEW_METAMODEL}" \
186 --output "${TOOLS_DIR}/queries/" \
187 --include-navigation \
188 --include-aggregation
189
190# Output:
191# Generated query library:
192# - navigation/get-customer-organisation.ocl
193# - navigation/get-organisation-customers.ocl
194# - navigation/get-customer-contacts.ocl
195# - aggregation/count-customers-by-status.ocl
196# - aggregation/sum-credit-limits.ocl
197# - aggregation/customers-by-category.ocl
198
199echo " Query library generated"
200
201# Step 7: Update documentation
202echo ""
203echo "Step 7: Updating documentation..."
204
205swift-ecore generate-docs "${NEW_METAMODEL}" \
206 --output "./docs/metamodel/" \
207 --format markdown \
208 --include-diagrams
209
210# Output:
211# Generated documentation:
212# - docs/metamodel/index.md
213# - docs/metamodel/classes/Organisation.md
214# - docs/metamodel/classes/Customer.md
215# - docs/metamodel/classes/Contact.md
216# - docs/metamodel/classes/Address.md
217# - docs/metamodel/enumerations.md
218# - docs/metamodel/diagrams/class-diagram.svg
219# - docs/metamodel/diagrams/containment-diagram.svg
220
221echo " Documentation generated"
222
223# Step 8: Run integration tests
224echo ""
225echo "Step 8: Running integration tests..."
226
227swift-ecore test "${TOOLS_DIR}/tests/" \
228 --metamodel "${NEW_METAMODEL}" \
229 --report "${CONFIG_DIR}/test-results.json"
230
231# Output:
232# Running integration tests...
233# [OK] test_load_customer
234# [OK] test_query_by_status
235# [OK] test_create_organisation
236# [OK] test_update_contact
237# [OK] test_serialize_xmi
238# [OK] test_serialize_json
239# [OK] test_validation_rules
240#
241# Tests: 7 passed, 0 failed
242
243echo " Integration tests passed"
244
245# Step 9: Verify toolchain
246echo ""
247echo "Step 9: Verifying toolchain..."
248
249swift-ecore verify-toolchain \
250 --config "${CONFIG_DIR}/metamodel-registry.json" \
251 --tools "${TOOLS_DIR}" \
252 --templates "${TEMPLATES_DIR}"
253
254# Output:
255# Toolchain Verification
256# ======================
257# [OK] Metamodel registry valid
258# [OK] Code generation output exists
259# [OK] MTL templates compile
260# [OK] ATL transformations compile
261# [OK] Validation rules present
262# [OK] Query library valid
263# [OK] Documentation generated
264#
265# Toolchain Status: READY
266
267echo ""
268echo "=============================================="
269echo " Toolchain Update Complete"
270echo "=============================================="
271echo ""
272echo "Updated components:"
273echo " - Generated code (Swift)"
274echo " - MTL templates (${TEMPLATES_DIR})"
275echo " - ATL transformations (${TOOLS_DIR}/transforms)"
276echo " - Configuration files (${CONFIG_DIR})"
277echo " - Validation rules"
278echo " - Query library"
279echo " - Documentation"
280echo ""
281echo "All integration tests passed."
282echo "Toolchain ready for deployment."

Step 4

Deploy and monitor the refactored models.

Deploy the refactored models to production with comprehensive monitoring to ensure they perform correctly and meet quality expectations.

Terminal
1# Post-deployment monitoring and ongoing operations
2# Ensures migration stability and catches issues early
3
4# Set monitoring parameters
5PROD_ENV="production"
6ALERT_EMAIL="ops-team@example.com"
7DASHBOARD_URL="https://monitoring.example.com/models/orders"
8
9# Start continuous health monitoring
10echo "Starting post-deployment monitoring..."
11
12# Check health every 30 seconds for the first hour
13for i in {1..120}; do
14 HEALTH=$(swift-ecore health-check \
15 --environment ${PROD_ENV} \
16 --format json 2>/dev/null)
17
18 STATUS=$(echo "${HEALTH}" | jq -r '.status')
19 LATENCY=$(echo "${HEALTH}" | jq -r '.queryLatency')
20
21 if [ "${STATUS}" != "healthy" ]; then
22 echo "[ALERT] Health check failed at $(date)"
23 echo "Sending alert to: ${ALERT_EMAIL}"
24 # swift-notify send --to "${ALERT_EMAIL}" --subject "Model Health Alert"
25 fi
26
27 echo "[$(date +%H:%M:%S)] Status: ${STATUS}, Latency: ${LATENCY}ms"
28 sleep 30
29done
30
31# Output (sample):
32# Starting post-deployment monitoring...
33# [15:01:30] Status: healthy, Latency: 8ms
34# [15:02:00] Status: healthy, Latency: 12ms
35# [15:02:30] Status: healthy, Latency: 7ms
36# ...
37
38# Generate hourly metrics report
39swift-ecore metrics \
40 --environment ${PROD_ENV} \
41 --period 1h \
42 --output hourly-metrics.json
43
44# Output:
45# Hourly Metrics Report
46# =====================
47# Period: 2024-01-20 15:00 - 16:00
48#
49# Query Statistics:
50# Total queries: 1,247
51# Avg latency: 9.2ms
52# 95th percentile: 24ms
53# Max latency: 89ms
54#
55# Error Statistics:
56# Total errors: 0
57# Error rate: 0.00%
58#
59# Model Statistics:
60# Read operations: 1,156
61# Write operations: 91
62# Elements modified: 23
63
64# Set up ongoing monitoring alerts
65swift-ecore alert configure \
66 --environment ${PROD_ENV} \
67 --metric "error_rate" \
68 --threshold "0.05" \
69 --action "email:${ALERT_EMAIL}"
70
71swift-ecore alert configure \
72 --environment ${PROD_ENV} \
73 --metric "query_latency_p95" \
74 --threshold "100ms" \
75 --action "email:${ALERT_EMAIL}"
76
77# Output:
78# Alert configured: error_rate > 5% -> email notification
79# Alert configured: query_latency_p95 > 100ms -> email notification
80
81# Generate migration completion report
82cat > migration-completion-report.md << 'EOF'
83# Model Migration Completion Report
84
85## Summary
86
87| Metric | Value |
88|--------|-------|
89| Migration Date | 2024-01-20 |
90| Source Version | LegacyOrders v1.0 |
91| Target Version | Orders v2.0 |
92| Duration | 45 minutes |
93| Status | SUCCESS |
94
95## Data Migrated
96
97| Element Type | Count |
98|--------------|-------|
99| Orders | 4 |
100| Products | 5 |
101| Customers | 4 (deduplicated) |
102| Categories | 3 |
103| Addresses | 7 |
104
105## Quality Improvements
106
107- Eliminated 6 instances of denormalised data
108- Converted 2 string enumerations to proper enums
109- Separated inventory concerns from product
110- Added proper bidirectional references
111- Normalisation score improved from 0.35 to 0.92
112
113## Post-Migration Status
114
115- All health checks passing
116- No errors in first hour
117- Query latency within threshold
118- Backward compatibility endpoints operational
119
120## Monitoring
121
122- Dashboard: https://monitoring.example.com/models/orders
123- Alerts: Configured for error rate and latency
124- Next review: 2024-01-21 09:00
125
126EOF
127
128echo ""
129echo "═══════════════════════════════════════════════════════"
130echo " DEPLOYMENT MONITORING COMPLETE"
131echo "═══════════════════════════════════════════════════════"
132echo " All health checks passed"
133echo " Monitoring alerts configured"
134echo " Report: migration-completion-report.md"
135echo " Dashboard: ${DASHBOARD_URL}"
136echo "═══════════════════════════════════════════════════════"

Check Your Understanding

Question 1 of 4

What should be the first step in model refactoring?

Question 2 of 4

Why is backwards compatibility important during model refactoring?

Question 3 of 4

What role do ATL transformations play in model refactoring?

Question 4 of 4

How should you validate model refactoring results?

Cross-Format Integration

Master cross-format model integration by connecting XMI, JSON, and native Swift data sources.