chartlet 0.21.0

Compile structured data into deterministic, accessible static charts
Documentation
1
<div class="chartlet-wrapper"><style>.chartlet-stage{position:relative;display:inline-block;max-width:100%;vertical-align:top}.chartlet-stage>svg{display:block}.chartlet-hotspot{position:absolute;border-radius:8px;cursor:pointer}.chartlet-focus{margin:0 0 12px 0;font-size:13px}.chartlet-focus summary{cursor:pointer;min-height:24px}.chartlet-focus fieldset{border:none;padding:0;margin:4px 0 0 0}.chartlet-focus label{display:inline-flex;align-items:center;min-height:44px;margin-right:14px;cursor:pointer;white-space:nowrap}.chartlet-focus input{margin-right:4px}.chartlet-focus input:focus-visible{outline:2px solid #2563eb;outline-offset:2px}.chartlet-wrapper:has(.chartlet-focus-node:checked) #chartlet-dc093feb2f245ec0 .chartlet-f{opacity:.18}#chartlet-dc093feb2f245ec0-hs-0{left:44.629%;top:1.538%;width:12.44%;height:9.487%}#chartlet-dc093feb2f245ec0-hs-1{left:42.427%;top:22.198%;width:14.373%;height:14.872%}#chartlet-dc093feb2f245ec0-hs-2{left:25.376%;top:43.35%;width:14.187%;height:9.487%}#chartlet-dc093feb2f245ec0-hs-3{left:42.673%;top:43.35%;width:13.88%;height:9.487%}#chartlet-dc093feb2f245ec0-hs-4{left:42.927%;top:64.138%;width:19.76%;height:9.359%}#chartlet-dc093feb2f245ec0-hs-5{left:46.467%;top:78.031%;width:12.68%;height:7.564%}#chartlet-dc093feb2f245ec0-hs-6{left:59.664%;top:43.222%;width:21.6%;height:9.744%}#chartlet-dc093feb2f245ec0-hs-7{left:18.736%;top:65.869%;width:21.08%;height:5.897%}#chartlet-dc093feb2f245ec0-hs-8{left:41.993%;top:90.128%;width:21.627%;height:7.821%}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-0:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-0{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-0:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-0 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-0:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-0 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-1:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-1{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-1:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-1 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-1:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-1 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-2:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-2{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-2:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-2 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-2:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-2 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-3:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-3{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-3:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-3 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-3:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-3 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-4:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-4{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-4:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-4 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-4:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-4 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-5:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-5{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-5:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-5 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-5:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-5 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-6:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-6{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-6:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-6 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-6:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-6 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-7:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-7{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-7:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-7 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-7:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-7 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-8:checked) #chartlet-dc093feb2f245ec0 .chartlet-f-8{opacity:1}.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-8:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-8 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-dc093feb2f245ec0-focus-8:checked) #chartlet-dc093feb2f245ec0 .chartlet-n-8 .chartlet-seq-box{stroke-width:3}</style><details class="chartlet-focus"><summary>Focus</summary><fieldset><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" checked> Show all</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-0"> Customer</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-1"> Subscription</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-2"> Plan</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-3"> Invoice</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-4"> PaymentMethod</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-5"> Card</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-6"> Renews on the same day each month; a cancelled subscription runs to the end of its period.</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-7"> Prices are net; tax is added on the invoice.</label><label><input type="radio" name="chartlet-dc093feb2f245ec0-focus" class="chartlet-focus-node" id="chartlet-dc093feb2f245ec0-focus-8"> Stores only a token from the payment provider, never the card number.</label></fieldset></details><figure class="chartlet-figure" data-viz="chart"><figcaption>Subscription model</figcaption><div class="chartlet-stage"><svg xmlns="http://www.w3.org/2000/svg" width="900" height="780" viewBox="0 0 900 780" id="chartlet-dc093feb2f245ec0" role="img" aria-labelledby="chartlet-dc093feb2f245ec0-title chartlet-dc093feb2f245ec0-description" class="chartlet-root"><title id="chartlet-dc093feb2f245ec0-title">Subscription model</title><desc id="chartlet-dc093feb2f245ec0-description">Class diagram with 6 classes and 5 relations. Most connected: Subscription (3 connections). Bottleneck: Invoice; without it the diagram falls apart. Customer: 2 attributes, 0 methods. Customer is associated with Subscription (1 : 0..*) [holds]. Subscription: 2 attributes, 2 methods. Subscription is associated with Plan (* : 1); is composed of Invoice (1 : 0..*). Plan: 2 attributes, 0 methods. Invoice: 2 attributes, 0 methods. Invoice depends on PaymentMethod [paid with]. PaymentMethod: 0 attributes, 1 method. Card: 1 attribute, 0 methods. Card implements PaymentMethod. Note on Subscription: Renews on the same day each month; a cancelled subscription runs to the end of its period. Note on Plan: Prices are net; tax is added on the invoice. Note on Card: Stores only a token from the payment provider, never the card number.</desc><style>.chartlet-root{--chartlet-text:#172033;--chartlet-muted:#344054;--chartlet-grid:#d9dee8;--chartlet-zero:#667085;--chartlet-accent:#2563eb;--chartlet-background:#fff;--chartlet-color-1:#2563eb;--chartlet-color-2:#c2410c;--chartlet-color-3:#475569;--chartlet-color-4:#111827;max-width:100%;height:auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:var(--chartlet-text)}#chartlet-dc093feb2f245ec0 .chartlet-title{font-size:22px;font-weight:650;fill:var(--chartlet-text)}#chartlet-dc093feb2f245ec0 .chartlet-label,#chartlet-dc093feb2f245ec0 .chartlet-tick,#chartlet-dc093feb2f245ec0 .chartlet-value,#chartlet-dc093feb2f245ec0 .chartlet-value-inverse,#chartlet-dc093feb2f245ec0 .chartlet-axis-title{font-size:12px;fill:var(--chartlet-muted)}#chartlet-dc093feb2f245ec0 .chartlet-value,#chartlet-dc093feb2f245ec0 .chartlet-value-inverse{font-weight:600}#chartlet-dc093feb2f245ec0 .chartlet-value{fill:var(--chartlet-text)}#chartlet-dc093feb2f245ec0 .chartlet-value-inverse{fill:var(--chartlet-background)}#chartlet-dc093feb2f245ec0 .chartlet-axis-title{font-weight:600}#chartlet-dc093feb2f245ec0 .chartlet-grid{stroke:var(--chartlet-grid);stroke-width:1}#chartlet-dc093feb2f245ec0 .chartlet-zero{stroke:var(--chartlet-zero);stroke-width:1.5}#chartlet-dc093feb2f245ec0 .chartlet-bar,#chartlet-dc093feb2f245ec0 .chartlet-point{fill:var(--chartlet-accent)}#chartlet-dc093feb2f245ec0 .chartlet-line{fill:none;stroke:var(--chartlet-accent);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}#chartlet-dc093feb2f245ec0 .chartlet-background{fill:var(--chartlet-background)}.chartlet-root{--chartlet-node:#eef3fd;--chartlet-lane:#f7f9fc;--chartlet-lane-head:#eef1f6;--chartlet-shadow:rgba(16,24,40,.09);--chartlet-role-blue:#2563eb;--chartlet-role-blue-fill:#eef3fd;--chartlet-role-green:#15803d;--chartlet-role-green-fill:#ecfdf3;--chartlet-role-amber:#b45309;--chartlet-role-amber-fill:#fff7e8;--chartlet-role-teal:#0f766e;--chartlet-role-teal-fill:#e9faf7;--chartlet-role-violet:#6d28d9;--chartlet-role-violet-fill:#f3efff;--chartlet-role-gray:#667085;--chartlet-role-gray-fill:#f7f8fa;--chartlet-role-red:#b42318;--chartlet-role-red-fill:#fef3f2}.chartlet-root.chartlet-theme-dark{--chartlet-node:#1a2436;--chartlet-lane:#121925;--chartlet-lane-head:#18212f;--chartlet-shadow:rgba(0,0,0,.45);--chartlet-role-blue:#7ea6ff;--chartlet-role-blue-fill:#1a2436;--chartlet-role-green:#4ade80;--chartlet-role-green-fill:#132a1d;--chartlet-role-amber:#fbbf24;--chartlet-role-amber-fill:#2b2210;--chartlet-role-teal:#2dd4bf;--chartlet-role-teal-fill:#0f2a27;--chartlet-role-violet:#b69cff;--chartlet-role-violet-fill:#231c38;--chartlet-role-gray:#9aa7bd;--chartlet-role-gray-fill:#161d29;--chartlet-role-red:#f97066;--chartlet-role-red-fill:#2d1616}#chartlet-dc093feb2f245ec0 .chartlet-role-blue{fill:var(--chartlet-role-blue-fill);stroke:var(--chartlet-role-blue)}#chartlet-dc093feb2f245ec0 .chartlet-role-green{fill:var(--chartlet-role-green-fill);stroke:var(--chartlet-role-green)}#chartlet-dc093feb2f245ec0 .chartlet-role-amber{fill:var(--chartlet-role-amber-fill);stroke:var(--chartlet-role-amber)}#chartlet-dc093feb2f245ec0 .chartlet-role-teal{fill:var(--chartlet-role-teal-fill);stroke:var(--chartlet-role-teal)}#chartlet-dc093feb2f245ec0 .chartlet-role-violet{fill:var(--chartlet-role-violet-fill);stroke:var(--chartlet-role-violet)}#chartlet-dc093feb2f245ec0 .chartlet-role-gray{fill:var(--chartlet-role-gray-fill);stroke:var(--chartlet-role-gray)}#chartlet-dc093feb2f245ec0 .chartlet-role-red{fill:var(--chartlet-role-red-fill);stroke:var(--chartlet-role-red)}#chartlet-dc093feb2f245ec0 .chartlet-diagram-rim{fill:none;stroke:var(--chartlet-role-teal);stroke-width:1.5}#chartlet-dc093feb2f245ec0 .chartlet-diagram-shadow{fill:var(--chartlet-shadow);stroke:none;rx:7px}#chartlet-dc093feb2f245ec0 .chartlet-diagram-chip{fill:var(--chartlet-background);stroke:var(--chartlet-grid);stroke-width:1;rx:4px}#chartlet-dc093feb2f245ec0 .chartlet-diagram-chip-text{font-size:12px;fill:var(--chartlet-text)}#chartlet-dc093feb2f245ec0 .chartlet-diagram-badge{fill:var(--chartlet-accent);stroke:var(--chartlet-background);stroke-width:2}#chartlet-dc093feb2f245ec0 .chartlet-diagram-badge-text{font-size:10px;font-weight:700;fill:var(--chartlet-background)}#chartlet-dc093feb2f245ec0 .chartlet-flow-node{stroke-width:1.5;stroke-linejoin:round;rx:7px}#chartlet-dc093feb2f245ec0 .chartlet-flow-start{stroke-width:2}#chartlet-dc093feb2f245ec0 .chartlet-flow-end{stroke-width:3}#chartlet-dc093feb2f245ec0 .chartlet-flow-external{stroke-dasharray:5 3}#chartlet-dc093feb2f245ec0 .chartlet-flow-inner{stroke:var(--chartlet-role-blue);stroke-width:1}#chartlet-dc093feb2f245ec0 .chartlet-flow-label{font-size:13px;font-weight:600;fill:var(--chartlet-text)}#chartlet-dc093feb2f245ec0 .chartlet-flow-sublabel{font-size:11px;fill:var(--chartlet-muted)}#chartlet-dc093feb2f245ec0 .chartlet-flow-edge{fill:none;stroke:var(--chartlet-zero);stroke-width:1.4;stroke-linejoin:round}#chartlet-dc093feb2f245ec0 .chartlet-flow-dashed{stroke-dasharray:5 4}#chartlet-dc093feb2f245ec0 .chartlet-flow-dotted{stroke-dasharray:1 4;stroke-linecap:round}#chartlet-dc093feb2f245ec0 .chartlet-flow-main{stroke:var(--chartlet-accent);stroke-width:2.2}#chartlet-dc093feb2f245ec0 .chartlet-flow-head{fill:var(--chartlet-zero);stroke:var(--chartlet-zero);stroke-width:1;stroke-linejoin:round}#chartlet-dc093feb2f245ec0 .chartlet-flow-main-head{fill:var(--chartlet-accent);stroke:var(--chartlet-accent)}#chartlet-dc093feb2f245ec0 .chartlet-flow-lane{fill:var(--chartlet-lane);stroke:var(--chartlet-grid);stroke-width:1}#chartlet-dc093feb2f245ec0 .chartlet-flow-lane-alt{fill:var(--chartlet-background)}#chartlet-dc093feb2f245ec0 .chartlet-flow-lane-head{fill:var(--chartlet-lane-head);stroke:none}#chartlet-dc093feb2f245ec0 .chartlet-flow-lane-label{font-size:11px;font-weight:650;fill:var(--chartlet-muted);letter-spacing:.04em}#chartlet-dc093feb2f245ec0 .chartlet-flow-group{fill:var(--chartlet-accent);fill-opacity:.04;stroke:var(--chartlet-zero);stroke-width:1;stroke-dasharray:4 3;rx:10px}#chartlet-dc093feb2f245ec0 .chartlet-flow-group-label{font-size:11px;font-weight:600;fill:var(--chartlet-muted)}#chartlet-dc093feb2f245ec0 .chartlet-class-member{font-size:12px;fill:var(--chartlet-text)}#chartlet-dc093feb2f245ec0 .chartlet-flow-card{font-size:11px;fill:var(--chartlet-muted)}#chartlet-dc093feb2f245ec0 .chartlet-flow-hollow{fill:var(--chartlet-background)}#chartlet-dc093feb2f245ec0 .chartlet-flow-solid{fill:var(--chartlet-zero)}#chartlet-dc093feb2f245ec0 .chartlet-flow-open{fill:none;stroke:var(--chartlet-zero);stroke-width:1.4;stroke-linejoin:round}</style><g class="chartlet-f chartlet-f-0 chartlet-f-1"><polyline points="457.64,86 457.64,142.832 446.52,142.832 446.52,173.142" class="chartlet-flow-edge"><title>Customer → Subscription: holds</title></polyline><text x="434.52" y="169.142" text-anchor="end" class="chartlet-flow-card">0..*</text><text x="445.64" y="98" text-anchor="end" class="chartlet-flow-card">1</text></g><g class="chartlet-f chartlet-f-1 chartlet-f-2"><polyline points="422.52,289.142 422.52,306.823 292.22,306.823 292.22,338.134" class="chartlet-flow-edge"><title>Subscription → Plan</title></polyline><text x="280.22" y="334.134" text-anchor="end" class="chartlet-flow-card">1</text><text x="410.52" y="301.142" text-anchor="end" class="chartlet-flow-card">*</text></g><g class="chartlet-f chartlet-f-1 chartlet-f-3"><polyline points="446.52,305.142 446.52,338.134" class="chartlet-flow-edge"><title>Subscription → Invoice</title></polyline><text x="434.52" y="334.134" text-anchor="end" class="chartlet-flow-card">0..*</text><polyline points="446.52,289.142 451.52,297.142 446.52,305.142 441.52,297.142 446.52,289.142" class="chartlet-flow-head chartlet-flow-solid"/><text x="434.52" y="317.142" text-anchor="end" class="chartlet-flow-card">1</text></g><g class="chartlet-f chartlet-f-3 chartlet-f-4"><polyline points="446.52,412.134 446.52,469.966 475.26,469.966 475.26,492.276" class="chartlet-flow-edge chartlet-flow-dashed"><title>Invoice → PaymentMethod: paid with</title></polyline><polyline points="471.76,492.276 475.26,500.276 478.76,492.276" class="chartlet-flow-open"/></g><g class="chartlet-f chartlet-f-4 chartlet-f-5"><polyline points="475.26,585.276 475.26,608.638" class="chartlet-flow-edge chartlet-flow-dashed"><title>PaymentMethod → Card</title></polyline><polyline points="475.26,573.276 481.26,585.276 469.26,585.276 475.26,573.276" class="chartlet-flow-head chartlet-flow-hollow"/></g><g class="chartlet-f chartlet-f-1 chartlet-f-6"><polyline points="470.52,289.142 470.52,306.823 634.18,306.823 634.18,337.134" class="chartlet-flow-edge chartlet-flow-dotted"/></g><g class="chartlet-f chartlet-f-2 chartlet-f-7"><polyline points="292.22,412.134 292.22,469.966 263.48,469.966 263.48,513.776" class="chartlet-flow-edge chartlet-flow-dotted"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-8"><polyline points="475.26,667.638 475.26,703" class="chartlet-flow-edge chartlet-flow-dotted"/></g><g class="chartlet-f chartlet-n-0 chartlet-f-0 chartlet-f-1"></g><g class="chartlet-f chartlet-n-1 chartlet-f-0 chartlet-f-1 chartlet-f-2 chartlet-f-3 chartlet-f-6"></g><g class="chartlet-f chartlet-n-2 chartlet-f-1 chartlet-f-2 chartlet-f-7"></g><g class="chartlet-f chartlet-n-3 chartlet-f-1 chartlet-f-3 chartlet-f-4"></g><g class="chartlet-f chartlet-n-4 chartlet-f-3 chartlet-f-4 chartlet-f-5"></g><g class="chartlet-f chartlet-n-5 chartlet-f-4 chartlet-f-5 chartlet-f-8"></g><g class="chartlet-f chartlet-n-6 chartlet-f-1 chartlet-f-6"></g><g class="chartlet-f chartlet-n-7 chartlet-f-2 chartlet-f-7"></g><g class="chartlet-f chartlet-n-8 chartlet-f-5 chartlet-f-8"></g><g class="chartlet-f chartlet-f-0 chartlet-f-1"><rect x="461.64" y="91" width="40.48" height="21" class="chartlet-diagram-chip"/><text x="466.64" y="105" text-anchor="start" class="chartlet-diagram-chip-text">holds</text></g><g class="chartlet-f chartlet-f-3 chartlet-f-4"><rect x="450.52" y="417.134" width="65.44" height="21" class="chartlet-diagram-chip"/><text x="455.52" y="431.134" text-anchor="start" class="chartlet-diagram-chip-text">paid with</text></g><g class="chartlet-f chartlet-n-0 chartlet-f-0 chartlet-f-1"><rect x="401.66" y="14.5" width="111.96" height="74" class="chartlet-diagram-shadow"/><rect x="401.66" y="12" width="111.96" height="74" class="chartlet-flow-node chartlet-role-blue chartlet-class"><title>Customer</title></rect><text x="457.64" y="38" text-anchor="middle" class="chartlet-flow-label">Customer</text><line x1="401.66" y1="44" x2="513.62" y2="44" class="chartlet-flow-inner"/><text x="413.66" y="61" text-anchor="start" class="chartlet-class-member">+ id: Int</text><text x="413.66" y="76" text-anchor="start" class="chartlet-class-member">+ email: String</text></g><g class="chartlet-f chartlet-n-1 chartlet-f-0 chartlet-f-1 chartlet-f-2 chartlet-f-3 chartlet-f-6"><rect x="381.84" y="175.642" width="129.36" height="116" class="chartlet-diagram-shadow"/><rect x="381.84" y="173.142" width="129.36" height="116" class="chartlet-flow-node chartlet-role-blue chartlet-class"><title>Subscription</title></rect><text x="446.52" y="199.142" text-anchor="middle" class="chartlet-flow-label">Subscription</text><line x1="381.84" y1="205.142" x2="511.2" y2="205.142" class="chartlet-flow-inner"/><text x="393.84" y="222.142" text-anchor="start" class="chartlet-class-member">+ startsOn: Date</text><text x="393.84" y="237.142" text-anchor="start" class="chartlet-class-member">+ renewsOn: Date</text><line x1="381.84" y1="247.142" x2="511.2" y2="247.142" class="chartlet-flow-inner"/><text x="393.84" y="264.142" text-anchor="start" class="chartlet-class-member">+ renew()</text><text x="393.84" y="279.142" text-anchor="start" class="chartlet-class-member">+ cancel()</text></g><g class="chartlet-f chartlet-n-2 chartlet-f-1 chartlet-f-2 chartlet-f-7"><rect x="228.38" y="340.634" width="127.68" height="74" class="chartlet-diagram-shadow"/><rect x="228.38" y="338.134" width="127.68" height="74" class="chartlet-flow-node chartlet-role-blue chartlet-class"><title>Plan</title></rect><text x="292.22" y="364.134" text-anchor="middle" class="chartlet-flow-label">Plan</text><line x1="228.38" y1="370.134" x2="356.06" y2="370.134" class="chartlet-flow-inner"/><text x="240.38" y="387.134" text-anchor="start" class="chartlet-class-member">+ name: String</text><text x="240.38" y="402.134" text-anchor="start" class="chartlet-class-member">+ monthly: Money</text></g><g class="chartlet-f chartlet-n-3 chartlet-f-1 chartlet-f-3 chartlet-f-4"><rect x="384.06" y="340.634" width="124.92" height="74" class="chartlet-diagram-shadow"/><rect x="384.06" y="338.134" width="124.92" height="74" class="chartlet-flow-node chartlet-role-blue chartlet-class"><title>Invoice</title></rect><text x="446.52" y="364.134" text-anchor="middle" class="chartlet-flow-label">Invoice</text><line x1="384.06" y1="370.134" x2="508.98" y2="370.134" class="chartlet-flow-inner"/><text x="396.06" y="387.134" text-anchor="start" class="chartlet-class-member">+ number: String</text><text x="396.06" y="402.134" text-anchor="start" class="chartlet-class-member">+ total: Money</text></g><g class="chartlet-f chartlet-n-4 chartlet-f-3 chartlet-f-4 chartlet-f-5"><rect x="386.34" y="502.776" width="177.84" height="73" class="chartlet-diagram-shadow"/><rect x="386.34" y="500.276" width="177.84" height="73" class="chartlet-flow-node chartlet-role-blue chartlet-class"><title>PaymentMethod – «interface»</title></rect><text x="475.26" y="523.276" text-anchor="middle" class="chartlet-flow-sublabel">«interface»</text><text x="475.26" y="540.276" text-anchor="middle" class="chartlet-flow-label">PaymentMethod</text><line x1="386.34" y1="546.276" x2="564.18" y2="546.276" class="chartlet-flow-inner"/><text x="398.34" y="563.276" text-anchor="start" class="chartlet-class-member">+ charge(amount: Money)</text></g><g class="chartlet-f chartlet-n-5 chartlet-f-4 chartlet-f-5 chartlet-f-8"><rect x="418.2" y="611.138" width="114.12" height="59" class="chartlet-diagram-shadow"/><rect x="418.2" y="608.638" width="114.12" height="59" class="chartlet-flow-node chartlet-role-blue chartlet-class"><title>Card</title></rect><text x="475.26" y="634.638" text-anchor="middle" class="chartlet-flow-label">Card</text><line x1="418.2" y1="640.638" x2="532.32" y2="640.638" class="chartlet-flow-inner"/><text x="430.2" y="657.638" text-anchor="start" class="chartlet-class-member">- token: String</text></g><g class="chartlet-f chartlet-n-6 chartlet-f-1 chartlet-f-6"><polyline points="536.98,337.134 721.38,337.134 731.38,347.134 731.38,413.134 536.98,413.134 536.98,337.134" class="chartlet-flow-node chartlet-role-amber"><title>Renews on the same day each month; a cancelled subscription runs to the end of its period.</title></polyline><polyline points="721.38,337.134 721.38,347.134 731.38,347.134" class="chartlet-flow-node chartlet-role-amber"/><text x="548.98" y="356.134" text-anchor="start" class="chartlet-diagram-chip-text">Renews on the same day</text><text x="548.98" y="371.134" text-anchor="start" class="chartlet-diagram-chip-text">each month; a cancelled</text><text x="548.98" y="386.134" text-anchor="start" class="chartlet-diagram-chip-text">subscription runs to the end</text><text x="548.98" y="401.134" text-anchor="start" class="chartlet-diagram-chip-text">of its period.</text></g><g class="chartlet-f chartlet-n-7 chartlet-f-2 chartlet-f-7"><polyline points="168.62,513.776 348.34,513.776 358.34,523.776 358.34,559.776 168.62,559.776 168.62,513.776" class="chartlet-flow-node chartlet-role-amber"><title>Prices are net; tax is added on the invoice.</title></polyline><polyline points="348.34,513.776 348.34,523.776 358.34,523.776" class="chartlet-flow-node chartlet-role-amber"/><text x="180.62" y="532.776" text-anchor="start" class="chartlet-diagram-chip-text">Prices are net; tax is added</text><text x="180.62" y="547.776" text-anchor="start" class="chartlet-diagram-chip-text">on the invoice.</text></g><g class="chartlet-f chartlet-n-8 chartlet-f-5 chartlet-f-8"><polyline points="377.94,703 562.58,703 572.58,713 572.58,764 377.94,764 377.94,703" class="chartlet-flow-node chartlet-role-amber"><title>Stores only a token from the payment provider, never the card number.</title></polyline><polyline points="562.58,703 562.58,713 572.58,713" class="chartlet-flow-node chartlet-role-amber"/><text x="389.94" y="722" text-anchor="start" class="chartlet-diagram-chip-text">Stores only a token from</text><text x="389.94" y="737" text-anchor="start" class="chartlet-diagram-chip-text">the payment provider, never</text><text x="389.94" y="752" text-anchor="start" class="chartlet-diagram-chip-text">the card number.</text></g></svg><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-0" for="chartlet-dc093feb2f245ec0-focus-0"></label><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-1" for="chartlet-dc093feb2f245ec0-focus-1"></label><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-2" for="chartlet-dc093feb2f245ec0-focus-2"></label><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-3" for="chartlet-dc093feb2f245ec0-focus-3"></label><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-4" for="chartlet-dc093feb2f245ec0-focus-4"></label><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-5" for="chartlet-dc093feb2f245ec0-focus-5"></label><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-6" for="chartlet-dc093feb2f245ec0-focus-6"></label><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-7" for="chartlet-dc093feb2f245ec0-focus-7"></label><label class="chartlet-hotspot" id="chartlet-dc093feb2f245ec0-hs-8" for="chartlet-dc093feb2f245ec0-focus-8"></label></div><p class="chartlet-source">Source: Illustrative domain model</p><details class="chartlet-data" data-viz-text><summary>Show chart data</summary><table><caption>Data for Subscription model</caption><thead><tr><th scope="col">Item</th><th scope="col">Kind</th><th scope="col">Attributes</th><th scope="col">Methods</th><th scope="col">Relations</th><th scope="col">Notes</th></tr></thead><tbody><tr><th scope="row">Customer</th><td>Class</td><td>+ id: Int; + email: String</td><td></td><td>is associated with Subscription (1 : 0..*) [holds]</td><td></td></tr><tr><th scope="row">Subscription</th><td>Class</td><td>+ startsOn: Date; + renewsOn: Date</td><td>+ renew(); + cancel()</td><td>is associated with Plan (* : 1); is composed of Invoice (1 : 0..*)</td><td>Renews on the same day each month; a cancelled subscription runs to the end of its period.</td></tr><tr><th scope="row">Plan</th><td>Class</td><td>+ name: String; + monthly: Money</td><td></td><td></td><td>Prices are net; tax is added on the invoice.</td></tr><tr><th scope="row">Invoice</th><td>Class</td><td>+ number: String; + total: Money</td><td></td><td>depends on PaymentMethod [paid with]</td><td></td></tr><tr><th scope="row">PaymentMethod</th><td>Interface</td><td></td><td>+ charge(amount: Money)</td><td></td><td></td></tr><tr><th scope="row">Card</th><td>Class</td><td>- token: String</td><td></td><td>implements PaymentMethod</td><td>Stores only a token from the payment provider, never the card number.</td></tr></tbody></table></details></figure></div>