@peter.prib/renderengine 0.5.0 → 0.8.0

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 (74) hide show
  1. package/README.md +4 -0
  2. package/REFERENCE.md +297 -17
  3. package/dist/renderengine-workspace.umd.js +5369 -2952
  4. package/dist/renderengine.umd.js +4466 -2320
  5. package/examples/archimate.html +8 -28
  6. package/examples/data-model.html +45 -556
  7. package/package.json +70 -70
  8. package/src/archimateArchiFormat.js +11 -3
  9. package/src/archimateExchangeFormat.js +17 -9
  10. package/src/chartCatalog.js +10 -0
  11. package/src/charting/ArchimateDiagramEditor.js +1 -1
  12. package/src/charting/Axis.js +20 -0
  13. package/src/charting/ChartChooser.js +229 -0
  14. package/src/charting/DiagramEditor.js +35 -3
  15. package/src/charting/Dialog.js +195 -0
  16. package/src/charting/DuplicatesDialog.js +108 -0
  17. package/src/charting/FormDialog.js +28 -97
  18. package/src/charting/InteractionManager.js +3 -0
  19. package/src/charting/Legend.js +45 -2
  20. package/src/charting/MergeCompareDialog.js +81 -0
  21. package/src/charting/PopupMenu.js +12 -3
  22. package/src/charting/SettingsDialog.js +6 -2
  23. package/src/charting/SettingsManager.js +14 -1
  24. package/src/charting/archimate/ArchimateCanvasEditor.js +190 -15
  25. package/src/charting/archimate/ArchimatePalette.js +1 -1
  26. package/src/charting/charts/AreaChart.js +5 -2
  27. package/src/charting/charts/BarChart.js +5 -3
  28. package/src/charting/charts/BarChart3DOrbit.js +5 -7
  29. package/src/charting/charts/BubbleChart.js +5 -7
  30. package/src/charting/charts/BubbleChart3D.js +5 -7
  31. package/src/charting/charts/CandlestickChart.js +3 -1
  32. package/src/charting/charts/DensityChart.js +4 -6
  33. package/src/charting/charts/GroupedBarChart.js +2 -2
  34. package/src/charting/charts/LineChart.js +5 -2
  35. package/src/charting/charts/ParallelCoordinatesChart.js +2 -2
  36. package/src/charting/charts/PipeChart.js +4 -6
  37. package/src/charting/charts/RadarChart.js +5 -6
  38. package/src/charting/charts/ScatterChart.js +5 -7
  39. package/src/charting/charts/StackedBarChart.js +4 -2
  40. package/src/charting/charts/StackedBarChart3DOrbit.js +5 -7
  41. package/src/charting/charts/StreamGraphChart.js +5 -2
  42. package/src/charting/charts/TableChart.js +98 -0
  43. package/src/charting/charts/WaterfallChart.js +2 -1
  44. package/src/charting/content/ContentSource.js +129 -0
  45. package/src/charting/content/ContentSources.js +77 -0
  46. package/src/charting/datamodel/DataModel.js +116 -0
  47. package/src/charting/diagrams/ArchimateModel.js +38 -3
  48. package/src/charting/diagrams/ArchimateModelChecker.js +13 -4
  49. package/src/charting/diagrams/MergeElementsCommand.js +124 -101
  50. package/src/charting/diagrams/SvgZoom.js +20 -1
  51. package/src/charting/diagrams/duplicates.js +95 -13
  52. package/src/charting/duplicateSearchForm.js +58 -0
  53. package/src/charting/modelTree/ModelTree.js +239 -202
  54. package/src/charting/modelTree/ModelTreePanel.js +955 -919
  55. package/src/charting/modelTree/TreeBuilder.js +93 -45
  56. package/src/charting/modelTree/TreeViewMode.js +9 -6
  57. package/src/charting/modelTree/viewFolders.js +15 -6
  58. package/src/icons/IconSet.js +86 -0
  59. package/src/icons/index.js +59 -0
  60. package/src/index.js +86 -10
  61. package/src/workspace/dom.js +56 -42
  62. package/src/workspace/index.js +1 -1
  63. package/src/workspace/nav/NavEntry.js +42 -0
  64. package/src/workspace/nav/NavGroup.js +43 -0
  65. package/src/workspace/nav/NavItem.js +53 -0
  66. package/src/workspace/nav/NavMenu.js +143 -0
  67. package/src/workspace/nav/NavSection.js +17 -0
  68. package/src/workspace/nav/index.js +5 -0
  69. package/src/workspace/parts/ArchimateCanvasPart.js +8 -3
  70. package/src/workspace/parts/DataModelPart.js +91 -0
  71. package/src/workspace/parts/EditorPart.js +8 -0
  72. package/src/workspace/parts/ModelHosted.js +1 -0
  73. package/src/workspace/parts/ModelsPart.js +765 -622
  74. package/src/workspace/styles.js +28 -1
