@peter.prib/renderengine 0.2.6 → 0.2.7

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 (49) hide show
  1. package/README.md +4 -0
  2. package/REFERENCE.md +180 -2
  3. package/dist/renderengine-workspace.umd.js +8704 -5310
  4. package/dist/renderengine.umd.js +5675 -3398
  5. package/examples/archimate.html +68 -66
  6. package/examples/data-model.html +223 -13
  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/ModelCheckDialog.js +63 -0
  12. package/src/charting/ModelChecker.js +26 -0
  13. package/src/charting/ViewTabStrip.js +94 -81
  14. package/src/charting/datamodel/BianLandscapeReader.js +185 -0
  15. package/src/charting/datamodel/BianSemanticApiReader.js +183 -0
  16. package/src/charting/datamodel/DataModel.js +356 -10
  17. package/src/charting/datamodel/DataModelChecker.js +92 -0
  18. package/src/charting/datamodel/DataModelCompared.js +127 -0
  19. package/src/charting/datamodel/DataModelFileFormat.js +33 -0
  20. package/src/charting/datamodel/DataModelPerspective.js +45 -0
  21. package/src/charting/datamodel/DataType.js +108 -0
  22. package/src/charting/datamodel/EnumerationType.js +51 -0
  23. package/src/charting/datamodel/ServiceDomain.js +177 -0
  24. package/src/charting/datamodel/ServiceLandscape.js +104 -0
  25. package/src/charting/datamodel/StructuredType.js +63 -0
  26. package/src/charting/datamodel/index.js +10 -0
  27. package/src/charting/diagramModel/MergeModelCommand.js +43 -37
  28. package/src/charting/diagramModel/OpenViewList.js +75 -0
  29. package/src/charting/diagramModel/ViewSetChecker.js +22 -34
  30. package/src/charting/diagrams/DataModelRelationshipStrategy.js +12 -4
  31. package/src/charting/diagrams/EdgeGeometry.js +578 -553
  32. package/src/charting/diagrams/GraphDiagram.js +11 -1
  33. package/src/charting/diagrams/NodeGeometry.js +169 -158
  34. package/src/charting/glyphs/PersonGlyph.js +24 -23
  35. package/src/index.js +2666 -2664
  36. package/src/workspace/EditorShell.js +166 -67
  37. package/src/workspace/PanelLayout.js +56 -0
  38. package/src/workspace/layout/TabsNode.js +205 -191
  39. package/src/workspace/parts/ArchimateCanvasPart.js +5 -7
  40. package/src/workspace/parts/DataModelPart.js +339 -29
  41. package/src/workspace/parts/DiagramEditorPart.js +2 -8
  42. package/src/workspace/parts/EditorPart.js +461 -407
  43. package/src/workspace/parts/ModelFile.js +216 -187
  44. package/src/workspace/parts/ModelsPart.js +483 -192
  45. package/src/workspace/storage/BackupFolder.js +145 -0
  46. package/src/workspace/storage/KeyValueStore.js +92 -0
  47. package/src/workspace/storage/ModelLibrary.js +65 -0
  48. package/src/workspace/workArea.js +417 -413
  49. 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 },
@@ -93,7 +118,10 @@
93
118
  { id: 'person', name: 'Person', ...at(0, 0), attributes: [attr('party id', 'PK'), attr('given name'), attr('family name'), attr('born on')] },
94
119
  { id: 'organisation', name: 'Organisation', ...at(1, 0), attributes: [attr('party id', 'PK'), attr('legal name'), attr('registration number')] },
