@peter.prib/renderengine 0.2.6 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +70 -9
  2. package/REFERENCE.md +303 -2
  3. package/dist/renderengine-workspace.umd.js +8851 -3305
  4. package/dist/renderengine.umd.js +5621 -1382
  5. package/examples/archimate.html +68 -66
  6. package/examples/data-model.html +251 -19
  7. package/package.json +1 -1
  8. package/src/bian.js +182 -0
  9. package/src/charting/DiagramEditor.js +5 -26
  10. package/src/charting/DiagramTypeRegistry.js +8 -2
  11. package/src/charting/FormDialog.js +398 -385
  12. package/src/charting/ModelCheckDialog.js +63 -0
  13. package/src/charting/ModelChecker.js +26 -0
  14. package/src/charting/RowsField.js +127 -0
  15. package/src/charting/ViewTabStrip.js +94 -81
  16. package/src/charting/datamodel/BianLandscapeReader.js +185 -0
  17. package/src/charting/datamodel/BianSemanticApiReader.js +183 -0
  18. package/src/charting/datamodel/ClassificationLens.js +33 -0
  19. package/src/charting/datamodel/ClassificationScheme.js +98 -0
  20. package/src/charting/datamodel/DataModel.js +574 -12
  21. package/src/charting/datamodel/DataModelChecker.js +338 -0
  22. package/src/charting/datamodel/DataModelCompared.js +127 -0
  23. package/src/charting/datamodel/DataModelContext.js +40 -10
  24. package/src/charting/datamodel/DataModelEditCommand.js +47 -0
  25. package/src/charting/datamodel/DataModelFileFormat.js +33 -0
  26. package/src/charting/datamodel/DataModelPerspective.js +55 -0
  27. package/src/charting/datamodel/DataType.js +108 -0
  28. package/src/charting/datamodel/EnumerationType.js +64 -0
  29. package/src/charting/datamodel/ServiceDomain.js +186 -0
  30. package/src/charting/datamodel/ServiceLandscape.js +104 -0
  31. package/src/charting/datamodel/SqlDdl.js +80 -0
  32. package/src/charting/datamodel/SqlDdlFileFormat.js +23 -0
  33. package/src/charting/datamodel/StructuredType.js +63 -0
  34. package/src/charting/datamodel/index.js +21 -5
  35. package/src/charting/datamodel/naming/CamelCaseStyle.js +13 -0
  36. package/src/charting/datamodel/naming/LowerWordsStyle.js +13 -0
  37. package/src/charting/datamodel/naming/NameStyle.js +57 -0
  38. package/src/charting/datamodel/naming/NamingStandard.js +132 -0
  39. package/src/charting/datamodel/naming/PascalCaseStyle.js +13 -0
  40. package/src/charting/datamodel/naming/SnakeCaseStyle.js +13 -0
  41. package/src/charting/datamodel/naming/TitleCaseStyle.js +25 -0
  42. package/src/charting/datamodel/naming/UpperSnakeStyle.js +13 -0
  43. package/src/charting/datamodel/naming/index.js +8 -0
  44. package/src/charting/datamodel/scorecard/CompletenessCategory.js +30 -0
  45. package/src/charting/datamodel/scorecard/ConsistencyCategory.js +33 -0
  46. package/src/charting/datamodel/scorecard/DefinitionsCategory.js +33 -0
  47. package/src/charting/datamodel/scorecard/ModelScorecard.js +113 -0
  48. package/src/charting/datamodel/scorecard/ReadabilityCategory.js +37 -0
  49. package/src/charting/datamodel/scorecard/ScorecardCategory.js +46 -0
  50. package/src/charting/datamodel/scorecard/StandardsCategory.js +29 -0
  51. package/src/charting/datamodel/scorecard/StructureCategory.js +32 -0
  52. package/src/charting/datamodel/scorecard/index.js +8 -0
  53. package/src/charting/diagramModel/MergeModelCommand.js +43 -37
  54. package/src/charting/diagramModel/OpenViewList.js +75 -0
  55. package/src/charting/diagramModel/ViewSetChecker.js +22 -34
  56. package/src/charting/diagrams/DataModelDiagram.js +4 -11
  57. package/src/charting/diagrams/DataModelRelationshipStrategy.js +19 -5
  58. package/src/charting/diagrams/EdgeGeometry.js +578 -553
  59. package/src/charting/diagrams/GraphDiagram.js +11 -1
  60. package/src/charting/diagrams/NodeGeometry.js +173 -158
  61. package/src/charting/glyphs/PersonGlyph.js +24 -23
  62. package/src/charting/toolbar/ToolbarSelect.js +6 -0
  63. package/src/datamodel.js +402 -4
  64. package/src/index.js +6 -4
  65. package/src/workspace/EditorShell.js +166 -67
  66. package/src/workspace/PanelLayout.js +56 -0
  67. package/src/workspace/layout/TabsNode.js +205 -191
  68. package/src/workspace/parts/ArchimateCanvasPart.js +5 -7
  69. package/src/workspace/parts/DataModelPart.js +534 -33
  70. package/src/workspace/parts/DiagramEditorPart.js +2 -8
  71. package/src/workspace/parts/EditorPart.js +461 -407
  72. package/src/workspace/parts/ModelFile.js +216 -187
  73. package/src/workspace/parts/ModelsPart.js +483 -192
  74. package/src/workspace/storage/BackupFolder.js +145 -0
  75. package/src/workspace/storage/KeyValueStore.js +92 -0
  76. package/src/workspace/storage/ModelLibrary.js +65 -0
  77. package/src/workspace/workArea.js +417 -413
  78. package/src/yaml/YamlReader.js +372 -0
