1
0

Fix chapter 3 (normalization)

anybody can find love
This commit is contained in:
2025-02-19 09:13:57 +02:00
parent 7307f6eef6
commit a4dad5c788
8 changed files with 590 additions and 578 deletions

View File

@ -1,11 +1,11 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.0.9 Chrome/128.0.6613.186 Electron/32.3.0 Safari/537.36" version="26.0.9">
<diagram name="Page-1" id="e56a1550-8fbb-45ad-956c-1786394a9013">
<mxGraphModel dx="1395" dy="2456" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
<mxGraphModel dx="1195" dy="2456" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="TstbxcchZz1pR3d-lyST-31" value="Пакунок" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="480" y="-732" width="160" height="248" as="geometry" />
<mxGeometry x="530" y="-1432" width="160" height="248" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-32" value="Пакунок id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-31" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -62,7 +62,7 @@
<mxGeometry width="30" height="10" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-70" value="Користувач" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="680" y="-860" width="150" height="220" as="geometry" />
<mxGeometry x="730" y="-1560" width="150" height="220" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-71" value="Користувач id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="26" width="150" height="30" as="geometry" />
@ -113,7 +113,7 @@
<mxGeometry width="30" height="8" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-116" value="Тип ролі" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="270" y="-840" width="160" height="118" as="geometry" />
<mxGeometry x="320" y="-1560" width="160" height="118" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-117" value="Тип ролі id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-116" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -140,7 +140,7 @@
<mxGeometry width="30" height="10" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-140" value="Тип залежності" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="670" y="-440" width="160" height="92" as="geometry" />
<mxGeometry x="720" y="-1140" width="160" height="92" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-141" value="Тип залежності id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-140" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -161,7 +161,7 @@
<mxGeometry width="30" height="10" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-150" value="Тип відношення" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="480" y="-440" width="160" height="92" as="geometry" />
<mxGeometry x="530" y="-1140" width="160" height="92" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-151" value="Тип відношення id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-150" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -182,7 +182,7 @@
<mxGeometry width="30" height="10" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-157" value="База пакунку" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="270" y="-700" width="160" height="170" as="geometry" />
<mxGeometry x="320" y="-1410" width="160" height="170" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-158" value="База пакунку id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-157" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -224,7 +224,7 @@
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-171" value="Залежність" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="680" y="-630" width="150" height="180" as="geometry" />
<mxGeometry x="730" y="-1330" width="150" height="180" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-172" value="Залежність id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-171" vertex="1">
<mxGeometry y="26" width="150" height="30" as="geometry" />
@ -257,7 +257,7 @@
<mxGeometry width="30" height="46" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-188" value="Відношення" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="270" y="-502" width="160" height="154" as="geometry" />
<mxGeometry x="320" y="-1202" width="160" height="154" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-189" value="Відношення id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-188" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -285,10 +285,10 @@
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-206" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="TstbxcchZz1pR3d-lyST-193" target="TstbxcchZz1pR3d-lyST-61" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="480" y="-540" as="targetPoint" />
<mxPoint x="530" y="-1240" as="targetPoint" />
<Array as="points">
<mxPoint x="460" y="-407" />
<mxPoint x="460" y="-507" />
<mxPoint x="510" y="-1107" />
<mxPoint x="510" y="-1207" />
</Array>
</mxGeometry>
</mxCell>
@ -298,8 +298,8 @@
<mxCell id="TstbxcchZz1pR3d-lyST-208" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;" parent="1" source="TstbxcchZz1pR3d-lyST-178" target="TstbxcchZz1pR3d-lyST-143" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="650" y="-483" />
<mxPoint x="650" y="-371" />
<mxPoint x="700" y="-1183" />
<mxPoint x="700" y="-1071" />
</Array>
</mxGeometry>
</mxCell>
@ -308,16 +308,16 @@
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-212" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="PkgUqFx-XmgoDWfw4lMe-12" target="TstbxcchZz1pR3d-lyST-119" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="480" y="-790" as="sourcePoint" />
<mxPoint x="440" y="-760" as="targetPoint" />
<mxPoint x="530" y="-1490" as="sourcePoint" />
<mxPoint x="490" y="-1460" as="targetPoint" />
<Array as="points">
<mxPoint x="450" y="-791" />
<mxPoint x="450" y="-771" />
<mxPoint x="500" y="-1491" />
<mxPoint x="500" y="-1471" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="PkgUqFx-XmgoDWfw4lMe-1" value="Роль" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="480" y="-860" width="160" height="82" as="geometry" />
<mxGeometry x="530" y="-1560" width="160" height="82" as="geometry" />
</mxCell>
<mxCell id="PkgUqFx-XmgoDWfw4lMe-2" value="Роль id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="PkgUqFx-XmgoDWfw4lMe-1" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -333,21 +333,21 @@
</mxCell>
<mxCell id="PkgUqFx-XmgoDWfw4lMe-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=ERmandOne;startFill=0;endArrow=ERzeroToMany;endFill=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" target="PkgUqFx-XmgoDWfw4lMe-12" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="430" y="-640" as="sourcePoint" />
<mxPoint x="470" y="-750" as="targetPoint" />
<mxPoint x="480" y="-1340" as="sourcePoint" />
<mxPoint x="520" y="-1450" as="targetPoint" />
<Array as="points">
<mxPoint x="460" y="-640" />
<mxPoint x="460" y="-791" />
<mxPoint x="510" y="-1340" />
<mxPoint x="510" y="-1491" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="PkgUqFx-XmgoDWfw4lMe-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="PkgUqFx-XmgoDWfw4lMe-12" target="TstbxcchZz1pR3d-lyST-77" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="690" y="-770" as="targetPoint" />
<mxPoint x="640" y="-813" as="sourcePoint" />
<mxPoint x="740" y="-1470" as="targetPoint" />
<mxPoint x="690" y="-1513" as="sourcePoint" />
<Array as="points">
<mxPoint x="660" y="-791" />
<mxPoint x="660" y="-739" />
<mxPoint x="710" y="-1491" />
<mxPoint x="710" y="-1439" />
</Array>
</mxGeometry>
</mxCell>

View File