95
120
  { 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')] },
121
+ { id: 'arrangement', name: 'Arrangement', ...at(2, 0), attributes: [attr('arrangement id', 'PK'), attr('agreement id', 'FK'), attr('type', 'FK'), attr('starts'), attr('ends')] },
122
+ { id: 'agreement', name: 'Agreement', ...at(1, 3), attributes: [attr('agreement id', 'PK'), attr('party id', 'FK'), attr('signed on'), attr('terms')] },
123
+ { id: 'product-feature', name: 'Product Feature', ...at(2, 3), attributes: [attr('product id', 'PK FK'), attr('feature', 'PK'), attr('value')] },
124
+ { id: 'condition', name: 'Condition', ...at(3, 3), attributes: [attr('condition code', 'PK'), attr('kind'), attr('rate'), attr('applies to', 'FK')] },
97
125
  { 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
126
  { 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
127
  { id: 'place', name: 'Place', ...at(4, 0), attributes: [attr('place id', 'PK'), attr('name'), attr('location'), attr('time zone')] },
@@ -120,6 +148,9 @@
120
148
  { source: 'event', target: 'ledger-entry', name: 'is recorded in', sourceCardinality: '1', targetCardinality: '1..*' },
121
149
  { source: 'account', target: 'ledger-entry', name: 'is posted with', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
122
150
  { source: 'scheme', target: 'code', name: 'holds', sourceCardinality: '1', targetCardinality: '1..*' },
151
+ { source: 'agreement', target: 'arrangement', name: 'governs', sourceCardinality: '1', targetCardinality: '1..*' },
152
+ { source: 'product', target: 'product-feature', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
153
+ { source: 'condition', target: 'product', name: 'applies to', sourceCardinality: '*', targetCardinality: '*', identifying: false },
123
154
  { source: 'product', target: 'arrangement', name: 'is sold as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
124
155
  { source: 'product', target: 'product-price', name: 'is priced by', sourceCardinality: '1', targetCardinality: '1..*' },
125
156
  { source: 'product', target: 'product-component', name: 'is made of', sourceCardinality: '1', targetCardinality: '0..*' },
@@ -168,6 +199,15 @@
168
199
  { id: 'product_price', name: 'product_price', ...at(4, 1), attributes: [
169
200
  attr('product_id', 'PK FK', 'BIGINT'), attr('valid_from', 'PK', 'DATE'), attr('amount', '', 'DECIMAL(12,2)'), attr('currency', '', 'CHAR(3)'),
170
201
  ] },
202
+ { id: 'agreement', name: 'agreement', ...at(0, 3), attributes: [
203
+ attr('agreement_id', 'PK', 'BIGINT'), attr('party_id', 'FK', 'BIGINT'), attr('signed_on', '', 'DATE'), attr('terms', '', 'JSONB'),
204
+ ] },
205
+ { id: 'product_feature', name: 'product_feature', ...at(1, 3), attributes: [
206
+ attr('product_id', 'PK FK', 'BIGINT'), attr('feature', 'PK', 'VARCHAR(40)'), attr('value', '', 'TEXT'),
207
+ ] },
208
+ { id: 'condition', name: 'condition', ...at(2, 3), attributes: [
209
+ attr('condition_code', 'PK', 'VARCHAR(20)'), attr('kind', '', 'VARCHAR(12)'), attr('rate', '', 'DECIMAL(9,4)'), attr('product_id', 'FK', 'BIGINT'),
210
+ ] },
171
211
  { id: 'business_direction', name: 'business_direction', ...at(0, 2), attributes: [
172
212
  attr('direction_id', 'PK', 'BIGINT'), attr('kind', '', 'VARCHAR(20)'), attr('statement', '', 'TEXT'),
173
213
  attr('parent_id', 'FK', 'BIGINT'), attr('horizon', '', 'DATE'),
@@ -196,6 +236,9 @@
196
236
  { source: 'product', target: 'arrangement', name: 'is sold as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
197
237
  { source: 'product', target: 'product_price', name: 'is priced by', sourceCardinality: '1', targetCardinality: '1..*' },
198
238
  { source: 'business_direction', target: 'direction_target', name: 'is measured by', sourceCardinality: '1', targetCardinality: '0..*' },
239
+ { source: 'agreement', target: 'arrangement', name: 'governs', sourceCardinality: '1', targetCardinality: '1..*' },
240
+ { source: 'product', target: 'product_feature', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
241
+ { source: 'product', target: 'condition', name: 'is subject to', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
199
242
  { source: 'product', target: 'direction_target', name: 'is aimed at by', sourceCardinality: '0..1', targetCardinality: '0..*', identifying: false },
200
243
  ],
201
244
  };
@@ -209,6 +252,7 @@
209
252
  ['ledger', 'account'], ['ledger', 'ledger-entry'], ['classification', 'scheme'], ['classification', 'code'],
210
253
  ['product', 'product'], ['product', 'product-price'], ['product', 'product-component'], ['resource', 'product-component'],
211
254
  ['direction', 'direction'], ['direction', 'target'],
255
+ ['agreement', 'agreement'], ['feature', 'product-feature'], ['condition', 'condition'],
212
256
  ] },
213
257
  { from: 'logical', to: 'physical', pairs: [
214
258
  ['person', 'party'], ['organisation', 'party'], ['party-role', 'party_role'], ['participation', 'arrangement_party'],
@@ -217,6 +261,7 @@
217
261
  ['scheme', 'classification'], ['code', 'classification'],
218
262
  ['product', 'product'], ['product-component', 'product'], ['product-price', 'product_price'],
219
263
  ['direction', 'business_direction'], ['target', 'direction_target'],
264
+ ['agreement', 'agreement'], ['product-feature', 'product_feature'], ['condition', 'condition'],
220
265
  ] },
221
266
  ];
222
267
 
@@ -224,13 +269,13 @@
224
269
  const perspectives = [
225
270
  {
226
271
  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' },
272
+ entities: ['party', 'role', 'agreement', 'arrangement', 'product', 'resource', 'event', 'ledger'],
273
+ names: { party: 'Customer', role: 'Subscriber', agreement: 'Contract', arrangement: 'Subscription', product: 'Plan', resource: 'Device', event: 'Usage', ledger: 'Billing' },
229
274
  relationshipNames: { covers: 'includes', records: 'is billed in' },
230
275
  },
231
276
  {
232
277
  id: 'regulatory', name: 'Regulatory', context: 'regulatory', description: 'What a regulator holds us to',
233
- entities: ['party', 'arrangement', 'event', 'ledger', 'rule', 'classification', 'place'],
278
+ entities: ['party', 'agreement', 'arrangement', 'event', 'ledger', 'rule', 'classification', 'place'],
234
279
  names: { party: 'Legal Entity', arrangement: 'Compliance Obligation', event: 'Reportable Event', ledger: 'Audit Trail', rule: 'Regulation', place: 'Jurisdiction' },
235
280
  relationshipNames: { imposes: 'imposes', records: 'is evidenced by' },
236
281
  },
@@ -306,6 +351,160 @@
306
351
  },
307
352
  ];
308
353
 
354
+ // A retail-banking slice at BIAN level: BIAN service domains (each a kind of perspective) over the
355
+ // concepts, each managing its control record, broken down by behaviour qualifiers, at the logical level.
356
+ // Illustrative: the service domains and patterns are BIAN's; the attributes are a sketch.
357
+ const grid = (column, row) => ({ x: 130 + column * 290, y: 100 + row * 230 });
358
+ const banking = {
359
+ name: 'Retail banking (BIAN)',
360
+ levels: {
361
+ conceptual: {
362
+ entities: [
363
+ { 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.' },
364
+ { 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.' },
365
+ { 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.' },
366
+ { 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.' },
367
+ { 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.' },
368
+ { id: 'payment', name: 'Payment', ...grid(2, 1), shape: 'ellipse', color: '#dbeafe', description: 'Money moved from one account to another.' },
369
+ ],
370
+ relationships: [
371
+ { source: 'party', target: 'agreement', name: 'signs', sourceCardinality: '1..*', targetCardinality: '*' },
372
+ { source: 'agreement', target: 'arrangement', name: 'governs', sourceCardinality: '1', targetCardinality: '1..*' },
373
+ { source: 'arrangement', target: 'product', name: 'provides', sourceCardinality: '*', targetCardinality: '1', identifying: false },
374
+ { source: 'product', target: 'condition', name: 'is subject to', sourceCardinality: '*', targetCardinality: '*', identifying: false },
375
+ { source: 'arrangement', target: 'payment', name: 'makes', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
376
+ ],
377
+ },
378
+ logical: {
379
+ entities: [
380
+ { id: 'party-entry', name: 'Party Reference Data Directory Entry', ...grid(0, 0), attributes: [attr('party id', 'PK'), attr('legal name'), attr('kind')] },
381
+ { id: 'party-reference', name: 'Reference', ...grid(0, 1), attributes: [attr('party id', 'PK FK'), attr('tax number'), attr('passport')] },
382
+ { id: 'demographics', name: 'Demographics', ...grid(0, 2), attributes: [attr('party id', 'PK FK'), attr('born on'), attr('occupation')] },
383
+ { id: 'customer-agreement', name: 'Customer Agreement', ...grid(1, 0), attributes: [attr('agreement id', 'PK'), attr('party id', 'FK'), attr('signed on'), attr('status')] },
384
+ { id: 'agreement-term', name: 'Agreement Term', ...grid(1, 1), attributes: [attr('agreement id', 'PK FK'), attr('term', 'PK'), attr('value')] },
385
+ { 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')] },
386
+ { id: 'interest', name: 'Interest', ...grid(2, 1), attributes: [attr('arrangement id', 'PK FK'), attr('rate'), attr('accrual basis')] },
387
+ { id: 'account-payments', name: 'Payments', ...grid(2, 2), attributes: [attr('arrangement id', 'PK FK'), attr('mandate', 'PK'), attr('kind'), attr('amount', '', 'Amount')] },
388
+ { id: 'service-fees', name: 'Service Fees', ...grid(2, 3), attributes: [attr('arrangement id', 'PK FK'), attr('fee code', 'PK'), attr('amount', '', 'Amount')] },
389
+ { 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')] },
390
+ { id: 'disbursement', name: 'Disbursement', ...grid(3, 1), attributes: [attr('arrangement id', 'PK FK'), attr('paid on', 'PK'), attr('amount', '', 'Amount')] },
391
+ { id: 'repayment', name: 'Repayment', ...grid(3, 2), attributes: [attr('arrangement id', 'PK FK'), attr('due on', 'PK'), attr('amount', '', 'Amount'), attr('paid')] },
392
+ { 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')] },
393
+ { id: 'compliance-check', name: 'Compliance Check', ...grid(4, 1), attributes: [attr('payment id', 'PK FK'), attr('outcome'), attr('checked at')] },
394
+ { id: 'transfer', name: 'Transfer', ...grid(4, 2), attributes: [attr('payment id', 'PK FK'), attr('scheme'), attr('settled at')] },
395
+ { id: 'product-entry', name: 'Product Directory Entry', ...grid(5, 0), attributes: [attr('product id', 'PK'), attr('name'), attr('line')] },
396
+ { id: 'product-feature', name: 'Features', ...grid(5, 1), attributes: [attr('product id', 'PK FK'), attr('feature', 'PK'), attr('value')] },
397
+ { id: 'product-condition', name: 'Conditions', ...grid(5, 2), attributes: [attr('product id', 'PK FK'), attr('condition', 'PK'), attr('rule')] },
398
+ ],
399
+ relationships: [
400
+ { source: 'party-entry', target: 'party-reference', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
401
+ { source: 'party-entry', target: 'demographics', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
402
+ { source: 'party-entry', target: 'customer-agreement', name: 'signs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
403
+ { source: 'customer-agreement', target: 'agreement-term', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
404
+ { source: 'customer-agreement', target: 'current-account', name: 'governs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
405
+ { source: 'customer-agreement', target: 'consumer-loan', name: 'governs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
406
+ { source: 'current-account', target: 'interest', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
407
+ { source: 'current-account', target: 'account-payments', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
408
+ { source: 'current-account', target: 'service-fees', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
409
+ { source: 'consumer-loan', target: 'disbursement', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
410
+ { source: 'consumer-loan', target: 'repayment', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
411
+ { source: 'current-account', target: 'payment-order', name: 'pays by', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
412
+ { source: 'payment-order', target: 'compliance-check', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
413
+ { source: 'payment-order', target: 'transfer', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
414
+ { source: 'product-entry', target: 'product-feature', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
415
+ { source: 'product-entry', target: 'product-condition', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
416
+ { source: 'product-entry', target: 'current-account', name: 'is provided as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
417
+ { source: 'product-entry', target: 'consumer-loan', name: 'is provided as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
418
+ ],
419
+ },
420
+ physical: {
421
+ entities: [
422
+ { id: 'party', name: 'party', ...grid(0, 0), 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')] },
423
+ { id: 'customer_agreement', name: 'customer_agreement', ...grid(1, 0), attributes: [attr('agreement_id', 'PK', 'BIGINT'), attr('party_id', 'FK', 'BIGINT'), attr('signed_on', '', 'DATE'), attr('status', '', 'VARCHAR(12)'), attr('terms', '', 'JSONB')] },
424
+ { id: 'account', name: 'account', ...grid(2, 0), attributes: [attr('arrangement_id', 'PK', 'BIGINT'), attr('agreement_id', 'FK', 'BIGINT'), attr('product_id', 'FK', 'BIGINT'), attr('kind', '', 'CHAR(1)'), attr('balance', '', 'DECIMAL(18,2)'), attr('opened_on', '', 'DATE')] },
425
+ { id: 'account_interest', name: 'account_interest', ...grid(2, 1), attributes: [attr('arrangement_id', 'PK FK', 'BIGINT'), attr('rate', '', 'DECIMAL(7,4)'), attr('basis', '', 'VARCHAR(10)')] },
426
+ { id: 'account_mandate', name: 'account_mandate', ...grid(2, 2), attributes: [attr('arrangement_id', 'PK FK', 'BIGINT'), attr('mandate', 'PK', 'VARCHAR(30)'), attr('kind', '', 'VARCHAR(12)'), attr('amount', '', 'DECIMAL(18,2)')] },
427
+ { id: 'account_fee', name: 'account_fee', ...grid(2, 3), attributes: [attr('arrangement_id', 'PK FK', 'BIGINT'), attr('fee_code', 'PK', 'VARCHAR(12)'), attr('amount', '', 'DECIMAL(12,2)')] },
428
+ { id: 'loan_schedule', name: 'loan_schedule', ...grid(3, 1), attributes: [attr('arrangement_id', 'PK FK', 'BIGINT'), attr('due_on', 'PK', 'DATE'), attr('kind', '', 'CHAR(1)'), attr('amount', '', 'DECIMAL(18,2)'), attr('paid', '', 'BOOLEAN')] },
429
+ { 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')] },
430
+ { 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')] },
431
+ ],
432
+ relationships: [
433
+ { source: 'party', target: 'customer_agreement', name: 'signs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
434
+ { source: 'customer_agreement', target: 'account', name: 'governs', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
435
+ { source: 'account', target: 'account_interest', name: 'has', sourceCardinality: '1', targetCardinality: '0..1' },
436
+ { source: 'account', target: 'account_mandate', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
437
+ { source: 'account', target: 'account_fee', name: 'has', sourceCardinality: '1', targetCardinality: '0..*' },
438
+ { source: 'account', target: 'loan_schedule', name: 'schedules', sourceCardinality: '1', targetCardinality: '0..*' },
439
+ { source: 'account', target: 'payment_order', name: 'pays by', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
440
+ { source: 'product', target: 'account', name: 'is provided as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
441
+ ],
442
+ },
443
+ },
444
+ // Many to one: both arrangements in one account table, a loan's schedule in one table,
445
+ // a party's reference data and demographics in its own row.
446
+ mappings: [
447
+ { from: 'conceptual', to: 'logical', pairs: [
448
+ ['party', 'party-entry'], ['party', 'party-reference'], ['party', 'demographics'],
449
+ ['agreement', 'customer-agreement'], ['agreement', 'agreement-term'],
450
+ ['arrangement', 'current-account'], ['arrangement', 'consumer-loan'], ['arrangement', 'interest'],
451
+ ['arrangement', 'account-payments'], ['arrangement', 'disbursement'], ['arrangement', 'repayment'],
452
+ ['condition', 'service-fees'], ['condition', 'product-condition'], ['condition', 'agreement-term'],
453
+ ['payment', 'payment-order'], ['payment', 'compliance-check'], ['payment', 'transfer'], ['payment', 'account-payments'],
454
+ ['product', 'product-entry'], ['product', 'product-feature'],
455
+ ] },
456
+ { from: 'logical', to: 'physical', pairs: [
457
+ ['party-entry', 'party'], ['party-reference', 'party'], ['demographics', 'party'],
458
+ ['customer-agreement', 'customer_agreement'], ['agreement-term', 'customer_agreement'],
459
+ ['current-account', 'account'], ['consumer-loan', 'account'], ['interest', 'account_interest'],
460
+ ['account-payments', 'account_mandate'], ['service-fees', 'account_fee'],
461
+ ['disbursement', 'loan_schedule'], ['repayment', 'loan_schedule'],
462
+ ['payment-order', 'payment_order'], ['compliance-check', 'payment_order'], ['transfer', 'payment_order'],
463
+ ['product-entry', 'product'], ['product-feature', 'product'], ['product-condition', 'product'],
464
+ ] },
465
+ ],
466
+ /* Data types, as BIAN's semantic APIs have them (github.com/bian-official/public, Apache-2.0):
467
+ an enumeration's values with what each means, a structure's fields, the ISO 20022 type each is. */
468
+ dataTypes: [
469
+ { kind: 'enumeration', id: 'current-account-type', name: 'Current Account Type', description: "BIAN's Currentaccounttypevalues (some of them).", values: [
470
+ { name: 'PersonalCurrentAccount', description: 'For a person.' },
471
+ { name: 'BusinessCurrentAccount', description: 'Supports business transactions, payroll, invoicing, often with higher transaction limits.' },
472
+ { name: 'StudentCurrentAccount', description: 'Tailored for students, usually with no fees, free overdraft, and student-related perks.' },
473
+ { name: 'JointCurrentAccount', description: 'Shared between two or more account holders with equal access.' },
474
+ { name: 'ForeignCurrencyCurrentAccount', description: 'Maintains balances in foreign currencies for international needs.' },
475
+ { name: 'BasicBankAccount', description: 'Minimal feature account, no overdraft, designed for financial inclusion.' },
476
+ ] },
477
+ { kind: 'enumeration', id: 'payment-status', name: 'Payment Status', values: ['Pending', 'Accepted', 'Rejected', 'Completed', 'Cancelled'] },
478
+ { kind: 'structure', id: 'amount', name: 'Amount', description: 'An amount of money in a currency.', iso20022: 'ActiveCurrencyAndAmount', fields: [
479
+ { name: 'value', type: 'Decimal' }, { name: 'currency', type: 'Currency Code' },
480
+ ] },
481
+ { id: 'currency-code', name: 'Currency Code', basedOn: 'Text', description: 'A currency, as ISO 4217 codes it.', iso20022: 'CurrencyCode' },
482
+ { id: 'account-identification', name: 'Account Identification', basedOn: 'Text', description: 'An account number in any suitable form (an IBAN).', iso20022: 'AccountIdentification' },
483
+ ],
484
+ perspectives: [
485
+ { kind: 'service-domain', id: 'sd-current-account', name: 'Current Account', businessArea: 'Operations and Execution', businessDomain: 'Loans and Deposits',
486
+ asset: 'Current Account', pattern: 'Fulfill', entities: ['arrangement', 'product', 'condition'], controlRecord: 'current-account',
487
+ qualifiers: [{ name: 'Interest', entity: 'interest' }, { name: 'Payments', entity: 'account-payments' }, { name: 'Service Fees', entity: 'service-fees' }] },
488
+ { kind: 'service-domain', id: 'sd-consumer-loan', name: 'Consumer Loan', businessArea: 'Operations and Execution', businessDomain: 'Loans and Deposits',
489
+ asset: 'Consumer Loan', pattern: 'Fulfill', entities: ['arrangement', 'product'], controlRecord: 'consumer-loan',
490
+ qualifiers: [{ name: 'Disbursement', entity: 'disbursement' }, { name: 'Repayment', entity: 'repayment' }] },
491
+ { kind: 'service-domain', id: 'sd-customer-agreement', name: 'Customer Agreement', businessArea: 'Sales and Service', businessDomain: 'Customer Management',
492
+ asset: 'Customer', pattern: 'Agree Terms', entities: ['party', 'agreement'], controlRecord: 'customer-agreement',
493
+ qualifiers: [{ name: 'Terms', entity: 'agreement-term' }] },
494
+ { kind: 'service-domain', id: 'sd-payment-order', name: 'Payment Order', businessArea: 'Operations and Execution', businessDomain: 'Payments',
495
+ asset: 'Payment Order', pattern: 'Process', entities: ['payment', 'arrangement'], controlRecord: 'payment-order',
496
+ qualifiers: [{ name: 'Compliance Check', entity: 'compliance-check' }, { name: 'Transfer', entity: 'transfer' }] },
497
+ { kind: 'service-domain', id: 'sd-party-directory', name: 'Party Reference Data Directory', businessArea: 'Sales and Service', businessDomain: 'Customer Management',
498
+ asset: 'Party Reference Data', pattern: 'Catalog', entities: ['party'], controlRecord: 'party-entry',
499
+ qualifiers: [{ name: 'Reference', entity: 'party-reference' }, { name: 'Demographics', entity: 'demographics' }] },
500
+ { kind: 'service-domain', id: 'sd-product-directory', name: 'Product Directory', businessArea: 'Reference Data', businessDomain: 'Product Management',
501
+ asset: 'Product', pattern: 'Catalog', entities: ['product', 'condition'], controlRecord: 'product-entry',
502
+ qualifiers: [{ name: 'Features', entity: 'product-feature' }, { name: 'Conditions', entity: 'product-condition' }] },
503
+ ],
504
+ contexts: [],
505
+ level: 'logical', notation: 'crowsfoot',
506
+ };
507
+
309
508
  const ws = document.body.appendChild(el({
310
509
  tag: 'ws-workspace',
311
510
  attrs: { label: 'Data Model', theme: 'light' },
@@ -313,11 +512,22 @@
313
512
  }));
314
513
  registerBuiltinParts(ws);
315
514
  ws.registerPart(DataModelPart);
316
- ws.addParts([{
317
- id: 'model', type: 'data-model', title: 'Enterprise ontology',
515
+ // Several data models open at once, a tab each (File > New model). keep: each is kept in this browser
516
+ // as it changes and reopens next time; File > This browser backs them up (to a folder chosen once,
517
+ // such as one OneDrive or Google Drive syncs). File > Save writes a .datamodel.json.
518
+ // The enterprise ontology is the base: kept as given, File > New > From base makes a model from a
519
+ // copy of it, and Tools > Compare with / Merge from offer it beside the other open models.
520
+ const ontology = {
521
+ name: 'Enterprise ontology',
318
522
  levels: { conceptual, logical, physical }, mappings, perspectives, contexts, level: 'conceptual', notation: 'crowsfoot',
523
+ };
524
+ ws.addParts([{
525
+ id: 'models', type: 'models', editorType: 'data-model', keep: true,
526
+ newModel: { name: 'New data model', level: 'conceptual' },
527
+ bases: [ontology, banking],
528
+ models: [{ id: 'model', ...ontology }],
319
529
  }]);
320
- ws.registerPerspective('Data model', { type: 'part', partId: 'model' });
530
+ ws.registerPerspective('Data model', { type: 'part', partId: 'models' });
321
531
  ws.activatePerspective('Data model');
322
532
  </script>
323
533
  </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.7",
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",