@@ -1,66 +1,68 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>ArchiMate Modeller</title>
7
- </head>
8
- <body>
9
- <!-- Deliberately empty: the workbench and the modeller part are built from
10
- JavaScript and JSON specs. A classic script (not type="module") so the
11
- page also opens straight off disk. The modeller is the diagram editor's
12
- canvas (ArchimateCanvasPart), one per model in a ModelsPart, so several
13
- models can be open at once (File > New model, Open… in a new tab);
14
- archimate-legacy.html keeps the graph-engine editor for comparison. -->
15
- <script>
16
- function runModeller() {
17
- const { el, registerBuiltinParts, ArchimateCanvasPart } = RenderEngineWorkspace;
18
-
19
- Object.assign(document.documentElement.style, { height: '100%' });
20
- Object.assign(document.body.style, { margin: '0', height: '100%', display: 'flex' });
21
-
22
- const ws = document.body.appendChild(el({
23
- tag: 'ws-workspace',
24
- attrs: { label: 'ArchiMate Modeller', theme: 'light' },
25
- style: { flex: '1 1 auto', minHeight: '0' },
26
- }));
27
- registerBuiltinParts(ws);
28
- ws.registerPart(ArchimateCanvasPart);
29
-
30
- // The models open at once: a tab each. The first is this sample; New model makes empty ones.
31
- ws.addParts([{
32
- id: 'models', type: 'models', editorType: 'archimate-canvas', newModel: { analysis: true },
33
- models: [{
34
- id: 'modeller', name: 'Sample model', analysis: true,
35
- elements: [
36
- { id: 'customer', name: 'Customer', layer: 'business', type: 'business-actor', icon: '👤' },
37
- { id: 'order', name: 'Order Process', layer: 'business', type: 'business-process' },
38
- { id: 'order-svc', name: 'Order Service', layer: 'application', type: 'application-service' },
39
- { id: 'db', name: 'Database', layer: 'technology', type: 'node', icon: '🗄️' },
40
- ],
41
- relationships: [
42
- { source: 'customer', target: 'order', kind: 'assignment' },
43
- { source: 'order-svc', target: 'order', kind: 'serving' },
44
- { source: 'db', target: 'order-svc', kind: 'serving' },
45
- ],
46
- views: [{
47
- id: 'default',
48
- name: 'Default View',
49
- elementIds: ['customer', 'order', 'order-svc', 'db'],
50
- layout: {
51
- customer: { x: 80, y: 80 },
52
- order: { x: 300, y: 80 },
53
- 'order-svc': { x: 300, y: 200 },
54
- db: { x: 80, y: 200 },
55
- },
56
- }],
57
- }],
58
- }]);
59
-
60
- ws.registerPerspective('Modelling', { type: 'part', partId: 'models' });
61
- ws.activatePerspective('Modelling');
62
- }
63
- </script>
64
- <script src="../dist/renderengine-workspace.umd.js" onload="runModeller()"></script>
65
- </body>
66
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ArchiMate Modeller</title>
7
+ </head>
8
+ <body>
9
+ <!-- Deliberately empty: the workbench and the modeller part are built from
10
+ JavaScript and JSON specs. A classic script (not type="module") so the
11
+ page also opens straight off disk. The modeller is the diagram editor's
12
+ canvas (ArchimateCanvasPart), one per model in a ModelsPart, so several
13
+ models can be open at once (File > New model, Open… in a new tab);
14
+ archimate-legacy.html keeps the graph-engine editor for comparison. -->
15
+ <script>
16
+ function runModeller() {
17
+ const { el, registerBuiltinParts, ArchimateCanvasPart } = RenderEngineWorkspace;
18
+
19
+ Object.assign(document.documentElement.style, { height: '100%' });
20
+ Object.assign(document.body.style, { margin: '0', height: '100%', display: 'flex' });
21
+
22
+ const ws = document.body.appendChild(el({
23
+ tag: 'ws-workspace',
24
+ attrs: { label: 'ArchiMate Modeller', theme: 'light' },
25
+ style: { flex: '1 1 auto', minHeight: '0' },
26
+ }));
27
+ registerBuiltinParts(ws);
28
+ ws.registerPart(ArchimateCanvasPart);
29
+
30
+ // The models open at once: a tab each. The first is this sample; New model makes empty ones.
31
+ // keep: every model is kept in this browser as it is edited, and those open reopen next time;
32
+ // File > This browser backs them up (to a folder chosen once, such as one OneDrive or Google Drive syncs).
33
+ ws.addParts([{
34
+ id: 'models', type: 'models', editorType: 'archimate-canvas', newModel: { analysis: true }, keep: true,
35
+ models: [{
36
+ id: 'modeller', name: 'Sample model', analysis: true,
37
+ elements: [
38
+ { id: 'customer', name: 'Customer', layer: 'business', type: 'business-actor', icon: '👤' },
39
+ { id: 'order', name: 'Order Process', layer: 'business', type: 'business-process' },
40
+ { id: 'order-svc', name: 'Order Service', layer: 'application', type: 'application-service' },
41
+ { id: 'db', name: 'Database', layer: 'technology', type: 'node', icon: '🗄️' },
42
+ ],
43
+ relationships: [
44
+ { source: 'customer', target: 'order', kind: 'assignment' },
45
+ { source: 'order-svc', target: 'order', kind: 'serving' },
46
+ { source: 'db', target: 'order-svc', kind: 'serving' },
47
+ ],
48
+ views: [{
49
+ id: 'default',
50
+ name: 'Default View',
51
+ elementIds: ['customer', 'order', 'order-svc', 'db'],
52
+ layout: {
53
+ customer: { x: 80, y: 80 },
54
+ order: { x: 300, y: 80 },
55
+ 'order-svc': { x: 300, y: 200 },
56
+ db: { x: 80, y: 200 },
57
+ },
58
+ }],
59
+ }],
60
+ }]);
61
+
62
+ ws.registerPerspective('Modelling', { type: 'part', partId: 'models' });
63
+ ws.activatePerspective('Modelling');
64
+ }
65
+ </script>
66
+ <script src="../dist/renderengine-workspace.umd.js" onload="runModeller()"></script>
67
+ </body>
68
+ </html>
@@ -14,7 +14,9 @@
14
14
  three levels, each its own schema, that do not map one to one (Party is a
15
15
  Person or an Organisation, both kept in one party table; a Ledger is
16
16
  Accounts and their Entries; a Participation realises Party, Arrangement and
17
- Role; a Product is priced over time and made of resources; a Business
17
+ Role; an Agreement (the contract) governs Arrangements (the terms a
18
+ product is provided on), as BIAN keeps them apart; a Product has features
19
+ and is subject to conditions, is priced over time and made of resources; a Business
18
20
  Direction is measured by targets; a Rule stays a concept). Level shows each one, or Mapping: the three
19
21
  side by side, joined where one realises another. Notation draws them in
20
22
  Chen, Crow's Foot or UML. It uses every feature: all five cardinalities,
@@ -27,6 +29,20 @@
27
29
  ASIC, Operations) says what they mean there, the rules that hold (notes)
28
30
  and where and when it applies. Choosing a perspective brings its context.
29
31
  Model ▸ makes, changes and removes your own perspectives and contexts.
32
+ A second base, Retail banking (BIAN), is a retail-banking slice at BIAN
33
+ level: its perspectives are BIAN service domains (Current Account, Consumer
34
+ Loan, Customer Agreement, Payment Order, the Party and Product directories),
35
+ each managing a control record broken down by behaviour qualifiers; seen
36
+ through one at the logical level they are marked, with a note of its
37
+ service operations. Model ▸ New service domain… makes one of your own.
38
+ The ontology is also the base: File ▸ New ▸ From base makes a model from a
39
+ copy of it, File ▸ Duplicate model copies the one in front, and Tools ▸
40
+ Compare with / Merge from compare or merge with the base or another open
41
+ model (Edit ▸ Undo takes a merge back).
42
+ File ▸ saves it as a .datamodel.json, opens one, makes a new model in a
43
+ tab of its own; every model is kept in this browser as it changes (and
44
+ reopens next time), and File ▸ This browser backs them up to a folder
45
+ chosen once, such as one OneDrive or Google Drive syncs.
30
46
  Drag the boxes and notes; they stay put, level by level. Classic scripts,
31
47
  so the page opens straight off disk; `npm run datamodel` opens it. -->
32
48
  <script src="../dist/renderengine.umd.js"></script>
@@ -46,13 +62,19 @@
46
62
  // Participation realises Party, Arrangement and Role at once; a Rule stays a concept.
47
63
  const conceptual = {
48
64
  entities: [
49
- { id: 'party', name: 'Party', ...at(0, 0), color: '#ede9fe', shape: 'person', labelPosition: 'below',
65
+ { id: 'party', name: 'Party', ...at(0, 0), color: '#ede9fe', shape: 'person', labelPosition: 'below', details: { bian: 'Party' },
50
66
  description: 'A person or legal actor that can act or hold rights.' },
51
67
  { id: 'role', name: 'Party Role', ...at(0, 1), color: '#fae8ff', shape: 'pill',
52
68
  description: 'The part a party plays: customer, supplier, employee. A party has many, over time.' },
53
- { id: 'arrangement', name: 'Arrangement', ...at(1, 0), color: '#fef9c3', shape: 'contract-box',
54
- description: 'A contract, agreement, subscription, membership, policy or lease.' },
55
- { id: 'product', name: 'Product', ...at(1, 1.5), color: '#dcfce7', shape: 'product-box',
69
+ { id: 'agreement', name: 'Agreement', ...at(1, 0), color: '#fef9c3', shape: 'contract-box', details: { bian: 'Agreement' },
70
+ description: 'The contract parties sign: the promises they agree on as one unit.' },
71
+ { id: 'arrangement', name: 'Arrangement', ...at(0.6, 1.0), color: '#fef3c7', details: { bian: 'Arrangement' },
72
+ description: 'The terms a product is provided on, under an agreement: a subscription, an account, a loan, a lease.' },
73
+ { id: 'feature', name: 'Product Feature', ...at(0, 1.5), color: '#d1fae5', details: { bian: 'Feature' },
74
+ description: 'A feature a product is made up of: interest, payments, an overdraft.' },
75
+ { id: 'condition', name: 'Condition', ...at(2, 1.5), color: '#fee2e2', details: { bian: 'Condition' },
76
+ description: 'A fee, rate, limit or eligibility rule a product is subject to.' },
77
+ { id: 'product', name: 'Product', ...at(1, 1.5), color: '#dcfce7', shape: 'product-box', details: { bian: 'Product' },
56
78
  description: 'What the business offers and sells, under terms: a plan, a policy, a loan. The offer, not the things held.' },
57
79
  { id: 'resource', name: 'Resource', ...at(2, 0), color: '#dbeafe', shape: 'cube',
58
80
  description: 'A physical or digital thing of value: product, asset, document, device.' },
@@ -71,7 +93,10 @@
71
93
  ],
72
94
  relationships: [
73
95
  { source: 'party', target: 'role', name: 'plays', sourceCardinality: '1', targetCardinality: '0..*' },
74
- { id: 'enters', source: 'party', target: 'arrangement', name: 'enters', sourceCardinality: '1..*', targetCardinality: '*' },
96
+ { id: 'signs', source: 'party', target: 'agreement', name: 'signs', sourceCardinality: '1..*', targetCardinality: '*' },
97
+ { id: 'governs', source: 'agreement', target: 'arrangement', name: 'governs', sourceCardinality: '1', targetCardinality: '1..*' },
98
+ { id: 'has-feature', source: 'product', target: 'feature', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
99
+ { id: 'subject-to', source: 'product', target: 'condition', name: 'is subject to', sourceCardinality: '*', targetCardinality: '*', identifying: false },
75
100
  { id: 'covers', source: 'arrangement', target: 'resource', name: 'governs', sourceCardinality: '*', targetCardinality: '*', identifying: false },
76
101
  { source: 'place', target: 'resource', name: 'locates', sourceCardinality: '0..1', targetCardinality: '0..*', identifying: false },
77
102
  { source: 'event', target: 'arrangement', name: 'changes', sourceCardinality: '0..*', targetCardinality: '0..1', identifying: false },
@@ -90,10 +115,14 @@
90
115
 
91
116
  const logical = {
92
117
  entities: [
118
+ { id: 'party', name: 'Party', ...at(0.5, -0.8), description: 'A person or an organisation: what Person and Organisation are kinds of.', attributes: [attr('party id', 'PK'), attr('kind')] },
93
119
  { id: 'person', name: 'Person', ...at(0, 0), attributes: [attr('party id', 'PK'), attr('given name'), attr('family name'), attr('born on')] },
94
120
  { id: 'organisation', name: 'Organisation', ...at(1, 0), attributes: [attr('party id', 'PK'), attr('legal name'), attr('registration number')] },
95
121
  { id: 'party-role', name: 'Party Role', ...at(0, 1), attributes: [attr('party id', 'PK FK'), attr('role code', 'PK FK'), attr('valid from'), attr('valid to')] },
96
- { id: 'arrangement', name: 'Arrangement', ...at(2, 0), attributes: [attr('arrangement id', 'PK'), attr('type', 'FK'), attr('terms'), attr('starts'), attr('ends')] },
122
+ { id: 'arrangement', name: 'Arrangement', ...at(2, 0), attributes: [attr('arrangement id', 'PK'), attr('agreement id', 'FK'), attr('type', 'FK'), attr('starts'), attr('ends')] },
123
+ { id: 'agreement', name: 'Agreement', ...at(1, 3), attributes: [attr('agreement id', 'PK'), attr('party id', 'FK'), attr('signed on'), attr('terms')] },
124
+ { id: 'product-feature', name: 'Product Feature', ...at(2, 3), attributes: [attr('product id', 'PK FK'), attr('feature', 'PK'), attr('value')] },
125
+ { id: 'condition', name: 'Condition', ...at(3, 3), attributes: [attr('condition code', 'PK'), attr('kind'), attr('rate'), attr('applies to', 'FK')] },
97
126
  { id: 'participation', name: 'Participation', ...at(1, 1), attributes: [attr('arrangement id', 'PK FK'), attr('party id', 'PK FK'), attr('role code', 'FK'), attr('share')] },
98
127
  { id: 'resource', name: 'Resource', ...at(3, 0), attributes: [attr('resource id', 'PK'), attr('type', 'FK'), attr('place id', 'FK'), attr('serial number'), attr('specification')] },
99
128
  { id: 'place', name: 'Place', ...at(4, 0), attributes: [attr('place id', 'PK'), attr('name'), attr('location'), attr('time zone')] },
@@ -109,6 +138,8 @@
109
138
  { id: 'code', name: 'Classification Code', ...at(4, 2), attributes: [attr('scheme', 'PK FK'), attr('code', 'PK'), attr('label'), attr('parent code', 'FK')] },
110
139
  ],
111
140
  relationships: [
141
+ { id: 'person-is-party', kind: 'generalization', source: 'person', target: 'party', complete: true, disjoint: true },
142
+ { id: 'organisation-is-party', kind: 'generalization', source: 'organisation', target: 'party', complete: true, disjoint: true },
112
143
  { source: 'person', target: 'party-role', name: 'plays', sourceCardinality: '1', targetCardinality: '0..*' },
113
144
  { source: 'organisation', target: 'party-role', name: 'plays', sourceCardinality: '1', targetCardinality: '0..*' },
114
145
  { source: 'arrangement', target: 'participation', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
@@ -120,6 +151,9 @@
120
151
  { source: 'event', target: 'ledger-entry', name: 'is recorded in', sourceCardinality: '1', targetCardinality: '1..*' },
121
152
  { source: 'account', target: 'ledger-entry', name: 'is posted with', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
122
153
  { source: 'scheme', target: 'code', name: 'holds', sourceCardinality: '1', targetCardinality: '1..*' },
154
+ { source: 'agreement', target: 'arrangement', name: 'governs', sourceCardinality: '1', targetCardinality: '1..*' },
155
+ { source: 'product', target: 'product-feature', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
156
+ { source: 'condition', target: 'product', name: 'applies to', sourceCardinality: '*', targetCardinality: '*', identifying: false },
123
157
  { source: 'product', target: 'arrangement', name: 'is sold as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
124
158
  { source: 'product', target: 'product-price', name: 'is priced by', sourceCardinality: '1', targetCardinality: '1..*' },
125
159
  { source: 'product', target: 'product-component', name: 'is made of', sourceCardinality: '1', targetCardinality: '0..*' },
@@ -150,7 +184,7 @@
150
184
  ] },
151
185
  { id: 'place', name: 'place', ...at(3, 0), attributes: [
152
186
  // GEOGRAPHY is no type group the drawing knows: it shows as unknown (grey).
153
- attr('place_id', 'PK', 'BIGINT'), attr('name', '', 'TEXT'), attr('location', '', 'GEOGRAPHY'), attr('tz', '', 'VARCHAR(40)'),
187
+ attr('place_id', 'PK', 'BIGINT'), attr('name', '', 'VARCHAR(120)'), attr('location', '', 'GEOGRAPHY'), attr('tz', '', 'VARCHAR(40)'),
154
188
  ] },
155
189
  { id: 'business_event', name: 'business_event', ...at(2, 1), attributes: [
156
190
  attr('event_id', 'PK', 'BIGINT'), attr('type_code', 'FK', 'VARCHAR(20)'), attr('occurred_at', '', 'TIMESTAMP'),
@@ -166,7 +200,16 @@
166
200
  attr('launched_on', '', 'DATE'), attr('retired_on', '', 'DATE'), attr('components', '', 'JSONB'),
167
201
  ] },
168
202
  { id: 'product_price', name: 'product_price', ...at(4, 1), attributes: [
169
- attr('product_id', 'PK FK', 'BIGINT'), attr('valid_from', 'PK', 'DATE'), attr('amount', '', 'DECIMAL(12,2)'), attr('currency', '', 'CHAR(3)'),
203
+ attr('product_id', 'PK FK', 'BIGINT'), attr('valid_from', 'PK', 'DATE'), attr('amount', '', 'DECIMAL(18,2)'), attr('currency', '', 'CHAR(3)'),
204
+ ] },
205
+ { id: 'agreement', name: 'agreement', ...at(0, 3), attributes: [
206
+ attr('agreement_id', 'PK', 'BIGINT'), attr('party_id', 'FK', 'BIGINT'), attr('signed_on', '', 'DATE'), attr('terms', '', 'JSONB'),
207
+ ] },
208
+ { id: 'product_feature', name: 'product_feature', ...at(1, 3), attributes: [
209
+ attr('product_id', 'PK FK', 'BIGINT'), attr('feature', 'PK', 'VARCHAR(40)'), attr('value', '', 'TEXT'),
210
+ ] },
211
+ { id: 'condition', name: 'condition', ...at(2, 3), attributes: [
212
+ attr('condition_code', 'PK', 'VARCHAR(20)'), attr('kind', '', 'VARCHAR(20)'), attr('rate', '', 'DECIMAL(9,4)'), attr('product_id', 'FK', 'BIGINT'),
170
213
  ] },
171
214
  { id: 'business_direction', name: 'business_direction', ...at(0, 2), attributes: [
172
215
  attr('direction_id', 'PK', 'BIGINT'), attr('kind', '', 'VARCHAR(20)'), attr('statement', '', 'TEXT'),
@@ -174,9 +217,9 @@
174
217
  ] },
175
218
  { id: 'direction_target', name: 'direction_target', ...at(4, 2), attributes: [
176
219
  attr('direction_id', 'PK FK', 'BIGINT'), attr('measure', 'PK', 'VARCHAR(40)'), attr('product_id', 'FK', 'BIGINT'),
177
- attr('value', '', 'NUMERIC(14,2)'), attr('unit', '', 'VARCHAR(10)'), attr('due_on', '', 'DATE'),
220
+ attr('target_value', '', 'NUMERIC(14,2)'), attr('unit', '', 'VARCHAR(10)'), attr('due_on', '', 'DATE'),
178
221
  ] },
179
- { id: 'account', name: 'account', ...at(3, 2), attributes: [attr('account_code', 'PK', 'VARCHAR(20)'), attr('name', '', 'TEXT'), attr('kind', '', 'CHAR(1)')] },
222
+ { id: 'account', name: 'account', ...at(3, 2), attributes: [attr('account_code', 'PK', 'VARCHAR(20)'), attr('name', '', 'VARCHAR(120)'), attr('kind', '', 'VARCHAR(20)')] },
180
223
  { id: 'classification', name: 'classification', ...at(3, 1), attributes: [
181
224
  attr('scheme', 'PK', 'VARCHAR(40)'), attr('code', 'PK', 'VARCHAR(20)'), attr('label', '', 'TEXT'),
182
225
  attr('parent_code', 'FK', 'VARCHAR(20)'), attr('notes'), // no type at all: unknown too
@@ -196,6 +239,9 @@
196
239
  { source: 'product', target: 'arrangement', name: 'is sold as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
197
240
  { source: 'product', target: 'product_price', name: 'is priced by', sourceCardinality: '1', targetCardinality: '1..*' },
198
241
  { source: 'business_direction', target: 'direction_target', name: 'is measured by', sourceCardinality: '1', targetCardinality: '0..*' },
242
+ { source: 'agreement', target: 'arrangement', name: 'governs', sourceCardinality: '1', targetCardinality: '1..*' },
243
+ { source: 'product', target: 'product_feature', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
244
+ { source: 'product', target: 'condition', name: 'is subject to', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
199
245
  { source: 'product', target: 'direction_target', name: 'is aimed at by', sourceCardinality: '0..1', targetCardinality: '0..*', identifying: false },
200
246
  ],
201
247
  };
@@ -203,20 +249,22 @@
203
249
  // How the levels map: [concept, logical entity], then [logical entity, table]; any number either way.
204
250
  const mappings = [
205
251
  { from: 'conceptual', to: 'logical', pairs: [
206
- ['party', 'person'], ['party', 'organisation'], ['role', 'party-role'],
252
+ ['party', 'party'], ['party', 'person'], ['party', 'organisation'], ['role', 'party-role'],
207
253
  ['party', 'participation'], ['arrangement', 'participation'], ['role', 'participation'],
208
254
  ['arrangement', 'arrangement'], ['resource', 'resource'], ['place', 'place'], ['event', 'event'],
209
255
  ['ledger', 'account'], ['ledger', 'ledger-entry'], ['classification', 'scheme'], ['classification', 'code'],
210
256
  ['product', 'product'], ['product', 'product-price'], ['product', 'product-component'], ['resource', 'product-component'],
211
257
  ['direction', 'direction'], ['direction', 'target'],
258
+ ['agreement', 'agreement'], ['feature', 'product-feature'], ['condition', 'condition'],
212
259
  ] },
213
260
  { from: 'logical', to: 'physical', pairs: [
214
- ['person', 'party'], ['organisation', 'party'], ['party-role', 'party_role'], ['participation', 'arrangement_party'],
261
+ ['party', 'party'], ['person', 'party'], ['organisation', 'party'], ['party-role', 'party_role'], ['participation', 'arrangement_party'],
215
262
  ['arrangement', 'arrangement'], ['resource', 'resource'], ['place', 'place'],
216
263
  ['event', 'business_event'], ['event', 'event_payload'], ['account', 'account'], ['ledger-entry', 'ledger_entry'],
217
264
  ['scheme', 'classification'], ['code', 'classification'],
218
265
  ['product', 'product'], ['product-component', 'product'], ['product-price', 'product_price'],
219
266
  ['direction', 'business_direction'], ['target', 'direction_target'],
267
+ ['agreement', 'agreement'], ['product-feature', 'product_feature'], ['condition', 'condition'],
220
268
  ] },
221
269
  ];
222
270
 
@@ -224,13 +272,13 @@
224
272
  const perspectives = [
225
273
  {
226
274
  id: 'customer', name: 'Customer', context: 'retail', description: 'How a customer sees us: what they subscribe to and pay for',
227
- entities: ['party', 'role', 'arrangement', 'product', 'resource', 'event', 'ledger'],
228
- names: { party: 'Customer', role: 'Subscriber', arrangement: 'Subscription', product: 'Plan', resource: 'Device', event: 'Usage', ledger: 'Billing' },
275
+ entities: ['party', 'role', 'agreement', 'arrangement', 'product', 'resource', 'event', 'ledger'],
276
+ names: { party: 'Customer', role: 'Subscriber', agreement: 'Contract', arrangement: 'Subscription', product: 'Plan', resource: 'Device', event: 'Usage', ledger: 'Billing' },
229
277
  relationshipNames: { covers: 'includes', records: 'is billed in' },
230
278
  },
231
279
  {
232
280
  id: 'regulatory', name: 'Regulatory', context: 'regulatory', description: 'What a regulator holds us to',
233
- entities: ['party', 'arrangement', 'event', 'ledger', 'rule', 'classification', 'place'],
281
+ entities: ['party', 'agreement', 'arrangement', 'event', 'ledger', 'rule', 'classification', 'place'],
234
282
  names: { party: 'Legal Entity', arrangement: 'Compliance Obligation', event: 'Reportable Event', ledger: 'Audit Trail', rule: 'Regulation', place: 'Jurisdiction' },
235
283
  relationshipNames: { imposes: 'imposes', records: 'is evidenced by' },
236
284
  },
@@ -262,6 +310,7 @@
262
310
  { id: 'gst', text: 'Prices include GST (10%)', applies: ['ledger', 'product'], x: 1030, y: 600 },
263
311
  { id: 'one-price', text: 'A product has one current price', applies: ['product'], x: 730, y: 790 },
264
312
  { id: 'one-payer', text: 'A subscription has exactly one paying customer', applies: ['arrangement', 'party'], x: 280, y: 225 },
313
+ { id: 'legal-name', text: 'always given for an organisation', applies: ['party'], dimension: 'Completeness', attribute: 'legal name', x: 1330, y: 225 },
265
314
  ],
266
315
  },
267
316
  {
@@ -306,6 +355,178 @@
306
355
  },
307
356
  ];
308
357
 
358
+ // A retail-banking slice at BIAN level: BIAN service domains (each a kind of perspective) over the
359
+ // concepts, each managing its control record, broken down by behaviour qualifiers, at the logical level.
360
+ // Illustrative: the service domains and patterns are BIAN's; the attributes are a sketch.
361
+ const grid = (column, row) => ({ x: 130 + column * 290, y: 100 + row * 230 });
362
+ const banking = {
363
+ name: 'Retail banking (BIAN)',
364
+ levels: {
365
+ conceptual: {
366
+ entities: [
367
+ { id: 'party', name: 'Party', details: { bian: 'Party' }, ...grid(0, 0), shape: 'person', labelPosition: 'below', color: '#ede9fe', description: 'A person or organisation the bank deals with.',
368
+ sensitivity: 'Confidential', categories: ['PII'], stewardship: { owner: 'Head of Customer', steward: 'Customer data steward' } },
369
+ { id: 'agreement', name: 'Agreement', details: { bian: 'Agreement' }, ...grid(1, 0), shape: 'contract-box', color: '#fef9c3', description: 'The contract a customer signs with the bank: the promises both parties agree on.' },
370
+ { id: 'arrangement', name: 'Arrangement', details: { bian: 'Arrangement' }, ...grid(2, 0), color: '#fef3c7', description: 'The terms a product is provided on, under an agreement: an account, a loan.' },
371
+ { id: 'product', name: 'Product', details: { bian: 'Product' }, ...grid(3, 0), shape: 'product-box', color: '#dcfce7', description: 'What the bank offers: a current account, a consumer loan.' },
372
+ { id: 'condition', name: 'Condition', details: { bian: 'Condition' }, ...grid(3, 1), color: '#fee2e2', description: 'A fee, rate or eligibility rule a product or arrangement is subject to.' },
373
+ { id: 'payment', name: 'Payment', ...grid(2, 1), shape: 'ellipse', color: '#dbeafe', description: 'Money moved from one account to another.' },
374
+ ],
375
+ relationships: [
376
+ { source: 'party', target: 'agreement', name: 'signs', sourceCardinality: '1..*', targetCardinality: '*' },
377
+ { source: 'agreement', target: 'arrangement', name: 'governs', sourceCardinality: '1', targetCardinality: '1..*' },
378
+ { source: 'arrangement', target: 'product', name: 'provides', sourceCardinality: '*', targetCardinality: '1', identifying: false },
379
+ { source: 'product', target: 'condition', name: 'is subject to', sourceCardinality: '*', targetCardinality: '*', identifying: false },
380
+ { source: 'arrangement', target: 'payment', name: 'makes', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
381
+ ],
382
+ },
383
+ logical: {
384
+ entities: [
385
+ { id: 'party-entry', name: 'Party Reference Data Directory Entry', ...grid(0, 0), stewardship: { steward: 'Customer data steward' },
386
+ attributes: [attr('party id', 'PK'), { ...attr('legal name'), sensitivity: 'Confidential', categories: ['PII'] }, attr('kind')] },
387
+ { id: 'party-reference', name: 'Reference', ...grid(0, 1), attributes: [attr('party id', 'PK FK'),
388
+ { ...attr('tax number'), sensitivity: 'Restricted', categories: ['PII'] }, { ...attr('passport'), sensitivity: 'Restricted', categories: ['PII'] }] },
389
+ { id: 'demographics', name: 'Demographics', ...grid(0, 2), attributes: [attr('party id', 'PK FK'), { ...attr('born on'), sensitivity: 'Confidential', categories: ['PII'] }, attr('occupation')] },
390
+ { id: 'customer-agreement', name: 'Customer Agreement', ...grid(1, 0), attributes: [attr('agreement id', 'PK'), attr('party id', 'FK'), attr('signed on'), attr('status')] },
391
+ { id: 'agreement-term', name: 'Agreement Term', ...grid(1, 1), attributes: [attr('agreement id', 'PK FK'), attr('term', 'PK'), attr('value')] },
392
+ { id: 'current-account', name: 'Current Account Facility', ...grid(2, 0), attributes: [attr('arrangement id', 'PK'), attr('agreement id', 'FK'), attr('product id', 'FK'), attr('account number', '', 'Account Identification'), attr('account type', '', 'Current Account Type'), attr('currency', '', 'Currency Code'), attr('balance', '', 'Amount'), attr('opened on')] },
393
+ { id: 'interest', name: 'Interest', ...grid(2, 1), attributes: [attr('arrangement id', 'PK FK'), attr('rate'), attr('accrual basis')] },
394
+ { id: 'account-payments', name: 'Payments', ...grid(2, 2), attributes: [attr('arrangement id', 'PK FK'), attr('mandate', 'PK'), attr('kind'), attr('amount', '', 'Amount')] },
395
+ { id: 'service-fees', name: 'Service Fees', ...grid(2, 3), attributes: [attr('arrangement id', 'PK FK'), attr('fee code', 'PK'), attr('amount', '', 'Amount')] },
396
+ { id: 'consumer-loan', name: 'Consumer Loan Facility', ...grid(3, 0), attributes: [attr('arrangement id', 'PK'), attr('agreement id', 'FK'), attr('product id', 'FK'), attr('principal', '', 'Amount'), attr('term months')] },
397
+ { id: 'disbursement', name: 'Disbursement', ...grid(3, 1), attributes: [attr('arrangement id', 'PK FK'), attr('paid on', 'PK'), attr('amount', '', 'Amount')] },
398
+ { id: 'repayment', name: 'Repayment', ...grid(3, 2), attributes: [attr('arrangement id', 'PK FK'), attr('due on', 'PK'), attr('amount', '', 'Amount'), attr('paid')] },
399
+ { id: 'payment-order', name: 'Payment Order Procedure', ...grid(4, 0), attributes: [attr('payment id', 'PK'), attr('from arrangement', 'FK'), attr('to account', '', 'Account Identification'), attr('amount', '', 'Amount'), attr('status', '', 'Payment Status')] },
400
+ { id: 'compliance-check', name: 'Compliance Check', ...grid(4, 1), attributes: [attr('payment id', 'PK FK'), attr('outcome'), attr('checked at')] },
401
+ { id: 'transfer', name: 'Transfer', ...grid(4, 2), attributes: [attr('payment id', 'PK FK'), attr('scheme'), attr('settled at')] },
402
+ { id: 'product-entry', name: 'Product Directory Entry', ...grid(5, 0), attributes: [attr('product id', 'PK'), attr('name'), attr('line')] },
403
+ { id: 'product-feature', name: 'Features', ...grid(5, 1), attributes: [attr('product id', 'PK FK'), attr('feature', 'PK'), attr('value')] },
404
+ { id: 'product-condition', name: 'Conditions', ...grid(5, 2), attributes: [attr('product id', 'PK FK'), attr('condition', 'PK'), attr('rule')] },
405
+ ],
406
+ relationships: [
407
+ { source: 'party-entry', target: 'party-reference', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
408
+ { source: 'party-entry', target: 'demographics', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
409
+ { source: 'party-entry', target: 'customer-agreement', name: 'signs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
410
+ { source: 'customer-agreement', target: 'agreement-term', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
411
+ { source: 'customer-agreement', target: 'current-account', name: 'governs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
412
+ { source: 'customer-agreement', target: 'consumer-loan', name: 'governs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
413
+ { source: 'current-account', target: 'interest', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
414
+ { source: 'current-account', target: 'account-payments', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
415
+ { source: 'current-account', target: 'service-fees', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
416
+ { source: 'consumer-loan', target: 'disbursement', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
417
+ { source: 'consumer-loan', target: 'repayment', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
418
+ { source: 'current-account', target: 'payment-order', name: 'pays by', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
419
+ { source: 'payment-order', target: 'compliance-check', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
420
+ { source: 'payment-order', target: 'transfer', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
421
+ { source: 'product-entry', target: 'product-feature', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
422
+ { source: 'product-entry', target: 'product-condition', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
423
+ { source: 'product-entry', target: 'current-account', name: 'is provided as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
424
+ { source: 'product-entry', target: 'consumer-loan', name: 'is provided as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
425
+ ],
426
+ },
427
+ physical: {
428
+ entities: [
429
+ { id: 'party', name: 'party', ...grid(0, 0), sensitivity: 'Restricted', categories: ['PII'], stewardship: { custodian: 'Core banking platform team' }, attributes: [attr('party_id', 'PK', 'BIGINT'), attr('legal_name', '', 'VARCHAR(200)'), attr('party_kind', '', 'CHAR(1)'), attr('tax_number', '', 'VARCHAR(20)'), attr('born_on', '', 'DATE'), attr('details', '', 'JSONB')] },
430
+ { id: 'customer_agreement', name: 'customer_agreement', ...grid(1, 0), attributes: [attr('agreement_id', 'PK', 'BIGINT'), { ...attr('party_id', 'FK', 'BIGINT'), references: 'party' }, attr('signed_on', '', 'DATE'), attr('status', '', 'VARCHAR(12)'), attr('terms', '', 'JSONB')] },
431
+ { id: 'account', name: 'account', ...grid(2, 0), attributes: [attr('arrangement_id', 'PK', 'BIGINT'), { ...attr('agreement_id', 'FK', 'BIGINT'), references: 'customer_agreement' }, { ...attr('product_id', 'FK', 'BIGINT'), references: 'product' }, attr('kind', '', 'VARCHAR(20)'), attr('balance', '', 'DECIMAL(18,2)'), attr('opened_on', '', 'DATE')] },
432
+ { id: 'account_interest', name: 'account_interest', ...grid(2, 1), attributes: [{ ...attr('arrangement_id', 'PK FK', 'BIGINT'), references: 'account' }, attr('rate', '', 'DECIMAL(7,4)'), attr('basis', '', 'VARCHAR(10)')] },
433
+ { id: 'account_mandate', name: 'account_mandate', ...grid(2, 2), attributes: [{ ...attr('arrangement_id', 'PK FK', 'BIGINT'), references: 'account' }, attr('mandate', 'PK', 'VARCHAR(30)'), attr('kind', '', 'VARCHAR(20)'), attr('amount', '', 'DECIMAL(18,2)')] },
434
+ { id: 'account_fee', name: 'account_fee', ...grid(2, 3), attributes: [{ ...attr('arrangement_id', 'PK FK', 'BIGINT'), references: 'account' }, attr('fee_code', 'PK', 'VARCHAR(12)'), attr('amount', '', 'DECIMAL(18,2)')] },
435
+ { id: 'loan_schedule', name: 'loan_schedule', ...grid(3, 1), attributes: [attr('arrangement_id', 'PK FK', 'BIGINT'), attr('due_on', 'PK', 'DATE'), attr('kind', '', 'VARCHAR(20)'), attr('amount', '', 'DECIMAL(18,2)'), attr('paid', '', 'BOOLEAN')] },
436
+ { id: 'payment_order', name: 'payment_order', ...grid(4, 0), attributes: [attr('payment_id', 'PK', 'BIGINT'), attr('from_arrangement', 'FK', 'BIGINT'), attr('to_account', '', 'VARCHAR(34)'), attr('amount', '', 'DECIMAL(18,2)'), attr('status', '', 'VARCHAR(12)'), attr('steps', '', 'JSONB')] },
437
+ { id: 'product', name: 'product', ...grid(5, 0), attributes: [attr('product_id', 'PK', 'BIGINT'), attr('name', '', 'VARCHAR(120)'), attr('line', '', 'VARCHAR(20)'), attr('features', '', 'JSONB'), attr('conditions', '', 'JSONB')] },
438
+ // A star for reporting balances (a dimensional scheme): a fact at a stated grain, its dimensions around it.
439
+ { id: 'dim_account', name: 'dim_account', ...grid(6, 0), role: 'dimension', scdType: '2', description: 'An account as it was over time: a row per version.', attributes: [
440
+ attr('account_key', 'PK', 'BIGINT'), { ...attr('arrangement_id', 'FK', 'BIGINT'), references: 'account' }, attr('valid_from', '', 'DATE'), attr('valid_to', '', 'DATE'),
441
+ ] },
442
+ { id: 'dim_date', name: 'dim_date', ...grid(7, 0), role: 'dimension', scdType: '0', conformed: true, description: 'A calendar day, shared by every fact.', attributes: [
443
+ attr('date_key', 'PK', 'INT'), attr('calendar_date', '', 'DATE'), attr('month', '', 'INT'), attr('year', '', 'INT'),
444
+ ] },
445
+ { id: 'fact_daily_balance', name: 'fact_daily_balance', ...grid(6.5, 1.5), role: 'fact', grain: 'One row per account per day', attributes: [
446
+ { ...attr('date_key', 'PK FK', 'INT'), references: 'dim_date' }, { ...attr('account_key', 'PK FK', 'BIGINT'), references: 'dim_account' },
447
+ { ...attr('balance', '', 'DECIMAL(18,2)'), measure: 'semi-additive' }, { ...attr('interest_accrued', '', 'DECIMAL(18,2)'), measure: 'additive' },
448
+ ] },
449
+ ],
450
+ relationships: [
451
+ { source: 'account', target: 'dim_account', name: 'versioned as', sourceCardinality: '1', targetCardinality: '1..*', identifying: false },
452
+ { source: 'dim_account', target: 'fact_daily_balance', name: 'balance of', sourceCardinality: '1', targetCardinality: '0..*' },
453
+ { source: 'dim_date', target: 'fact_daily_balance', name: 'balance on', sourceCardinality: '1', targetCardinality: '0..*' },
454
+ { source: 'party', target: 'customer_agreement', name: 'signs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
455
+ { source: 'customer_agreement', target: 'account', name: 'governs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
456
+ { source: 'account', target: 'account_interest', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
457
+ { source: 'account', target: 'account_mandate', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
458
+ { source: 'account', target: 'account_fee', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
459
+ { source: 'account', target: 'loan_schedule', name: 'schedules', sourceCardinality: '1', targetCardinality: '0..*' },
460
+ { source: 'account', target: 'payment_order', name: 'pays by', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
461
+ { source: 'product', target: 'account', name: 'is provided as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
462
+ ],
463
+ },
464
+ },
465
+ // Many to one: both arrangements in one account table, a loan's schedule in one table,
466
+ // a party's reference data and demographics in its own row.
467
+ mappings: [
468
+ { from: 'conceptual', to: 'logical', pairs: [
469
+ ['party', 'party-entry'], ['party', 'party-reference'], ['party', 'demographics'],
470
+ ['agreement', 'customer-agreement'], ['agreement', 'agreement-term'],
471
+ ['arrangement', 'current-account'], ['arrangement', 'consumer-loan'], ['arrangement', 'interest'],
472
+ ['arrangement', 'account-payments'], ['arrangement', 'disbursement'], ['arrangement', 'repayment'],
473
+ ['condition', 'service-fees'], ['condition', 'product-condition'], ['condition', 'agreement-term'],
474
+ ['payment', 'payment-order'], ['payment', 'compliance-check'], ['payment', 'transfer'], ['payment', 'account-payments'],
475
+ ['product', 'product-entry'], ['product', 'product-feature'],
476
+ ] },
477
+ { from: 'logical', to: 'physical', pairs: [
478
+ ['party-entry', 'party'], ['party-reference', 'party'], ['demographics', 'party'],
479
+ ['customer-agreement', 'customer_agreement'], ['agreement-term', 'customer_agreement'],
480
+ ['current-account', 'account'], ['consumer-loan', 'account'], ['interest', 'account_interest'],
481
+ ['account-payments', 'account_mandate'], ['service-fees', 'account_fee'],
482
+ ['disbursement', 'loan_schedule'], ['repayment', 'loan_schedule'],
483
+ ['payment-order', 'payment_order'], ['compliance-check', 'payment_order'], ['transfer', 'payment_order'],
484
+ ['product-entry', 'product'], ['product-feature', 'product'], ['product-condition', 'product'],
485
+ ] },
486
+ ],
487
+ /* Data types, as BIAN's semantic APIs have them (github.com/bian-official/public, Apache-2.0):
488
+ an enumeration's values with what each means, a structure's fields, the ISO 20022 type each is. */
489
+ dataTypes: [
490
+ { kind: 'enumeration', id: 'current-account-type', name: 'Current Account Type', description: "BIAN's Currentaccounttypevalues (some of them).", values: [
491
+ { name: 'PersonalCurrentAccount', description: 'For a person.' },
492
+ { name: 'BusinessCurrentAccount', description: 'Supports business transactions, payroll, invoicing, often with higher transaction limits.' },
493
+ { name: 'StudentCurrentAccount', description: 'Tailored for students, usually with no fees, free overdraft, and student-related perks.' },
494
+ { name: 'JointCurrentAccount', description: 'Shared between two or more account holders with equal access.' },
495
+ { name: 'ForeignCurrencyCurrentAccount', description: 'Maintains balances in foreign currencies for international needs.' },
496
+ { name: 'BasicBankAccount', description: 'Minimal feature account, no overdraft, designed for financial inclusion.' },
497
+ ] },
498
+ { kind: 'enumeration', id: 'payment-status', name: 'Payment Status', values: ['Pending', 'Accepted', 'Rejected', 'Completed', 'Cancelled'] },
499
+ { kind: 'structure', id: 'amount', name: 'Amount', description: 'An amount of money in a currency.', iso20022: 'ActiveCurrencyAndAmount', fields: [
500
+ { name: 'value', type: 'Decimal' }, { name: 'currency', type: 'Currency Code' },
501
+ ] },
502
+ { id: 'currency-code', name: 'Currency Code', basedOn: 'Text', description: 'A currency, as ISO 4217 codes it.', iso20022: 'CurrencyCode' },
503
+ { id: 'account-identification', name: 'Account Identification', basedOn: 'Text', description: 'An account number in any suitable form (an IBAN).', iso20022: 'AccountIdentification' },
504
+ ],
505
+ perspectives: [
506
+ { kind: 'classification', id: 'sensitive', name: 'Sensitive data', description: 'Every entity coloured by how sensitive it is: green public, blue internal, amber confidential, red restricted.' },
507
+ { kind: 'service-domain', id: 'sd-current-account', name: 'Current Account', businessArea: 'Operations and Execution', businessDomain: 'Loans and Deposits',
508
+ asset: 'Current Account', pattern: 'Fulfill', entities: ['arrangement', 'product', 'condition'], controlRecord: 'current-account',
509
+ qualifiers: [{ name: 'Interest', entity: 'interest' }, { name: 'Payments', entity: 'account-payments' }, { name: 'Service Fees', entity: 'service-fees' }] },
510
+ { kind: 'service-domain', id: 'sd-consumer-loan', name: 'Consumer Loan', businessArea: 'Operations and Execution', businessDomain: 'Loans and Deposits',
511
+ asset: 'Consumer Loan', pattern: 'Fulfill', entities: ['arrangement', 'product'], controlRecord: 'consumer-loan',
512
+ qualifiers: [{ name: 'Disbursement', entity: 'disbursement' }, { name: 'Repayment', entity: 'repayment' }] },
513
+ { kind: 'service-domain', id: 'sd-customer-agreement', name: 'Customer Agreement', businessArea: 'Sales and Service', businessDomain: 'Customer Management',
514
+ asset: 'Customer', pattern: 'Agree Terms', entities: ['party', 'agreement'], controlRecord: 'customer-agreement',
515
+ qualifiers: [{ name: 'Terms', entity: 'agreement-term' }] },
516
+ { kind: 'service-domain', id: 'sd-payment-order', name: 'Payment Order', businessArea: 'Operations and Execution', businessDomain: 'Payments',
517
+ asset: 'Payment Order', pattern: 'Process', entities: ['payment', 'arrangement'], controlRecord: 'payment-order',
518
+ qualifiers: [{ name: 'Compliance Check', entity: 'compliance-check' }, { name: 'Transfer', entity: 'transfer' }] },
519
+ { kind: 'service-domain', id: 'sd-party-directory', name: 'Party Reference Data Directory', businessArea: 'Sales and Service', businessDomain: 'Customer Management',
520
+ asset: 'Party Reference Data', pattern: 'Catalog', entities: ['party'], controlRecord: 'party-entry',
521
+ qualifiers: [{ name: 'Reference', entity: 'party-reference' }, { name: 'Demographics', entity: 'demographics' }] },
522
+ { kind: 'service-domain', id: 'sd-product-directory', name: 'Product Directory', businessArea: 'Reference Data', businessDomain: 'Product Management',
523
+ asset: 'Product', pattern: 'Catalog', entities: ['product', 'condition'], controlRecord: 'product-entry',
524
+ qualifiers: [{ name: 'Features', entity: 'product-feature' }, { name: 'Conditions', entity: 'product-condition' }] },
525
+ ],
526
+ contexts: [],
527
+ level: 'logical', notation: 'crowsfoot',
528
+ };
529
+
309
530
  const ws = document.body.appendChild(el({
310
531
  tag: 'ws-workspace',
311
532
  attrs: { label: 'Data Model', theme: 'light' },
@@ -313,11 +534,22 @@
313
534
  }));
314
535
  registerBuiltinParts(ws);
315
536
  ws.registerPart(DataModelPart);
316
- ws.addParts([{
317
- id: 'model', type: 'data-model', title: 'Enterprise ontology',
537
+ // Several data models open at once, a tab each (File > New model). keep: each is kept in this browser
538
+ // as it changes and reopens next time; File > This browser backs them up (to a folder chosen once,
539
+ // such as one OneDrive or Google Drive syncs). File > Save writes a .datamodel.json.
540
+ // The enterprise ontology is the base: kept as given, File > New > From base makes a model from a
541
+ // copy of it, and Tools > Compare with / Merge from offer it beside the other open models.
542
+ const ontology = {
543
+ name: 'Enterprise ontology',
318
544
  levels: { conceptual, logical, physical }, mappings, perspectives, contexts, level: 'conceptual', notation: 'crowsfoot',
545
+ };
546
+ ws.addParts([{
547
+ id: 'models', type: 'models', editorType: 'data-model', keep: true,
548
+ newModel: { name: 'New data model', level: 'conceptual' },
549
+ bases: [ontology, banking],
550
+ models: [{ id: 'model', ...ontology }],
319
551
  }]);
320
- ws.registerPerspective('Data model', { type: 'part', partId: 'model' });
552
+ ws.registerPerspective('Data model', { type: 'part', partId: 'models' });
321
553
  ws.activatePerspective('Data model');
322
554
  </script>
323
555
  </body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peter.prib/renderengine",
3
- "version": "0.2.6",
3
+ "version": "0.2.10",
4
4
  "description": "ArchiMate modeller, diagram editor and chart library for the browser, with no runtime dependencies.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",