@ -1,11 +1,11 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.0.9 Chrome/128.0.6613.186 Electron/32.3.0 Safari/537.36" version="26.0.9">
<diagram name="Page-1" id="e56a1550-8fbb-45ad-956c-1786394a9013">
<mxGraphModel dx="1572" dy="2484" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
<mxGraphModel dx="1434" dy="2607" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="TstbxcchZz1pR3d-lyST-31" value="Packages" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="470" y="-760" width="160" height="274" as="geometry" />
<mxGeometry x="480" y="-1349" width="160" height="274" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-32" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-31" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -55,10 +55,10 @@
<mxCell id="TstbxcchZz1pR3d-lyST-62" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="TstbxcchZz1pR3d-lyST-61" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-47" value="updated_at" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-31">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-47" value="updated_at" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-31" vertex="1">
<mxGeometry y="238" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-48" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-47">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-48" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-47" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-40" value="" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-31" vertex="1">
@ -68,7 +68,7 @@
<mxGeometry width="30" height="10" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-70" value="Users" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="260" y="-1070" width="150" height="246" as="geometry" />
<mxGeometry x="270" y="-1639" width="150" height="220" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-71" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="26" width="150" height="30" as="geometry" />
@ -76,56 +76,50 @@
<mxCell id="TstbxcchZz1pR3d-lyST-72" value="PK" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="TstbxcchZz1pR3d-lyST-71" vertex="1" connectable="0">
<mxGeometry width="30" height="30" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-93" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-31" value="name" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="56" width="150" height="26" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-94" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="TstbxcchZz1pR3d-lyST-93" vertex="1" connectable="0">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-32" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-31" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-31" value="name" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-70">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-33" value="email" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="82" width="150" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-32" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-31">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-34" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-33" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-33" value="email" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-70">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-35" value="password" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="108" width="150" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-34" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-33">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-36" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-35" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-35" value="password" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-70">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-37" value="last_used" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="134" width="150" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-36" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-35">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-38" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-37" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-37" value="last_used" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-70">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-39" value="created_at" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="160" width="150" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-38" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-37">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-40" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-39" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-39" value="created_at" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-70">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-41" value="updated_at" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="186" width="150" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-40" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-39">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-41" value="updated_at" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-70">
<mxGeometry y="212" width="150" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-42" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-41">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-42" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-41" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-114" value="" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-70" vertex="1">
<mxGeometry y="238" width="150" height="8" as="geometry" />
<mxGeometry y="212" width="150" height="8" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-115" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="TstbxcchZz1pR3d-lyST-114" vertex="1" connectable="0">
<mxGeometry width="30" height="8" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-116" value="PackageBaseRoles" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="470" y="-890" width="160" height="118" as="geometry" />
<mxGeometry x="480" y="-1510" width="160" height="118" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-117" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-116" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -152,7 +146,7 @@
<mxGeometry width="30" height="10" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-129" value="PackageBaseUserRoles" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14" parent="1" vertex="1">
<mxGeometry x="260" y="-810" width="160" height="142" as="geometry" />
<mxGeometry x="270" y="-1399" width="160" height="142" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-130" value="user" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=60;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-129" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -179,7 +173,7 @@
<mxGeometry width="56" height="26" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-140" value="DependencyTypes" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="470" y="-1030" width="160" height="92" as="geometry" />
<mxGeometry x="480" y="-1639" width="160" height="92" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-141" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-140" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -200,7 +194,7 @@
<mxGeometry width="30" height="10" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-150" value="RelationTypes" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="700" y="-810" width="160" height="92" as="geometry" />
<mxGeometry x="720" y="-1399" width="160" height="92" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-151" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-150" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -221,7 +215,7 @@
<mxGeometry width="30" height="10" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-157" value="PackageBases" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="260" y="-656" width="160" height="170" as="geometry" />
<mxGeometry x="270" y="-1245" width="160" height="170" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-158" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-157" vertex="1">
<mxGeometry y="26" width="160" height="30" as="geometry" />
@ -261,15 +255,15 @@
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-170" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=ERmandOne;startFill=0;endArrow=ERzeroToMany;endFill=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="TstbxcchZz1pR3d-lyST-158" target="TstbxcchZz1pR3d-lyST-34" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="440" y="-630" as="sourcePoint" />
<mxPoint x="450" y="-1219" as="sourcePoint" />
<Array as="points">
<mxPoint x="450" y="-615" />
<mxPoint x="450" y="-691" />
<mxPoint x="460" y="-1204" />
<mxPoint x="460" y="-1280" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-171" value="PackageDependencies" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="670" y="-1057" width="200" height="220" as="geometry" />
<mxGeometry x="680" y="-1646" width="200" height="220" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-172" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-171" vertex="1">
<mxGeometry y="26" width="200" height="30" as="geometry" />
@ -283,10 +277,10 @@
<mxCell id="TstbxcchZz1pR3d-lyST-185" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="TstbxcchZz1pR3d-lyST-184" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-23" value="requirement" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-171">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-23" value="requirement" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-171" vertex="1">
<mxGeometry y="82" width="200" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-24" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-23">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-24" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-23" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-176" value="description" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-171" vertex="1">
@ -295,10 +289,10 @@
<mxCell id="TstbxcchZz1pR3d-lyST-177" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="TstbxcchZz1pR3d-lyST-176" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-29" value="dependency_type" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-171">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-29" value="dependency_type" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-171" vertex="1">
<mxGeometry y="134" width="200" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-30" value="FK" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-29">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-30" value="FK" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-29" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-174" value="package" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-171" vertex="1">
@ -314,7 +308,7 @@
<mxGeometry width="30" height="34" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-188" value="&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); font-size: 12px; text-wrap: wrap;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;PackageRelations&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;" style="swimlane;html=1;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#e0e0e0;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;align=center;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Verdana;fontSize=14;swimlaneLine=1;" parent="1" vertex="1">
<mxGeometry x="690" y="-686" width="180" height="200" as="geometry" />
<mxGeometry x="700" y="-1275" width="180" height="200" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-189" value="id" style="shape=partialRectangle;top=0;left=0;right=0;bottom=1;html=1;align=left;verticalAlign=middle;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;fontStyle=5;" parent="TstbxcchZz1pR3d-lyST-188" vertex="1">
<mxGeometry y="26" width="180" height="30" as="geometry" />
@ -340,25 +334,25 @@
<mxCell id="TstbxcchZz1pR3d-lyST-192" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="TstbxcchZz1pR3d-lyST-191" vertex="1" connectable="0">
<mxGeometry width="30" height="26" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-1" value="requirement" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-188">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-1" value="requirement" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-188" vertex="1">
<mxGeometry y="140" width="180" height="30" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-2" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-1">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-2" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-1" vertex="1" connectable="0">
<mxGeometry width="30" height="30" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-3" value="relation_package_name" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" vertex="1" parent="TstbxcchZz1pR3d-lyST-188">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-3" value="relation_package_name" style="shape=partialRectangle;top=0;left=0;right=0;bottom=0;html=1;align=left;verticalAlign=top;fillColor=none;spacingLeft=34;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;dropTarget=0;" parent="TstbxcchZz1pR3d-lyST-188" vertex="1">
<mxGeometry y="170" width="180" height="30" as="geometry" />
</mxCell>
<mxCell id="j6MDIjJCqvpe9HdPzyI4-4" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" vertex="1" connectable="0" parent="j6MDIjJCqvpe9HdPzyI4-3">
<mxCell id="j6MDIjJCqvpe9HdPzyI4-4" value="" style="shape=partialRectangle;top=0;left=0;bottom=0;html=1;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;points=[];portConstraint=eastwest;part=1;" parent="j6MDIjJCqvpe9HdPzyI4-3" vertex="1" connectable="0">
<mxGeometry width="30" height="30" as="geometry" />
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-203" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="TstbxcchZz1pR3d-lyST-132" target="TstbxcchZz1pR3d-lyST-158" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="440" y="-709" />
<mxPoint x="440" y="-615" />
<mxPoint x="450" y="-1298" />
<mxPoint x="450" y="-1204" />
</Array>
<mxPoint x="170" y="-710" as="sourcePoint" />
<mxPoint x="180" y="-1299" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-204" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=ERmandOne;startFill=0;endArrow=ERzeroToMany;endFill=0;entryX=1.004;entryY=0.627;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="TstbxcchZz1pR3d-lyST-71" target="TstbxcchZz1pR3d-lyST-130" edge="1">
@ -367,43 +361,43 @@
<mxCell id="TstbxcchZz1pR3d-lyST-206" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="TstbxcchZz1pR3d-lyST-193" target="TstbxcchZz1pR3d-lyST-32" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="660" y="-585" />
<mxPoint x="660" y="-719" />
<mxPoint x="670" y="-1174" />
<mxPoint x="670" y="-1308" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-207" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="TstbxcchZz1pR3d-lyST-197" target="TstbxcchZz1pR3d-lyST-151" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="670" y="-614" />
<mxPoint x="670" y="-769" />
<mxPoint x="680" y="-1203" />
<mxPoint x="680" y="-1358" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-208" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="j6MDIjJCqvpe9HdPzyI4-29" target="TstbxcchZz1pR3d-lyST-141" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="650" y="-910" />
<mxPoint x="650" y="-989" />
<mxPoint x="660" y="-1499" />
<mxPoint x="660" y="-1578" />
</Array>
<mxPoint x="1110" y="-650" as="sourcePoint" />
<mxPoint x="670" y="-400" as="targetPoint" />
<mxPoint x="1120" y="-1239" as="sourcePoint" />
<mxPoint x="680" y="-989" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-210" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=ERmandOne;startFill=0;endArrow=ERzeroToMany;endFill=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="TstbxcchZz1pR3d-lyST-32" target="TstbxcchZz1pR3d-lyST-174" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="710" y="-547" as="targetPoint" />
<mxPoint x="720" y="-1136" as="targetPoint" />
<Array as="points">
<mxPoint x="650" y="-719" />
<mxPoint x="650" y="-884" />
<mxPoint x="660" y="-1308" />
<mxPoint x="660" y="-1473" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="TstbxcchZz1pR3d-lyST-212" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=ERmandOne;endFill=0;startArrow=ERzeroToMany;startFill=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="TstbxcchZz1pR3d-lyST-138" target="TstbxcchZz1pR3d-lyST-117" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="450" y="-739" />
<mxPoint x="450" y="-849" />
<mxPoint x="460" y="-1328" />
<mxPoint x="460" y="-1438" />
</Array>
</mxGeometry>
</mxCell>

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ rust:
title: Rust Programming Language
author: Rust Programming Language
url:
value: https://www.rust-lang.org/
value: https://www.rust-lang.org
date: 2025-02-09
iced:
@ -22,7 +22,7 @@ iced:
title: iced - A cross-platform GUI library for Rust
author: iced - A cross-platform GUI library for Rust
url:
value: https://iced.rs/
value: https://iced.rs
date: 2025-02-09
sqlx:
@ -30,7 +30,7 @@ sqlx:
title: "GitHub - launchbadge/sqlx: The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite"
author: GitHub
url:
value: https://github.com/launchbadge/sqlx/
value: https://github.com/launchbadge/sqlx
date: 2025-02-09
# ===== HOWTO ==== #
@ -40,7 +40,7 @@ halloy:
title: Halloy
author: Halloy
url:
value: https://halloy.squidowl.org/
value: https://halloy.squidowl.org
date: 2025-02-09
iced-guide:
@ -48,7 +48,7 @@ iced-guide:
title: Introduction - Unofficial Iced Guide
author: GitHub Pages
url:
value: https://jl710.github.io/iced-guide/
value: https://jl710.github.io/iced-guide
date: 2025-02-09
iced-editor:
@ -60,6 +60,14 @@ iced-editor:
value: https://www.youtube.com/watch?v=gcBJ7cPSALo
date: 2025-02-09
normalization:
type: Web
title: Database Normalization Normal Forms 1nf 2nf 3nf Table Examples
author: Kolade Chris
url:
value: https://www.freecodecamp.org/news/database-normalization-1nf-2nf-3nf-table-examples
date: 2025-02-09
# ===== DESIGN ==== #
elm:
@ -67,7 +75,7 @@ elm:
title: Elm - delightful language for reliable web applications.
author: Elm - delightful language for reliable web applications.
url:
value: https://elm-lang.org/
value: https://elm-lang.org
date: 2025-02-11
# Richard Feldman. Making Impossible States Impossible, 2016. YouTube. URL: https://www.youtube.com/watch?v=IcgmSRJHu_8 (date of access: 09.02.2025).
@ -96,7 +104,7 @@ hexagonal:
author: Josip Benko-Đaković
publisher: Barrage
url:
value: https://www.barrage.net/blog/technology/how-to-apply-hexagonal-architecture-to-rust/
value: https://www.barrage.net/blog/technology/how-to-apply-hexagonal-architecture-to-rust
date: 2025-02-09
state-machines:
@ -104,10 +112,10 @@ state-machines:
title: Pretty State Machine Patterns in Rust
author: Hoverbear
url:
value: https://hoverbear.org/blog/rust-state-machine-pattern/
value: https://hoverbear.org/blog/rust-state-machine-pattern
date: 2025-02-09
# ===== EXAMPLES ==== #
# ===== I use Arch btw ==== #
aurweb:
type: Web
@ -122,7 +130,7 @@ aur:
title: AUR (en) - Home
author: AUR (en) - Home
url:
value: https://aur.archlinux.org/
value: https://aur.archlinux.org
date: 2025-02-06
aur-search:
@ -162,7 +170,7 @@ archlinux: # I use Arch btw!
title: Arch Linux
author: Arch Linux
url:
value: https://archlinux.org/
value: https://archlinux.org
date: 2025-02-09
# ===== TOOLS ==== #
@ -180,7 +188,7 @@ neovim:
title: Neovim
author: Neovim
url:
value: https://neovim.io/
value: https://neovim.io
date: 2025-02-09
mysql:
@ -188,7 +196,7 @@ mysql:
title: MySQL
author: MySQL
url:
value: https://www.mysql.com/
value: https://www.mysql.com
date: 2025-02-09
docker:
@ -196,7 +204,7 @@ docker:
title: "Docker: accelerated container application development"
author: Docker
url:
value: https://www.docker.com/
value: https://www.docker.com
date: 2025-02-09
compose:
@ -204,5 +212,5 @@ compose:
title: Docker compose
author: Docker Documentation
url:
value: https://docs.docker.com/compose/
value: https://docs.docker.com/compose
date: 2025-02-09

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 306 KiB

