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