@@ -1,556 +1,45 @@
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>Data Model</title>
7
- <style>
8
- html, body { margin: 0; height: 100%; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
9
- body { display: flex; flex-direction: column; }
10
- </style>
11
- </head>
12
- <body>
13
- <!-- A data model (DataModelPart) in the workbench: the enterprise ontology at
14
- three levels, each its own schema, that do not map one to one (Party is a
15
- Person or an Organisation, both kept in one party table; a Ledger is
16
- Accounts and their Entries; a Participation realises Party, Arrangement and
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
20
- Direction is measured by targets; a Rule stays a concept). Level shows each one, or Mapping: the three
21
- side by side, joined where one realises another. Notation draws them in
22
- Chen, Crow's Foot or UML. It uses every feature: all five cardinalities,
23
- identifying (solid) and non-identifying (dashed) relationships, primary,
24
- foreign and composite keys, each column type group, a shape, colour and
25
- description per concept.
26
- One schema, read many ways: a perspective (Customer, Regulatory,
27
- Operations) shows the concepts a stakeholder sees under their names, and
28
- at the other levels what realises them; a context (Retail AU, Regulatory
29
- ASIC, Operations) says what they mean there, the rules that hold (notes)
30
- and where and when it applies. Choosing a perspective brings its context.
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.
46
- Drag the boxes and notes; they stay put, level by level. Classic scripts,
47
- so the page opens straight off disk; `npm run datamodel` opens it. -->
48
- <script src="../dist/renderengine.umd.js"></script>
49
- <script src="../dist/renderengine-workspace.umd.js"></script>
50
- <script>
51
- const { el, registerBuiltinParts, DataModelPart } = window.RenderEngineWorkspace;
52
-
53
- /** An attribute: `name`, then 'PK' / 'FK' / 'PK FK', and at the physical level a column type. */
54
- const attr = (name, keys = '', type) => ({
55
- name, ...(type && { type }), ...(keys.includes('PK') && { isPK: true }), ...(keys.includes('FK') && { isFK: true }),
56
- });
57
- /** Where a box sits on its level's diagram: a grid of columns and rows. */
58
- const at = (column, row, rowHeight = 250) => ({ x: 130 + column * 300, y: 100 + row * rowHeight });
59
-
60
- // Each level is its own schema. They do not map one to one: Party is a Person or an
61
- // Organisation, both kept in one party table; a Ledger is Accounts and their Entries; a
62
- // Participation realises Party, Arrangement and Role at once; a Rule stays a concept.
63
- const conceptual = {
64
- entities: [
65
- { id: 'party', name: 'Party', ...at(0, 0), color: '#ede9fe', shape: 'person', labelPosition: 'below', details: { bian: 'Party' },
66
- description: 'A person or legal actor that can act or hold rights.' },
67
- { id: 'role', name: 'Party Role', ...at(0, 1), color: '#fae8ff', shape: 'pill',
68
- description: 'The part a party plays: customer, supplier, employee. A party has many, over time.' },
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' },
78
- description: 'What the business offers and sells, under terms: a plan, a policy, a loan. The offer, not the things held.' },
79
- { id: 'resource', name: 'Resource', ...at(2, 0), color: '#dbeafe', shape: 'cube',
80
- description: 'A physical or digital thing of value: product, asset, document, device.' },
81
- { id: 'place', name: 'Place', ...at(3, 0), color: '#ffedd5', shape: 'pin', labelPosition: 'below',
82
- description: 'Where things happen or exist: a building, a room, a cloud region.' },
83
- { id: 'event', name: 'Event', ...at(2, 1), color: '#dcfce7', shape: 'ellipse',
84
- description: 'A change of state the business cares about: an order placed, a payment made.' },
85
- { id: 'ledger', name: 'Ledger', ...at(2, 2), color: '#f1f5f9', shape: 'cylinder',
86
- description: 'The quantitative impact of events, posted to accounts.' },
87
- { id: 'classification', name: 'Classification', ...at(3, 1), color: '#ccfbf1', shape: 'hexagon',
88
- description: 'A code in a scheme that types or grades parties, resources, events and arrangements.' },
89
- { id: 'direction', name: 'Business Direction', ...at(1.3, 2.3), color: '#fef3c7', shape: 'target', labelPosition: 'below',
90
- description: 'Where the business means to go: goals, strategies, plans, policies, and the targets that measure them.' },
91
- { id: 'rule', name: 'Rule', ...at(0.2, 2.3), color: '#fee2e2', shape: 'principle',
92
- description: 'A condition that governs arrangements and events. Kept conceptual: rules are policy, not tables.' },
93
- ],
94
- relationships: [
95
- { source: 'party', target: 'role', name: 'plays', sourceCardinality: '1', targetCardinality: '0..*' },
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 },
100
- { id: 'covers', source: 'arrangement', target: 'resource', name: 'governs', sourceCardinality: '*', targetCardinality: '*', identifying: false },
101
- { source: 'place', target: 'resource', name: 'locates', sourceCardinality: '0..1', targetCardinality: '0..*', identifying: false },
102
- { source: 'event', target: 'arrangement', name: 'changes', sourceCardinality: '0..*', targetCardinality: '0..1', identifying: false },
103
- { source: 'event', target: 'resource', name: 'changes', sourceCardinality: '0..*', targetCardinality: '0..1', identifying: false },
104
- { id: 'records', source: 'event', target: 'ledger', name: 'is recorded in', sourceCardinality: '1', targetCardinality: '1..*' },
105
- { source: 'classification', target: 'event', name: 'types', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
106
- { id: 'is-for', source: 'arrangement', target: 'product', name: 'is for', sourceCardinality: '*', targetCardinality: '1', identifying: false },
107
- { id: 'packages', source: 'product', target: 'resource', name: 'packages', sourceCardinality: '*', targetCardinality: '*', identifying: false },
108
- { source: 'classification', target: 'product', name: 'groups', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
109
- { id: 'shapes', source: 'direction', target: 'product', name: 'shapes', sourceCardinality: '*', targetCardinality: '*', identifying: false },
110
- { source: 'direction', target: 'rule', name: 'is enforced by', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
111
- { id: 'imposes', source: 'rule', target: 'arrangement', name: 'governs', sourceCardinality: '*', targetCardinality: '*', identifying: false },
112
- { source: 'rule', target: 'event', name: 'validates', sourceCardinality: '*', targetCardinality: '*', identifying: false },
113
- ],
114
- };
115
-
116
- const logical = {
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')] },
119
- { id: 'person', name: 'Person', ...at(0, 0), attributes: [attr('party id', 'PK'), attr('given name'), attr('family name'), attr('born on')] },
120
- { id: 'organisation', name: 'Organisation', ...at(1, 0), attributes: [attr('party id', 'PK'), attr('legal name'), attr('registration number')] },
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')] },
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')] },
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')] },
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')] },
128
- { id: 'place', name: 'Place', ...at(4, 0), attributes: [attr('place id', 'PK'), attr('name'), attr('location'), attr('time zone')] },
129
- { id: 'event', name: 'Event', ...at(3, 1), attributes: [attr('event id', 'PK'), attr('type', 'FK'), attr('occurred at'), attr('arrangement id', 'FK'), attr('resource id', 'FK'), attr('payload')] },
130
- { id: 'product', name: 'Product', ...at(2, 1), attributes: [attr('product id', 'PK'), attr('name'), attr('line', 'FK'), attr('launched on'), attr('retired on')] },
131
- { id: 'product-price', name: 'Product Price', ...at(1, 2), attributes: [attr('product id', 'PK FK'), attr('valid from', 'PK'), attr('amount'), attr('currency')] },
132
- { id: 'product-component', name: 'Product Component', ...at(5, 0), attributes: [attr('product id', 'PK FK'), attr('resource id', 'PK FK'), attr('quantity')] },
133
- { id: 'direction', name: 'Business Direction', ...at(0, 2), attributes: [attr('direction id', 'PK'), attr('kind'), attr('statement'), attr('parent id', 'FK'), attr('horizon')] },
134
- { id: 'target', name: 'Target', ...at(5, 1), attributes: [attr('direction id', 'PK FK'), attr('measure', 'PK'), attr('product id', 'FK'), attr('value'), attr('unit'), attr('due on')] },
135
- { id: 'account', name: 'Account', ...at(2, 2), attributes: [attr('account code', 'PK'), attr('name'), attr('kind')] },
136
- { id: 'ledger-entry', name: 'Ledger Entry', ...at(3, 2), attributes: [attr('event id', 'PK FK'), attr('line', 'PK'), attr('account code', 'FK'), attr('amount'), attr('posted on')] },
137
- { id: 'scheme', name: 'Classification Scheme', ...at(4, 1), attributes: [attr('scheme', 'PK'), attr('owner')] },
138
- { id: 'code', name: 'Classification Code', ...at(4, 2), attributes: [attr('scheme', 'PK FK'), attr('code', 'PK'), attr('label'), attr('parent code', 'FK')] },
139
- ],
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 },
143
- { source: 'person', target: 'party-role', name: 'plays', sourceCardinality: '1', targetCardinality: '0..*' },
144
- { source: 'organisation', target: 'party-role', name: 'plays', sourceCardinality: '1', targetCardinality: '0..*' },
145
- { source: 'arrangement', target: 'participation', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
146
- { source: 'party-role', target: 'participation', name: 'takes part as', sourceCardinality: '1', targetCardinality: '*', identifying: false },
147
- { source: 'arrangement', target: 'resource', name: 'governs', sourceCardinality: '*', targetCardinality: '*', identifying: false },
148
- { source: 'place', target: 'resource', name: 'locates', sourceCardinality: '0..1', targetCardinality: '0..*', identifying: false },
149
- { source: 'event', target: 'arrangement', name: 'changes', sourceCardinality: '0..*', targetCardinality: '0..1', identifying: false },
150
- { source: 'event', target: 'resource', name: 'changes', sourceCardinality: '0..*', targetCardinality: '0..1', identifying: false },
151
- { source: 'event', target: 'ledger-entry', name: 'is recorded in', sourceCardinality: '1', targetCardinality: '1..*' },
152
- { source: 'account', target: 'ledger-entry', name: 'is posted with', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
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 },
157
- { source: 'product', target: 'arrangement', name: 'is sold as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
158
- { source: 'product', target: 'product-price', name: 'is priced by', sourceCardinality: '1', targetCardinality: '1..*' },
159
- { source: 'product', target: 'product-component', name: 'is made of', sourceCardinality: '1', targetCardinality: '0..*' },
160
- { source: 'resource', target: 'product-component', name: 'goes into', sourceCardinality: '1', targetCardinality: '0..*' },
161
- { source: 'direction', target: 'target', name: 'is measured by', sourceCardinality: '1', targetCardinality: '0..*' },
162
- { source: 'product', target: 'target', name: 'is aimed at by', sourceCardinality: '0..1', targetCardinality: '0..*', identifying: false },
163
- { source: 'code', target: 'event', name: 'types', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
164
- ],
165
- };
166
-
167
- const physical = {
168
- entities: [
169
- { id: 'party', name: 'party', ...at(0, 0), attributes: [
170
- attr('party_id', 'PK', 'BIGINT'), attr('party_kind', '', 'CHAR(1)'), attr('display_name', '', 'VARCHAR(200)'),
171
- attr('born_on', '', 'DATE'), attr('registration_no', '', 'VARCHAR(20)'), attr('is_active', '', 'BOOLEAN'),
172
- ] },
173
- { id: 'party_role', name: 'party_role', ...at(0, 1), attributes: [
174
- attr('party_id', 'PK FK', 'BIGINT'), attr('role_code', 'PK FK', 'VARCHAR(20)'), attr('valid_from', '', 'DATE'), attr('valid_to', '', 'DATE'),
175
- ] },
176
- { id: 'arrangement', name: 'arrangement', ...at(1, 0), attributes: [
177
- attr('arrangement_id', 'PK', 'BIGINT'), attr('type_code', 'FK', 'VARCHAR(20)'), attr('terms', '', 'JSONB'), attr('starts_on', '', 'DATE'), attr('ends_on', '', 'DATE'),
178
- ] },
179
- { id: 'arrangement_party', name: 'arrangement_party', ...at(1, 1), attributes: [
180
- attr('arrangement_id', 'PK FK', 'BIGINT'), attr('party_id', 'PK FK', 'BIGINT'), attr('role_code', 'FK', 'VARCHAR(20)'), attr('share_pct', '', 'NUMERIC(5,2)'),
181
- ] },
182
- { id: 'resource', name: 'resource', ...at(2, 0), attributes: [
183
- attr('resource_id', 'PK', 'BIGINT'), attr('type_code', 'FK', 'VARCHAR(20)'), attr('place_id', 'FK', 'BIGINT'), attr('serial_no', '', 'VARCHAR(60)'), attr('spec', '', 'JSON'),
184
- ] },
185
- { id: 'place', name: 'place', ...at(3, 0), attributes: [
186
- // GEOGRAPHY is no type group the drawing knows: it shows as unknown (grey).
187
- attr('place_id', 'PK', 'BIGINT'), attr('name', '', 'VARCHAR(120)'), attr('location', '', 'GEOGRAPHY'), attr('tz', '', 'VARCHAR(40)'),
188
- ] },
189
- { id: 'business_event', name: 'business_event', ...at(2, 1), attributes: [
190
- attr('event_id', 'PK', 'BIGINT'), attr('type_code', 'FK', 'VARCHAR(20)'), attr('occurred_at', '', 'TIMESTAMP'),
191
- attr('arrangement_id', 'FK', 'BIGINT'), attr('resource_id', 'FK', 'BIGINT'),
192
- ] },
193
- { id: 'event_payload', name: 'event_payload', ...at(1, 2), attributes: [attr('event_id', 'PK FK', 'BIGINT'), attr('payload', '', 'JSONB')] },
194
- { id: 'ledger_entry', name: 'ledger_entry', ...at(2, 2), attributes: [
195
- attr('event_id', 'PK FK', 'BIGINT'), attr('line_no', 'PK', 'SMALLINT'), attr('account_code', 'FK', 'VARCHAR(20)'),
196
- attr('amount', '', 'DECIMAL(18,2)'), attr('posted_on', '', 'DATE'),
197
- ] },
198
- { id: 'product', name: 'product', ...at(4, 0), attributes: [
199
- attr('product_id', 'PK', 'BIGINT'), attr('name', '', 'VARCHAR(120)'), attr('line_code', 'FK', 'VARCHAR(20)'),
200
- attr('launched_on', '', 'DATE'), attr('retired_on', '', 'DATE'), attr('components', '', 'JSONB'),
201
- ] },
202
- { id: 'product_price', name: 'product_price', ...at(4, 1), attributes: [
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'),
213
- ] },
214
- { id: 'business_direction', name: 'business_direction', ...at(0, 2), attributes: [
215
- attr('direction_id', 'PK', 'BIGINT'), attr('kind', '', 'VARCHAR(20)'), attr('statement', '', 'TEXT'),
216
- attr('parent_id', 'FK', 'BIGINT'), attr('horizon', '', 'DATE'),
217
- ] },
218
- { id: 'direction_target', name: 'direction_target', ...at(4, 2), attributes: [
219
- attr('direction_id', 'PK FK', 'BIGINT'), attr('measure', 'PK', 'VARCHAR(40)'), attr('product_id', 'FK', 'BIGINT'),
220
- attr('target_value', '', 'NUMERIC(14,2)'), attr('unit', '', 'VARCHAR(10)'), attr('due_on', '', 'DATE'),
221
- ] },
222
- { id: 'account', name: 'account', ...at(3, 2), attributes: [attr('account_code', 'PK', 'VARCHAR(20)'), attr('name', '', 'VARCHAR(120)'), attr('kind', '', 'VARCHAR(20)')] },
223
- { id: 'classification', name: 'classification', ...at(3, 1), attributes: [
224
- attr('scheme', 'PK', 'VARCHAR(40)'), attr('code', 'PK', 'VARCHAR(20)'), attr('label', '', 'TEXT'),
225
- attr('parent_code', 'FK', 'VARCHAR(20)'), attr('notes'), // no type at all: unknown too
226
- ] },
227
- ],
228
- relationships: [
229
- { source: 'party', target: 'party_role', name: 'plays', sourceCardinality: '1', targetCardinality: '0..*' },
230
- { source: 'arrangement', target: 'arrangement_party', name: 'has', sourceCardinality: '1', targetCardinality: '1..*' },
231
- { source: 'party_role', target: 'arrangement_party', name: 'takes part as', sourceCardinality: '1', targetCardinality: '*', identifying: false },
232
- { source: 'place', target: 'resource', name: 'locates', sourceCardinality: '0..1', targetCardinality: '0..*', identifying: false },
233
- { source: 'business_event', target: 'arrangement', name: 'changes', sourceCardinality: '0..*', targetCardinality: '0..1', identifying: false },
234
- { source: 'business_event', target: 'resource', name: 'changes', sourceCardinality: '0..*', targetCardinality: '0..1', identifying: false },
235
- { source: 'business_event', target: 'event_payload', name: 'carries', sourceCardinality: '1', targetCardinality: '0..1' },
236
- { source: 'business_event', target: 'ledger_entry', name: 'is recorded in', sourceCardinality: '1', targetCardinality: '1..*' },
237
- { source: 'account', target: 'ledger_entry', name: 'is posted with', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
238
- { source: 'classification', target: 'business_event', name: 'types', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
239
- { source: 'product', target: 'arrangement', name: 'is sold as', sourceCardinality: '1', targetCardinality: '0..*', identifying: false },
240
- { source: 'product', target: 'product_price', name: 'is priced by', sourceCardinality: '1', targetCardinality: '1..*' },
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 },
245
- { source: 'product', target: 'direction_target', name: 'is aimed at by', sourceCardinality: '0..1', targetCardinality: '0..*', identifying: false },
246
- ],
247
- };
248
-
249
- // How the levels map: [concept, logical entity], then [logical entity, table]; any number either way.
250
- const mappings = [
251
- { from: 'conceptual', to: 'logical', pairs: [
252
- ['party', 'party'], ['party', 'person'], ['party', 'organisation'], ['role', 'party-role'],
253
- ['party', 'participation'], ['arrangement', 'participation'], ['role', 'participation'],
254
- ['arrangement', 'arrangement'], ['resource', 'resource'], ['place', 'place'], ['event', 'event'],
255
- ['ledger', 'account'], ['ledger', 'ledger-entry'], ['classification', 'scheme'], ['classification', 'code'],
256
- ['product', 'product'], ['product', 'product-price'], ['product', 'product-component'], ['resource', 'product-component'],
257
- ['direction', 'direction'], ['direction', 'target'],
258
- ['agreement', 'agreement'], ['feature', 'product-feature'], ['condition', 'condition'],
259
- ] },
260
- { from: 'logical', to: 'physical', pairs: [
261
- ['party', 'party'], ['person', 'party'], ['organisation', 'party'], ['party-role', 'party_role'], ['participation', 'arrangement_party'],
262
- ['arrangement', 'arrangement'], ['resource', 'resource'], ['place', 'place'],
263
- ['event', 'business_event'], ['event', 'event_payload'], ['account', 'account'], ['ledger-entry', 'ledger_entry'],
264
- ['scheme', 'classification'], ['code', 'classification'],
265
- ['product', 'product'], ['product-component', 'product'], ['product-price', 'product_price'],
266
- ['direction', 'business_direction'], ['target', 'direction_target'],
267
- ['agreement', 'agreement'], ['product-feature', 'product_feature'], ['condition', 'condition'],
268
- ] },
269
- ];
270
-
271
- // Perspectives: who is looking, which entities they see and what they call them.
272
- const perspectives = [
273
- {
274
- id: 'customer', name: 'Customer', context: 'retail', description: 'How a customer sees us: what they subscribe to and pay for',
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' },
277
- relationshipNames: { covers: 'includes', records: 'is billed in' },
278
- },
279
- {
280
- id: 'regulatory', name: 'Regulatory', context: 'regulatory', description: 'What a regulator holds us to',
281
- entities: ['party', 'agreement', 'arrangement', 'event', 'ledger', 'rule', 'classification', 'place'],
282
- names: { party: 'Legal Entity', arrangement: 'Compliance Obligation', event: 'Reportable Event', ledger: 'Audit Trail', rule: 'Regulation', place: 'Jurisdiction' },
283
- relationshipNames: { imposes: 'imposes', records: 'is evidenced by' },
284
- },
285
- {
286
- id: 'operations', name: 'Operations', context: 'operations', description: 'What we run and maintain, where',
287
- entities: ['resource', 'place', 'event', 'ledger', 'classification'],
288
- names: { resource: 'Asset', place: 'Site', event: 'Work Order', ledger: 'Cost Ledger' },
289
- },
290
- {
291
- id: 'executive', name: 'Executive', context: 'strategy', description: 'Where we are going, with what offers, and how we measure it',
292
- entities: ['direction', 'product', 'ledger', 'party', 'classification'],
293
- names: { direction: 'Strategy', product: 'Offering', ledger: 'Financials', party: 'Market', classification: 'Product Line' },
294
- relationshipNames: { shapes: 'invests in' },
295
- },
296
- ];
297
-
298
- // Contexts: what the entities mean there, the rules that hold (each a note on its entities), where and when.
299
- const contexts = [
300
- {
301
- id: 'retail', name: 'Retail (AU)', jurisdiction: 'AU', validFrom: '2024-07-01',
302
- description: 'Selling subscriptions to consumers and small businesses',
303
- meanings: {
304
- party: 'Someone who buys from us: a person or a small business.',
305
- arrangement: 'A recurring subscription, billed monthly.',
306
- ledger: 'Invoices and payments: what a customer owes and has paid.',
307
- product: 'A plan a customer subscribes to, at a monthly price.',
308
- },
309
- constraints: [
310
- { id: 'gst', text: 'Prices include GST (10%)', applies: ['ledger', 'product'], x: 1030, y: 600 },
311
- { id: 'one-price', text: 'A product has one current price', applies: ['product'], x: 730, y: 790 },
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 },
314
- ],
315
- },
316
- {
317
- id: 'regulatory', name: 'Regulatory (ASIC)', jurisdiction: 'AU', validFrom: '2024-01-01', validTo: '2030-12-31',
318
- description: 'Corporate and financial-services regulation',
319
- meanings: {
320
- party: 'A legal entity, identified by its ABN or ACN.',
321
- arrangement: 'An obligation a regulator can enforce.',
322
- event: 'An event that must be reported.',
323
- ledger: 'The audit trail: immutable, kept seven years.',
324
- rule: 'A regulation or licence condition.',
325
- },
326
- constraints: [
327
- { id: 'immutable', text: 'Entries are never changed: a correction is a new entry', applies: ['ledger'], x: 1030, y: 600 },
328
- { id: 'retention', text: 'Records are kept for 7 years', applies: ['ledger', 'event'], x: 1030, y: 780 },
329
- { id: 'identified', text: 'Every legal entity has an ABN or ACN', applies: ['party'], x: 280, y: 225 },
330
- ],
331
- },
332
- {
333
- id: 'operations', name: 'Operations', description: 'Running and maintaining assets on sites',
334
- meanings: {
335
- resource: 'A maintained asset: plant, vehicle, device.',
336
- place: 'A site where assets are kept and work is done.',
337
- event: 'A work order, an inspection or a sensor reading.',
338
- },
339
- constraints: [
340
- { id: 'one-site', text: 'Every asset is at exactly one site', applies: ['resource', 'place'], x: 880, y: 225 },
341
- ],
342
- },
343
- {
344
- id: 'strategy', name: 'Strategy FY27', validFrom: '2026-07-01', validTo: '2027-06-30',
345
- description: 'The plan for the financial year: what we offer, and the targets it is held to',
346
- meanings: {
347
- direction: 'A goal or strategy of the plan, with the targets that measure it.',
348
- product: 'An offering in the portfolio, kept, grown or retired as the plan says.',
349
- ledger: 'The financial results the targets are read from.',
350
- },
351
- constraints: [
352
- { id: 'every-target', text: 'Every offering has a revenue target', applies: ['product', 'direction'], x: 730, y: 790 },
353
- { id: 'review', text: 'Targets are reviewed each quarter', applies: ['direction'], x: 130, y: 860 },
354
- ],
355
- },
356
- ];
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
-
530
- const ws = document.body.appendChild(el({
531
- tag: 'ws-workspace',
532
- attrs: { label: 'Data Model', theme: 'light' },
533
- style: { flex: '1 1 auto', minHeight: '0' },
534
- }));
535
- registerBuiltinParts(ws);
536
- ws.registerPart(DataModelPart);
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',
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 }],
551
- }]);
552
- ws.registerPerspective('Data model', { type: 'part', partId: 'models' });
553
- ws.activatePerspective('Data model');
554
- </script>
555
- </body>
556
- </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>Data Model</title>
7
+ <style>
8
+ html, body { margin: 0; height: 100%; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
9
+ body { display: flex; flex-direction: column; }
10
+ </style>
11
+ </head>
12
+ <body>
13
+ <!-- The data modeller (DataModelPart) in the workbench, a tab per model
14
+ (ModelsPart). Its models come from modelengine, kept apart from the tools:
15
+ with modelengine checked out beside renderengine, its bundle loads below
16
+ and the first data model it has (the enterprise ontology) opens on a first
17
+ visit; File ▸ Open from ▸ modelengine ▸ lists the rest by domain (retail
18
+ banking at BIAN level among them), and Update from modelengine… brings in
19
+ a newer version as a merge. Without it the modeller starts empty, and
20
+ File ▸ Open from ▸ Add a source… reads a collection from elsewhere.
21
+ Every model is kept in this browser as it changes (and reopens next
22
+ time); File ▸ saves a .datamodel.json. Classic scripts, so the page opens
23
+ straight off disk; `npm run datamodel` opens it. -->
24
+ <script src="../dist/renderengine.umd.js"></script>
25
+ <script src="../dist/renderengine-workspace.umd.js"></script>
26
+ <script src="../../modelengine/dist/modelengine.js"></script>
27
+ <script>
28
+ const { el, registerBuiltinParts, DataModelPart } = window.RenderEngineWorkspace;
29
+ const ws = document.body.appendChild(el({
30
+ tag: 'ws-workspace',
31
+ attrs: { label: 'Data Model', theme: 'light' },
32
+ style: { flex: '1 1 auto', minHeight: '0' },
33
+ }));
34
+ registerBuiltinParts(ws);
35
+ ws.registerPart(DataModelPart);
36
+ ws.addParts([{
37
+ id: 'models', type: 'models', editorType: 'data-model', keep: true,
38
+ newModel: { name: 'New data model', level: 'conceptual' },
39
+ openContent: 'first',
40
+ }]);
41
+ ws.registerPerspective('Data model', { type: 'part', partId: 'models' });
42
+ ws.activatePerspective('Data model');
43
+ </script>
44
+ </body>
45
+ </html>