View File

@ -148,52 +148,40 @@
#img("img/aur/search_func.png", "Фунціонал пошуку AUR", [@aur-search-func])
В контексті розробки пакункового репозиторію важливо визначити основні ролі користувачів та їхні потреби. Розробники пакунків виступають основним рушієм системи, створюючи та підтримуючи програмні компоненти, в той час як інші учасники можуть долучатися до процесу тестування, рецензування та вдосконалення пакунків.
В контексті розробки пакункового репозиторію важливо визначити основні ролі користувачів та їхні потреби. Зареєстровані користувачі виступають основним рушієм системи, створюючи та підтримуючи програмні компоненти, в той час як інші учасники мають змогу отримувати інформацію про пакунки: залежності, відносини, ліцензії, ресурс походження, тощо.
Аналіз інформаційної системи виявив наступні ключові об'єкти предметної області: читач, супроводжуючий, пакунок, база пакунку, залежності пакунків, відносини пакунків, роль користувача для бази пакунків. Основним користувачем виступає читач, який потребує ефективних інструментів для отримання інформації про програмні компоненти.
Аналіз інформаційної системи виявив наступні ключові об'єкти предметної області: незареєстрований користувач, зареєстрований користувач, пакунок, база пакунку, залежності пакунків, відносини пакунків, роль користувача для бази пакунків. Інформаційна система передбачає взаємодію як для незареєстрованих, так і для зареєстрованих користувачів. Вона автоматизує процеси пов'язані з відстеженням залежностей пакунків, прав користувачів, пошуком пакунків і генеруванням статистики.
Основні інформаційні потреби користувачів системи включають:
- перегляд і завантаження інформації про пакунки;
- забезпечення повного циклу управління пакунками: створення, редагування, версіонування та видалення;
- надання інструментів для автоматизованої перевірки залежностей та сумісності компонентів;
- забезпечення генерації технічної документації та звітів про використання пакунків.
На основі проведеного аналізу було визначено критичні вимоги до функціоналу репозиторію пакунків. Ця інформація становитиме основу для проектування системи, яка забезпечить ефективну кооперацію над програмними компонентами та автоматизує ключові процеси їх розробки та супроводу.
Особлива увага буде приділена реалізації механізмів забезпечення якості та безпеки пакунків, оскільки ці аспекти є критичними для створення надійної екосистеми програмного забезпечення. Система також передбачатиме можливості для масштабування та інтеграції з іншими інструментами розробки.
== Концептуальне моделювання предметної області //{{{2
Інформаційна система передбачає взаємодію як для читачів (незареєстрованих користувачів), так і для зареєстрованих користувачів. Система автоматизує процеси пов'язані з відстеженням залежностей пакунків, прав користувачів, пошуком пакунків і генеруванням статистик.
Кожний незареєстрований користувач повинен мати можливість ефективно:
Кожний незареєстрований користувач має можливість ефективно:
- здійснювати пошук пакунків за різними параметрами;
- переглядати інформацію про пакунки, їх бази;
- переглядати інформацію про користувачів;
- переглядати інформацію про пакунки, їх бази, залежності та відносини;
- переглядати статистику репозиторію;
- увійти у існуючий, або створити новий обліковий запис.
Разом з можливостями незареєстрованих користувачів, зареєстровані користувачі повинні мати можливість ефективно:
Разом з можливостями незареєстрованих користувачів, зареєстровані користувачі мають можливість ефективно:
- переглядати інформацію про користувачів;
- створювати пакунки та бази пакунків;
- видаляти власні пакунки та бази пакунків;
- додавати, видаляти та оновлювати залежності та відносини для власних пакунків;
- додавати, видаляти та оновлювати ролі інших користувачів для власних пакунків;
- оновлювати чужі пакунки та бази пакунків до яких був наданий доступ редагування;
- переглядати статистику свого облікового запису;
- оновлювати чужі пакунки та бази пакунків до яких були надані відповідні ролі;
- переглядати статистику свого облікового запису і своїх пакунків;
- змінювати інформацію свого облікового запису;
- вийти з облікового запису;
- видалити свій обліковий запис.
- вийти з облікового запису або видалити його;
Всі дані зберігатимуться в базі даних, яка містить пов'язані таблиці спроектовані таким чином, щоб забезпечити цілісність і коректність даних в них. Розумне проектування бази даних забезпечить оптимізацію обробки, пошуку та оновлення інформації.
На основі цього аналізу було визначено критичні вимоги до функціоналу репозиторію пакунків. Ця інформація становитиме основу для проектування системи, яка забезпечить ефективну кооперацію над програмними компонентами та автоматизує ключові процеси їх розробки та супроводу.
== Концептуальне моделювання предметної області //{{{2
Для виконання детального концептуального моделювання інформаційної системи "Репозиторій пакунків. Колаборація над пакунками" потрібно проаналізувати взаємозв'язки між основними об'єктами системи: користувачами, їх ролями, базами пакунків, пакунками, їх залежностями та відносинами.
В предметній області існують наступні поняття та зв'язки:
- користувач може мати багато типів ролей для багатьох баз пакунків;
- кожна роль користувача може мати коментар від користувача;
- база пакунку може мати багато підлеглих пакунків;
- база пакунку може мати багато пакунків;
- пакунок обов'язково має одну базу пакунку;
- пакунок може мати багато типів відносин з назвами інших пакунків;
- пакунок може мати багато типів залежностей від назв інших пакунків.
Для розуміння структури інформаційної системи спроектовано загальну діаграму класів @class_diagram, яка стане основою для проектування бази даних та визначення функціональних вимог до системи.
#img("img/class_diagram.png", "Загальна діаграма класів")
@ -209,24 +197,19 @@
- об'єкт виду відносин буде визначати типи відносин між пакунками (наприклад: конфліктує, надає, замінює). Містить номер типу відношення та його назву. Цей об'єкт дозволяє класифікувати відносини між пакунками;
- об'єкт відносин пакунків буде зберігати інформацію про відносини пакунків. Містить архітектуру, вимоги, пакунок, тип відношення та назву пакунку до якого формується відношення. Ця інформація дозволяє визначити, які пакунки конфліктують з даним пакунком, які функції він надає, і які пакунки він замінює.
Для ефективного опису функціональної структури та процесів пакункового репозиторію, необхідно побудувати DFD-діаграму, яка описує потоки даних. Ця діаграма відображає основні етапи обробки інформації та взаємодію між різними компонентами системи під час виконання бізнес задачі.
Для ефективного опису функціональної структури та процесів пакункового репозиторію, необхідно побудувати DFD-діаграму, яка описує потоки даних. Ця діаграма відобразить основні етапи обробки інформації та взаємодію між різними компонентами системи під час виконання бізнес-задачі отримання інформації про пакунки.
Зареєстровані користувачі створюють нові пакунки та бази пакунків, надають ролі іншим зареєстрованим користувачам до своїх баз пакунків, налаштовують залежності і відносини для своїх пакунків. Для виконання своїх цілей вони користуються даними про чужі пакунки, їх залежності, відносини та користувачів, котрі відповідають за ці пакунки.
Зареєстровані користувачі створюють нові бази пакунків та пакунки та налаштовують залежності і відносини для них, а також надають ролі іншим зареєстрованим користувачам до своїх баз пакунків. Для виконання своїх цілей вони користуються інформацією про пакунки інших користувачів. Для отримання інформації про пакунки зареєстровані, а також незареєстровані користувачі використовують функції пошуку. Створимо DFD-діаграму @data_diagram, що наочно відобразить послідовність процесів та потоків даних під час виконання користувачем кроків для досягнення цільової задачі перегляду інформації про пакунок.
Незареєстровані користувачі здійснюють пошук та переглядають інформацію про пакунки, їх базу, залежності та відносини пакунку, різноманітні ролі зареєстрованих користувачів для баз пакунків та інформацію про облікові записи зареєстрованих користувачів.
Для наочного представлення процесів та потоків даних в системі під час формування звіту про пакунок, створимо DFD-діаграму @data_diagram, що відображає послідовність кроків під час виконання користувачем цільової задачі перегляду інформації про пакунок.
#img("img/data_diagram.png", "Діаграма потоків даних")
#img("img/data_diagram.png", "Діаграма потоків даних", height: 65%)
Для забезпечення ефективної розробки та впровадження інформаційної системи критично важливим етапом є побудова діаграми варіантів використання (Use-Case Diagram). Дана діаграма @usecase_diagram є фундаментальним інструментом моделювання, який дозволяє показати та систематизувати більшість можливих сценаріїв взаємодії між системою та її користувачами.
#img("img/usecase_diagram.png", "Use-Case діаграма") //, height: 95%)
#img("img/usecase_diagram.png", "Use-Case діаграма")
Діаграма варіантів використання репозиторію пакунків відображає взаємодію між системою та двома групами користувачів: будь-якими користувачами та тільки автентифікованими. Кожна група користувачів має різний за розміром набір прав та можливостей.
У контексті розроблюваної системи діаграма варіантів використання відображає складну мережу взаємозв'язків між двома категоріями акторів (користувачів системи) та функціональними можливостями, які система надає. До основних акторів належать будь-які користувачі, в тому числі незареєстровані, а також зареєстровані та автентифіковані користувачі, кожен з яких має свій унікальний набір прав та можливостей взаємодії з системою.
Функціональні можливості, представлені на діаграмі, охоплюють широкий спектр операцій, включаючи, але не обмежуючись, управління пакунками, їх створення та модифікацію, перегляд доступної інформації, генерацію звітів, адміністрування ролей користувачів для баз пакунків та залежностей і відносин між пакунками. Кожен варіант використання супроводжується описом послідовності дій, необхідних для досягнення конкретної мети користувача.
Система надає функціональні можливості для управління пакунками, включаючи їх створення, модифікацію та адміністрування. Користувачі можуть переглядати інформацію про пакунки, генерувати звіти та керувати залежностями між пакунками відповідно до їхніх прав доступу.
Значущість діаграми варіантів використання полягає в тому, що вона служить потужним інструментом для:
- Визначення та узгодження вимог користувачів до системи
@ -234,9 +217,36 @@
- Оптимізації архітектурних рішень на ранніх етапах проектування
- Забезпечення відповідності розроблюваної системи реальним потребам користувачів
Більше того, Use-Case діаграма відіграє важливу роль у процесі розробки системи, допомагаючи виявити потенційні проблеми та незручності ще на етапі проектування. Це дозволяє своєчасно вносити необхідні корективи в архітектуру системи та оптимізувати її компоненти для досягнення максимальної ефективності та зручності використання.
// Більше того, Use-Case діаграма відіграє важливу роль у процесі розробки системи, допомагаючи виявити потенційні проблеми та незручності ще на етапі проектування. Це дозволяє своєчасно вносити необхідні корективи в архітектуру системи та оптимізувати її компоненти для досягнення максимальної ефективності та зручності використання.
На основі аналізу діаграми варіантів використання можна також визначити пріоритетність розробки різних функціональних модулів системи, що особливо важливо при обмежених ресурсах та часових рамках проекту. Це забезпечує раціональний розподіл ресурсів та дозволяє сконцентруватися на реалізації найбільш критичних функцій системи в першу чергу.
// На основі аналізу діаграми варіантів використання можна також визначити пріоритетність розробки різних функціональних модулів системи, що особливо важливо при обмежених ресурсах та часових рамках проекту. Це забезпечує раціональний розподіл ресурсів та дозволяє сконцентруватися на реалізації найбільш критичних функцій системи в першу чергу.
Важливою особливістю репозиторіїв пакунків є можливість керувати програмним забезпеченням та надавати користувачам доступ до актуальних версій програм. У репозиторіях пакунків основний документообіг пов'язаний з управлінням інформацією пакунків, їх відносинами та залежностями. Репозиторії ведуть облік завантажень пакунків та формують звітність про їх використання для аналізу популярності та стабільності.
Отже, документообіг репозиторію пакунків складається з наступних документів:
- опис пакунку: назва пакунку, версія, розробник, ліцензія, короткий опис функціональності, детальний опис можливостей, вимоги до системи, інструкції зі встановлення та налаштування, історія змін версій;
- файл потреб пакунку: унікальний ідентифікатор пакунку, назва, версія, архітектура процесора, список залежностей (обов'язкових та опціональних), конфлікти з іншими пакунками, контрольні суми файлів, розмір встановленого пакунку;
- журнал активності пакунку: статистика завантажень, оцінки користувачів, коментарі, повідомлення про помилки, запити на оновлення, історія модифікацій;
- інструкція зі збірки: скрипт для автоматизованої збірки пакунку, список додаткових залежностей для збірки, параметри конфігурації, інструкції з тестування, відомості про середовище збірки та тестування, контактна інформація супроводжувача пакунку.
У процесі проектування бази даних репозиторію пакунків важливо визначити та врахувати обмеження цілісності даних, які забезпечують коректність та узгодженість інформації. Для кожного обмеження необхідно визначити стратегію обробки можливих порушень, щоб гарантувати надійне функціонування системи та збереження важливих даних:
+ при видаленні облікового запису:
+ якщо обліковий запис володіє базами пакунків для яких немає інших володарів -- видалення заборонено;
+ якщо обліковий запис було видалено -- його ролі мають бути видаленими, але бази пакунків та пакунки продовжують існувати.
+ при видаленні бази пакунку:
+ всі пов'язані ролі користувачів мають бути видалені;
+ всі пов'язані пакунки мають бути видалені.
+ при видалені пакунків:
+ всі залежності та відносини пакунку мають бути видалені.
Додаткові обмеження:
- не можна створити залежність пакунку від самого себе;
- максимальний індивідуальний розмір об'єктів системи має бути обмежений для безпеки;
- назва пакунку повинна бути унікальною в межах репозиторію;
- назва бази пакунку повинна бути унікальною в межах репозиторію;
- юзернейм та пошта користувача повинні бути унікальними в межах репозиторію;
- обліковий запис не може мати дві однакові ролі для однієї бази пакунків;
- час збірки пакунка не може бути раніше ніж час його створення.
= Постановка задачі //{{{1
Потрібно розробити інформаційну систему "Репозиторій пакунків. Колаборація над пакунками", яка забезпечить ефективне зберігання, пошук, управління та спільну розробку програмних пакунків. Після аналізу предметної області та виявлених інформаційних потреб, було визначено наступні вимоги щодо функціоналу інформаційної системи.
@ -426,14 +436,23 @@
// Alerts & confirmations {{{2
Інформаційна система повинна мати систему зворотного зв'язку з користувачем:
+ система запитів підтвердження користувача при видаленні інформації та опис критичності дії. Приклади дуже критичних випадків:
+ при видаленні бази пакунків система має сповістити користувача що ця діє зітре всі пов'язані ролі та пакунки з їх залежностями і відносинами. Для підтвердження цієї дії користувач має ввести назву бази пакунків у форму підтвердження;
+ при видаленні облікового запису користувач має бути сповіщений що його ролі будуть повністю видалені з системи. У випадку, якщо користувач має бази пакунків без співавторів, система має заборонити видалення облікового запису до моменту знаходження співавторів (або передачі авторства супроводжуючому) або видалення баз пакунків.
+ система отримання підтвердження користувача яка надасть опис критичності дії при видаленні інформації. Приклади дуже критичних випадків:
+ при видаленні бази пакунків система має сповістити користувача що ця діє видалить всі пов'язані ролі та пакунки з їх залежностями і відносинами. Для підтвердження цієї дії користувач має ввести назву бази пакунків у форму підтвердження;
+ при видаленні облікового запису користувач має бути сповіщений що його ролі будуть повністю видалені з системи. У випадку, якщо користувач має бази пакунків для яких він є єдиним автором, система має заборонити видалення облікового запису до моменту знаходження співавторів (має бути запропонована опція надання співавторства одному або кільком супроводжуючим) або видалення цих баз пакунків.
/*+ при зміні назви пакунку система має попередити користувача що це може викликати неочікувані труднощі для інших користувачів яким можливо прийдеться корегувати залежності своїх пакунків.*/
+ система сповіщення користувача про помилки в системі, наприклад:
+ перебої з'єднання до сховища даних;
+ нестачу ресурсів комп'ютеру;
+ знаходження аномалій в даних.
+ система візуального відображення некоректних полів у формах даних та опису причини некоректності.
+ логічні помилки, котрі потрібно передати розробникам;
+ знаходження аномалій в даних системи.
+ система сповіщення користувача про помилки в системі, наприклад:
+ перебої з'єднання до сховища даних;
+ знаходження аномалій в даних;
+ конфлікти відносин залежностей (один пакунок конфліктує з тою залежністю, яку надає інший пакунок);
+ система візуального відображення статусу полів у формах даних та опису причини некоректності:
+ виділення полів з правильними даними зеленим кольором;
+ виділення полів з помилковими даними червоним кольором;
+ виділення полів з потенційно помилковими даними жовтим кольором;
= Проектування бази даних //{{{1
#v(-spacing)
@ -469,47 +488,75 @@
== Вибір та побудова логічної моделі бази даних на базі ER-діаграми //{{{2
Для створення логічної моделі бази даних репозиторію пакунків було обрано реляційну модель. Ця модель дозволяє ефективно організувати дані у вигляді взаємопов'язаних таблиць, що забезпечує чітку структуру інформації в контексті управління пакунками та колаборації між користувачами. Вона гарантує узгодженість всіх зв'язків між сутностями, такими як "Користувач", "Пакунок", "База пакунку" та іншими, а також підтримує обмеження цілісності даних.
Для створення логічної моделі бази даних репозиторію пакунків було обрано реляційну модель. Ця модель дозволяє ефективно організувати дані у вигляді взаємопов'язаних таблиць, що забезпечує чітку структуру інформації та гарантує узгодженість всіх зв'язків та інформації між сутностями.
Реляційна модель надає потужні можливості для виконання складних запитів, що є критичним для системи управління пакунками. Це дозволяє ефективно обробляти запити користувачів щодо пошуку пакунків, аналізу залежностей та відношень між ними, а також керування правами доступу через систему ролей. На основі розробленої ER-діаграми @er_diagram було спроєктовано логічну модель бази даних @logic_model.
Проведемо аналіз первинних та зовнішніх ключів на основі створеної ER-діаграми @er_diagram, відокремив кожну сутність у відповідну таблицю:
+ таблиця Users (Користувачі):
+ Первинний ключ: id.
+ таблиця PackageBases (Бази пакунків):
+ Первинний ключ: id.
+ таблиця PackageBaseRoles (Типи ролі):
+ Первинний ключ: id.
+ таблиця PackageBaseUserRoles (Ролі):
+ Складовий первинний ключ: (base, user, role);
+ Зовнішні ключі: base, user, role.
+ таблиця Packages (Пакунки):
+ Первинний ключ: id;
+ Зовнішній ключ: base
+ таблиця DependencyTypes (Типи залежностей):
+ Первинний ключ: id.
+ таблиця PackageDependencies (Залежності):
+ Первинний ключ: id;
+ Зовнішні ключі: package, dependency_type.
+ таблиця RelationTypes (Типи відношення):
+ Первинний ключ: id.
+ таблиця PackageRelations (Відношення):
+ Первинний ключ: id;
+ Зовнішні ключі: package, relation_type.
Для забезпечення точного розуміння відповідності між ER-діаграмою та логічною схемою створено довідник атрибутів @attributes. Цей довідник встановлює однозначну відповідність між термінами, використаними в концептуальній моделі (ER-діаграмі) та їх представленням у логічній моделі бази даних. Особливу увагу приділено відображенню зв'язків між сутностями, що реалізовані через механізм зовнішніх ключів, забезпечуючи цілісність даних та правильну роботу системи контролю версій пакунків.
Використання зовнішніх ключів забезпечить цілісність даних та дозволить легко виконувати операції JOIN для отримання необхідної інформації з кількох таблиць одночасно, що надасть можливість ефективного пошуку пакунків та їх аналізу.
Обрана модель також враховує специфіку роботи з пакунками, їх залежностями та відношеннями, що є ключовим аспектом функціонування репозиторію пакунків. Це дозволяє ефективно відстежувати зміни в пакунках, керувати правами доступу користувачів та забезпечувати надійну основу для колаборації над пакунками.
// TODO: як були додані зовнішні ключі?
Створимо довідник атрибутів @attributes. Він встановлює однозначну відповідність між термінами, використаними в ER-діаграмі @er_diagram та їх представленням у логічній моделі бази даних @logic_model. В логічній моделі особливу увагу приділено відображенню зв'язків між сутностями, які реалізовані через механізм зовнішніх ключів, що забезпечить цілісність даних та правильну роботу інформаційної системи.
#figure(
table(
columns: 2,
table.header[ER-діаграма][Логічна модель],
[Архітектура], [arch],
[База пакунку], [base],
[База пакунку id], [id],
[База пакунку], [PackageBases],
[База пакунку], [base],
[Веб-покликання], [url],
[Версія], [version],
[Відношення id], [id],
[Відношення], [PackageRelations],
[Дата логіну], [last_used],
[Дата оновлення], [updated_at],
[Дата позначення], [flagged_at],
[Дата створення], [created_at],
[Електронна пошта], [email],
[Залежність id], [id],
[Залежність], [PackageDependencies],
[Ім'я], [name],
[Коментар], [comment],
[Користувач], [user],
[Користувач id], [id],
[Користувач], [Users],
[Назва], [name],
[Користувач], [user],
[Назва залежного пакунку], [dependency_package_name],
[Назва пакунку з яким є відношення], [relation_package_name],
[Назва], [name],
[Опис], [description],
[Пакунок id], [id],
[Пакунок], [Packages],
[Пароль], [password],
[Роль id], [id],
[Роль], [PackageBaseUserRoles],
[Тип відношення id], [id],
[Тип відношення], [RelationTypes],
[Тип залежності id], [id],
[Тип залежності], [DependencyTypes],
[Тип ролі], [role],
[Тип ролі id], [id],
[Тип ролі], [PackageBaseRoles],
[Тип ролі], [role],
[Умова], [requirement],
),
caption: [довідник атрибутів (таблиця виконана самостійно)],
@ -517,106 +564,86 @@
#img("img/logic_model.png", "Логічна модель бази даних")
Побудована база даних відповідає умовам третьої нормальної форм, адже кожна таблиця має первинний ключ, всі атрибути є атомарними, всі не ключові атрибути знаходяться у повній функціональній залежності від відповідних первинних ключів, та у таблицях відсутні транзитивні залежності.
Проведемо перевірку схеми логічної моделі бази даних на відповідність вимогам третьої нормальної форми. Для кожної таблиці треба визначити функціональні залежності та перевірити їх на відповідність першій, другій та третій нормальним формам @normalization.
+ Таблиця Users:
+ від первинного ключа id залежать всі неключові атрибути, а саме: name, email, password, last_used, created_at, updated_at;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від первинного ключа id (2НФ), а також відсутні транзитивні залежності.
+ Таблиця Packages:
+ від первинного ключа id залежать всі неключові атрибути, а саме: base, name, version, description, url, flagged_at, created_at, updated_at;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від первинного ключа id (2НФ), а також відсутні транзитивні залежності.
+ Таблиця PackageBases:
+ від первинного ключа id залежать всі неключові атрибути, а саме: name, description, created_at, updated_at;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від первинного ключа id (2НФ), а також відсутні транзитивні залежності.
+ Таблиця PackageBaseRoles:
+ від первинного ключа id залежать всі неключові атрибути, а саме: name, description;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від первинного ключа id (2НФ), а також відсутні транзитивні залежності.
+ Таблиця PackageBaseUserRoles:
+ від складеного первинного ключа (base, user, role) залежать всі неключові атрибути, а саме: comment;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від складеного первинного ключа (base, user, role), відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від складеного первинного ключа (base, user, role) (2НФ), а також відсутні транзитивні залежності.
+ Таблиця DependencyTypes:
+ від первинного ключа id залежать всі неключові атрибути, а саме: name;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від первинного ключа id (2НФ), а також відсутні транзитивні залежності.
+ Таблиця PackageDependencies:
+ від первинного ключа id залежать всі неключові атрибути, а саме: arch, requirement, description, package, dependency_type, dependency_package_name;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від первинного ключа id (2НФ), а також відсутні транзитивні залежності.
+ Таблиця RelationTypes:
+ від первинного ключа id залежать всі неключові атрибути, а саме: name;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від первинного ключа id (2НФ), а також відсутні транзитивні залежності.
+ Таблиця PackageRelations:
+ від первинного ключа id залежать всі неключові атрибути, а саме: arch, requirement, package, relation_type, relation_package_name;
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.
+ таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні (1НФ), всі неключові атрибути залежать безпосередньо від первинного ключа id (2НФ), а також відсутні транзитивні залежності.
== Побудова логічної моделі бази даних шляхом нормалізаці //{{{2
== Побудова логічної моделі бази даних шляхом нормалізації //{{{2
Нормалізація структури даних відіграє фундаментальну роль у процесі проектування сучасних інформаційних систем, оскільки забезпечує створення раціональної архітектури даних, що ефективно запобігає надлишковості інформації та можливим аномаліям. В контексті розробки інформаційної системи пакункового репозиторію, нормалізація набуває особливої значущості через комплексну природу зв'зків між пакунками.
Нормалізація структури даних відіграє фундаментальну роль у процесі проектування сучасних інформаційних систем, оскільки забезпечує створення раціональної архітектури даних, що ефективно запобігає надлишковості інформації та можливим аномаліям. В контексті розробки інформаційної системи пакункового репозиторію, нормалізація набуває особливої значущості через комплексну природу зв'язків між пакунками, їх відносинами, залежностями та користувачами системи.
Розмір ER-діаграми інформаційної системи містить понад 25 атрибутів, тому згідно з методичними вказівками з курсового проектування було обрано частину, що охоплює 5 взаємопов'язаних сутностей: "Користувач", "Пакунок", "База пакунку", "Тип ролі" та "Роль" @normal_er_frag. Така декомпозиція дозволяє детально опрацювати найбільш критичні аспекти системи, забезпечуючи при цьому можливість подальшого масштабування.
#img("img/normal/er_frag.png", "Фрагмент ER-діаграми")
#img("img/normal/er_frag.png", "Фрагмент ER-діаграми", height: 65%)
Відповідно до теорії реляційних баз даних, відношення вважається нормалізованим до першої нормальної форми (1НФ) за умови дотримання наступних критичних вимог: атомарності даних (кожне поле містить лише одне значення), унікальності ідентифікації записів через первинні ключі, відсутності порожніх значень у ключових полях, та незалежності від фізичного порядку розташування записів.
За визначенням, відношення знаходиться в першій нормальній формі (1НФ) @normalization, якщо всі його атрибути є атомарними, тобто неподільними. Для практичної перевірки відповідності 1НФ, створимо універсальне відношення Т @normal_t, що інтегрує атрибути всіх релевантних сутностей. Це відношення формує фундаментальну структуру для подальшої нормалізації даних.
Для практичної перевірки відповідності 1НФ, створимо універсальне відношення T @normal_t, що інтегрує атрибути всіх релевантних сутностей. Це відношення формує фундаментальну структуру для подальшої нормалізації даних.
#img("img/normal/t.png", "Універсальне відношення Т")
При визначенні первинного ключа особлива увага приділяється семантичному аналізу даних. Зокрема можна помітити потребу бачити яку роль має користувач для кожного пакунку. Тому що найкращим ключовими атрибутами стануть "Пакунок id" та "Роль id". Ці атрибути надають можливість унікально ідентифікувати універсальне відношення та їх сполучення охоплює всі атрибути відношення.
При визначенні первинного ключа особлива увага приділяється семантичному аналізу даних. Зокрема можна помітити потребу бачити яку роль має користувач для кожного пакунку. Тому найкращим ключовими атрибутами стануть "Пакунок id" та "Роль id". Вони надають можливість унікально ідентифікувати універсальне відношення та їх сполучення охоплює всі атрибути відношення.
#img("img/normal/t.png", "Універсальне відношення T")
Всі визначені атрибути є неподільними, а значення атомарні. Сполучений первиний ключ, який складаєтсья з "Пакунок id" та "Роль id", дозволяє унікально ідентифікувати кожний кортеж. Ключові поля не мають порожніх значень, кортежі не мають фіксованого порядку, тому, універсальне вдношення Т знаходиться в першій нормальній формі (1НФ).
Всі визначені атрибути є неподільними, а значення атомарні. Сполучений первиний ключ який складаєтсья з "Пакунок id" та "Роль id" дозволяє унікально ідентифікувати кожний кортеж. Ключові поля не мають порожніх значень, кортежі не мають фіксованого порядку, тому, універсальне вдношення Т знаходиться в першій нормальній формі.
Для перевірки універсального відношення Т на відповідність другій нормальній формі (2НФ) @normalization, проаналізуємо його на існування часткових функціональних залежностей неключових атрибутів від частини первинного ключа @normal_t_dep.
Щоб перевірити універсальне відношення Т на відповідність другій нормальній формі (2НФ), потрібно проаналізувати його на існування часткових функціональних залежностей неключових атрибутів від частини первинного ключа.
Перелік атрибутів які залежать лише від частини ключа:
- "Роль id": Пакунок id, Назва, Версія, Опис, Веб-покликання, Дата позначення, Дата створення, Дата оновлення, База пакунку id, Назва, Опис, Дата створення, Дата оновлення;
- "Пакунок id": Роль id, Коментар, Тип ролі id, Назва, Опис, Користувач id, Ім'я, Електронна пошта, Пароль Дата логіну, Дата створення, Дата оновлення.
Для перевірки чи знаходиться відношення Т в другій нормальній формі визначимо функціональні залежності атрибутів @normal_t_dep.
Можемо зробити висновок, що відношення не знаходиться в другій нормальній формі, адже деякі атрибути мають неповні функціональні залежності (залежать лише від частини ключа). Для приведення універсального відношення Т до другої нормальної форми виділимо з нього два універсальних відношення Т1 та Т2 @normal_t12, де Т1 буде містити атрибути які повінстю залежать від частини ключа "Роль id", а Т2 буде містити атрибути які повінстю залежать від частини ключа "Пакунок id".
Можемо зробити висновок, що відношення не знаходиться в другій нормальній формі, адже деякі атрибути мають неповні функціональні залежності (залежать лише від частини ключа). Для приведення універсального відношення Т до другої нормальної форми виділимо з нього три універсальних відношення T1 та T2 виходячи з помічених раніше неповних функціональних залежностей @normal_t12.
//TODO: обгрунтувати чому саме t1, t2
Т1 та Т2 зберігають першу нормальну форму (1НФ) та не містять неповних функціональних залежностей, оскільки кожне з них має лише один ключовий атрибут. Таким чином, можна зробити висновок, що ці відношення відповідають вимогам другої нормальної форми (2НФ).
Відношення зберігають першу нормальну форму та не містять неповних функціональних залежностей, оскільки кожне з них має лише один ключовий атрибут. Таким чином, можна зробити висновок, що ці відношення відповідають вимогам другої нормальної форми.
#img("img/normal/t_dep.png", "Універсальне відношення Т із визначеними залежностями")
#img(
"img/normal/t_dep.png",
"Універсальне відношення T із визначеними залежностями",
)
#img("img/normal/t12.png", "Універсальні відношення Т1 та Т2", height: 60%)
#img("img/normal/t12.png", "Універсальні відношення T1 та Т2")
Після декомпозиції до 2НФ для досягнення третьої нормальної форми (3НФ) @normalization в T1 та T2 кожен неключовий атрибут повинен залежати безпосередньо від первинного ключа. Наявні транзитивні залежності від неключових атрибутів:
- "Тип ролі id": Назва, Опис;
- "База пакунку id": Назва, Опис, Дата створення, Дата оновлення;
- "Користувач id": Ім'я, Електронна пошта, Пароль, Дата логіну, Дата створення, Дата оновлення.
Для досягнення третьої нормальної форми (3НФ) таблиці мають бути в другій нормальній формі, a також в відношеннях не має бути транзитивних залежностей, тобто кожен неключовий атрибут повинен залежати безпосередньо від первинного ключа, а не через інші неключові атрибути.
Проаналізувавши відношення Т1 та Т2 можемо побачити, що всі атрибути в них залежать від ключів "Пакунок id" та "Роль id" відповідно, але ще існують інші залежності:
- "База пакунку id" - "Назва", "Опис", "Дата створення", "Дата оновлення";
- "Тип ролі id" - "Назва", "Опис";
- "Користувач id" - "Ім'я", "Електронна пошта", "Пароль", "Дата логіну", "Дата створення", "Дата оновлення";
Винесемо з відношення Т1 два відношення "Тип ролі" та "Користувач" за допомогою ключів "Тип ролі id" та "Користувач id" відповідно. Після чого сформуємо відношення T3 в котрому залишимо тільки зовнішні ключі "Тип ролі id" та "Користувач id" @normal_t23.
Винесемо з відношення Т1 два відношення "Тип ролі" та "Користувач" за допомогою ключів "Тип ролі id" та "Користувач id" відповідно. Після чого сформуємо відношення Т3 в котрому залишимо тільки зовнішні ключі "Тип ролі id" та "Користувач id" @normal_t23.
#img("img/normal/t23.png", [Відношення "Тип ролі", "Користувач", Т2 та Т3])
Можна помітити, що відношення Т2 та Т3 мають спільне відношення "База пакунку id". Утворимо відношення "Пакунок" та "Роль" виділив з відношень Т2 та Т3 відповідно відношення "База пакунку", залишимо на його місці ключ "База пакунку id".
#img(
"img/normal/normal.png",
[Відношення "Користувач", "Пакунок", "База пакунку", "Тип ролі" та "Роль"],
)
#img("img/normal/normal.png", [Відношення "Користувач", "Пакунок", "База пакунку", "Тип ролі" та "Роль"], height: 70%)
Тепер необхідно перевірити отримані відношення на відповідність третій нормальній формі.
Всі відношення мають первинні ключі, їх атрибути є атомарними, тому відношення відповідають вимогам першої нормальної форми. Крім того, всі атрибути кожного відношення повністю функціонально залежать від первинного ключа, що у поєднанні з відповідністю першій нормальній формі дозволяє зробити висновок, що відношення відповідають другій нормальній формі. Оскільки жодне відношення не містить транзитивних залежностей, а також відповідає вимогам другої нормальної форми, можна зробити висновок, що відношення знаходяться у третій нормальній формі.
Тепер всі відношення знаходяться в третій нормальній формі (3НФ): вони мають первинні ключі та всі атрибути є атомарними (1НФ), всі атрибути кожного відношення повністю функціонально залежать від первинного ключа (2НФ), жодне відношення не містить транзитивних залежностей (3НФ).
Побудуємо схему даних та позначимо зв'язки між сутностями @normal_linked. Після цього порівняємо отриману схему даних з початковою ER-діаграмою для подальшого аналізу.
#img("img/normal/linked.png", "Побудована схема даних")
#img("img/normal/linked.png", "Побудована схема даних", height: 75%)
Порівняння отриманої схеми даних з початковою ER-діаграмою @er_diagram показує їхню повну відповідність, що свідчить про успішну нормалізацію схеми даних до третьої нормальної форми. Це значить, що всі сутності, атрибути та зв'язки між ними були правильно ідентифіковані, а всі непотрібні залежності та повторення даних були усунуті. Це забезпечить ефективність та масштабованість інформаційної системи.
Порівняння отриманої схеми даних з початковою ER-діаграмою @er_diagram показує їхню повну відповідність, що свідчить про успішну нормалізацію схеми даних до третьої нормальної форми (3НФ). Це значить, що всі сутності, їх атрибути та зв'язки були правильно визначені, а всі непотрібні залежності та повторення даних були усунуті. Це забезпечить ефективність та масштабованість інформаційної системи.
= Опис програми //{{{1
@ -720,12 +747,6 @@ CREATE TABLE PackageBaseRoles (
name VARCHAR(31) UNIQUE NOT NULL,
description VARCHAR(255) NULL
);
INSERT INTO PackageBaseRoles (id, name) VALUES
(1, 'submitter'),
(2, 'packager'),
(3, 'maintainer'),
(4, 'flagger');
```
Таблиця "PackageBaseUserRoles" описує ролі користувачів для баз пакунків:
@ -790,12 +811,6 @@ CREATE TABLE DependencyTypes (
id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(31) UNIQUE NOT NULL
);
INSERT INTO DependencyTypes (id, name) VALUES
(1, 'depends'),
(2, 'makedepends'),
(3, 'checkdepends'),
(4, 'optdepends');
```
Таблиця "PackageDependencies" відображає залежності пакунків:
@ -834,11 +849,6 @@ CREATE TABLE RelationTypes (
id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(31) UNIQUE NOT NULL
);
INSERT INTO RelationTypes (id, name) VALUES
(1, 'conflicts'),
(2, 'provides'),
(3, 'replaces');
```
Таблиця "PackageRelations" описує зв'язки між пакунками: