Files
the-biergarten-app/docs/website/diagrams-out/architecture.svg
Aaron Po de1e9ac02b Migrate Auth/Brewery/Location repositories to pure Dapper; rename Timer to RowVersion (#237)
* Integrate Spectre.Console into seed application, use async data loading

* Replace stored-procedure repositories with Dapper-based repositories

Removes the USP_* stored procedures for Auth, Location, and Brewery
data access in favour of services built with Dapper.

Adds a new Features.Locations slice and optimistic concurrency support
for the Brewery slice.

* Add new documentation for Dapper migration/database schema

* Update FluentAssertions to most recent open source ver

* Update XML doc comment in backend projects

* Modify brewery location dto to include denormalized data

* - Updated column names from 'Timer' to 'RowVersion' in schema.sql for
- Introduced new BeerPost entity and updated BreweryPost and BreweryPostLocation entities to use 'RowVersion' for concurrency.
- Modified UserAccount, UserCredential, UserVerification, City, StateProvince, and Country entities to use 'RowVersion' instead of 'Timer'.
- Updated tests and command handlers to reflect changes in concurrency token from 'Timer' to 'RowVersion'.
- Adjusted BreweryDto and BreweryDtoMapper to include 'RowVersion' and updated mapping logic accordingly.
- Ensured all repository methods and interfaces are aligned with the new concurrency handling approach.

* updates

* Add BeerStyle entity and related properties; refactor BreweryDto to remove BeerPosts

* Fix Api.Specs test failures: stale Timer column refs and missing test.user fixture

* Remove repository tests, usages of registration dto used in repo

* archive old stored procs/functions

* update stale docs

* Add instructions for generating cryptographically secure base64 secrets on macOS/Linux and Windows
2026-08-24 01:09:47 -04:00

1 line
53 KiB
XML

<?plantuml 1.2026.2?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" data-diagram-type="DESCRIPTION" height="781px" preserveAspectRatio="none" style="width:2792px;height:781px;background:#FFFFFF;" version="1.1" viewBox="0 0 2792 781" width="2792px" zoomAndPan="magnify"><title>The Biergarten App - Vertical Slice Architecture</title><defs/><g><g class="title" data-source-line="6"><text fill="#000000" font-family="Arial" font-size="22" font-weight="bold" lengthAdjust="spacing" textLength="496.3643" x="1143.5229" y="35.6357">The Biergarten App - Vertical Slice Architecture</text></g><!--cluster API.Core (thin host)--><g class="cluster" data-qualified-name="API.Core .thin host." data-source-line="8" id="ent0002"><rect fill="#E3F2FD" height="150.69" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="514" x="1421.58" y="58.2979"/><text fill="#000000" font-family="Arial" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="131.4346" x="1612.8627" y="73.4297">API.Core (thin host)</text></g><!--cluster Feature Slices--><g class="cluster" data-qualified-name="Feature Slices" data-source-line="20" id="ent0007"><rect fill="#F3E5F5" height="135.74" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="1728" x="1013.58" y="271.9879"/><text fill="#000000" font-family="Arial" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="94.9375" x="1830.1113" y="287.1197">Feature Slices</text></g><!--cluster Shared--><g class="cluster" data-qualified-name="Shared" data-source-line="47" id="ent0022"><rect fill="#FCE4EC" height="135.75" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="682" x="407.58" y="470.7279"/><text fill="#000000" font-family="Arial" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="47.4619" x="724.849" y="485.8597">Shared</text></g><!--cluster Infrastructure Layer--><g class="cluster" data-qualified-name="Infrastructure Layer" data-source-line="59" id="ent0028"><rect fill="#FFF3E0" height="97.09" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="1097" x="1113.58" y="490.0579"/><text fill="#000000" font-family="Arial" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="132.2686" x="1595.9457" y="505.1897">Infrastructure Layer</text></g><!--cluster Domain Layer--><g class="cluster" data-qualified-name="Domain Layer" data-source-line="67" id="ent0034"><rect fill="#E8F5E9" height="135.74" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="696" x="2075.58" y="639.4779"/><text fill="#000000" font-family="Arial" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="92.5859" x="2377.287" y="654.6097">Domain Layer</text></g><!--cluster SQL Server--><g class="cluster" data-qualified-name="SQL Server" data-source-line="79" id="ent0040"><path d="M2234.58,485.0579 C2234.58,475.0579 2291.58,475.0579 2291.58,475.0579 C2291.58,475.0579 2348.58,475.0579 2348.58,485.0579 L2348.58,577.1479 C2348.58,587.1479 2291.58,587.1479 2291.58,587.1479 C2291.58,587.1479 2234.58,587.1479 2234.58,577.1479 L2234.58,485.0579" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M2234.58,485.0579 C2234.58,495.0579 2291.58,495.0579 2291.58,495.0579 C2291.58,495.0579 2348.58,495.0579 2348.58,485.0579" fill="none" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="76.2617" x="2253.4491" y="510.1897">SQL Server</text></g><!--entity API--><g class="entity" data-qualified-name="API.Core .thin host..API" data-source-line="9" id="ent0003"><rect fill="#FFFFFF" height="62.1973" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="223.6201" x="1437.77" y="112.0379"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1641.3901" y="117.0379"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1639.3901" y="119.0379"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1639.3901" y="123.0379"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="56.7998" x="1452.77" y="145.1697">API.Core</text><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="183.6201" x="1452.77" y="161.2683">ASP.NET Core Web API host</text></g><g class="entity" data-qualified-name="API.Core .thin host..GMN4" data-source-line="11" id="ent0005"><path d="M1695.92,93.2979 L1695.92,139.1379 L1661.7,143.1379 L1695.92,147.1379 L1695.92,192.9902 A0,0 0 0 0 1695.92,192.9902 L1919.2515,192.9902 A0,0 0 0 0 1919.2515,192.9902 L1919.2515,103.2979 L1909.2515,93.2979 L1695.92,93.2979 A0,0 0 0 0 1695.92,93.2979" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M1909.2515,93.2979 L1909.2515,103.2979 L1919.2515,103.2979 L1909.2515,93.2979" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="139.4263" x="1701.92" y="110.4917">- Program.cs wiring only</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="181.7334" x="1701.92" y="125.4404">- MediatR + AddApplicationPart</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="140.8926" x="1709.1436" y="140.3892">per Features.* assembly</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="202.3315" x="1701.92" y="155.3379">- Swagger/OpenAPI, health checks</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="133.6499" x="1701.92" y="170.2866">- JWT auth middleware</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="132.9517" x="1701.92" y="185.2354">- Global exception filter</text></g><!--entity AuthSlice--><g class="entity" data-qualified-name="Feature Slices.AuthSlice" data-source-line="21" id="ent0008"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="127.9375" x="1425.61" y="326.3079"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1533.5475" y="331.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1531.5475" y="333.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1531.5475" y="337.3079"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="87.9375" x="1440.61" y="359.4397">Features.Auth</text></g><!--entity BrewerySlice--><g class="entity" data-qualified-name="Feature Slices.BrewerySlice" data-source-line="22" id="ent0009"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="161.3789" x="2079.89" y="326.3079"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="2221.2689" y="331.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2219.2689" y="333.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2219.2689" y="337.3079"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="121.3789" x="2094.89" y="359.4397">Features.Breweries</text></g><!--entity UserSlice--><g class="entity" data-qualified-name="Feature Slices.UserSlice" data-source-line="23" id="ent0010"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="210.4131" x="1588.37" y="326.3079"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1778.7831" y="331.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1776.7831" y="333.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1776.7831" y="337.3079"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="170.4131" x="1603.37" y="359.4397">Features.UserManagement</text></g><!--entity EmailsSlice--><g class="entity" data-qualified-name="Feature Slices.EmailsSlice" data-source-line="24" id="ent0011"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="141.1445" x="1030.01" y="326.3079"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1151.1545" y="331.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1149.1545" y="333.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1149.1545" y="337.3079"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="101.1445" x="1045.01" y="359.4397">Features.Emails</text></g><!--entity LocationsSlice--><g class="entity" data-qualified-name="Feature Slices.LocationsSlice" data-source-line="25" id="ent0012"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="159.0684" x="2276.05" y="326.3079"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="2415.1184" y="331.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2413.1184" y="333.3079"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2413.1184" y="337.3079"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="119.0684" x="2291.05" y="359.4397">Features.Locations</text></g><g class="entity" data-qualified-name="Feature Slices.GMN13" data-source-line="27" id="ent0014"><path d="M1833.89,306.9879 L1833.89,391.7315 L2045.2689,391.7315 L2045.2689,316.9879 L2035.2689,306.9879 L1833.89,306.9879" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M2035.2689,306.9879 L2035.2689,316.9879 L2045.2689,316.9879 L2035.2689,306.9879" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="140.8926" x="1839.89" y="324.1817">Each slice owns its own:</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="170.498" x="1839.89" y="339.1304">- Controller (HTTP endpoints)</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="190.3789" x="1839.89" y="354.0792">- Commands/Queries + Handlers</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="65.749" x="1839.89" y="369.0279">- Validators</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="124.9854" x="1839.89" y="383.9766">- Repository (Dapper)</text></g><g class="entity" data-qualified-name="Feature Slices.GMN16" data-source-line="34" id="ent0017"><path d="M1206.08,321.9379 L1206.08,345.3579 L1171.35,349.3579 L1206.08,353.3579 L1206.08,376.784 A0,0 0 0 0 1206.08,376.784 L1391.0907,376.784 A0,0 0 0 0 1391.0907,376.784 L1391.0907,331.9379 L1381.0907,321.9379 L1206.08,321.9379 A0,0 0 0 0 1206.08,321.9379" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M1381.0907,321.9379 L1381.0907,331.9379 L1391.0907,331.9379 L1381.0907,321.9379" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="157.5171" x="1212.08" y="339.1317">No controller - invoked only</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="164.0107" x="1212.08" y="354.0804">via MediatR commands sent</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="95.3608" x="1212.08" y="369.0292">from other slices</text></g><g class="entity" data-qualified-name="Feature Slices.GMN19" data-source-line="39" id="ent0020"><path d="M2470,306.9879 L2470,345.3579 L2435.46,349.3579 L2470,353.3579 L2470,391.7315 A0,0 0 0 0 2470,391.7315 L2725.1714,391.7315 A0,0 0 0 0 2725.1714,391.7315 L2725.1714,316.9879 L2715.1714,306.9879 L2470,306.9879 A0,0 0 0 0 2470,306.9879" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M2715.1714,306.9879 L2715.1714,316.9879 L2725.1714,316.9879 L2715.1714,306.9879" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="169.0889" x="2476" y="324.1817">No controller and no MediatR</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="228.3506" x="2476" y="339.1304">handlers - exposes ILocationRepository</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="165.4517" x="2476" y="354.0792">directly as a plain DI service.</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="190.0298" x="2476" y="369.0279">Not registered by API.Core at all;</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="234.1714" x="2476" y="383.9766">only Database.Seed depends on it today</text></g><!--entity SharedContracts--><g class="entity" data-qualified-name="Shared.SharedContracts" data-source-line="48" id="ent0023"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="148.9443" x="925.11" y="525.0579"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1054.0543" y="530.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1052.0543" y="532.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1052.0543" y="536.0579"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="108.9443" x="940.11" y="558.1897">Shared.Contracts</text></g><!--entity SharedApp--><g class="entity" data-qualified-name="Shared.SharedApp" data-source-line="49" id="ent0024"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="157.5234" x="423.82" y="525.0579"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="561.3434" y="530.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="559.3434" y="532.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="559.3434" y="536.0579"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="117.5234" x="438.82" y="558.1897">Shared.Application</text></g><g class="entity" data-qualified-name="Shared.GMN25" data-source-line="51" id="ent0026"><path d="M616.64,505.7279 L616.64,544.0979 L581.51,548.0979 L616.64,552.0979 L616.64,590.4715 A0,0 0 0 0 616.64,590.4715 L890.5306,590.4715 A0,0 0 0 0 890.5306,590.4715 L890.5306,515.7279 L880.5306,505.7279 L616.64,505.7279 A0,0 0 0 0 616.64,505.7279" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M880.5306,505.7279 L880.5306,515.7279 L890.5306,515.7279 L880.5306,505.7279" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="116.3462" x="622.64" y="522.9217">- ValidationBehavior</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="105.4917" x="629.8636" y="537.8704">(MediatR pipeline)</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="174.0908" x="622.64" y="552.8192">- Cross-slice email commands</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="200.1416" x="629.8636" y="567.7679">(SendRegistrationEmailCommand,</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="245.667" x="629.8636" y="582.7166">SendResendConfirmationEmailCommand)</text></g><!--entity Sql--><g class="entity" data-qualified-name="Infrastructure Layer.Sql" data-source-line="60" id="ent0029"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="146.5996" x="2048.28" y="525.0579"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="2174.8796" y="530.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2172.8796" y="532.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2172.8796" y="536.0579"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="106.5996" x="2063.28" y="558.1897">Infrastructure.Sql</text></g><!--entity JWT--><g class="entity" data-qualified-name="Infrastructure Layer.JWT" data-source-line="61" id="ent0030"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="147.3652" x="1591.9" y="525.0579"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1719.2652" y="530.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1717.2652" y="532.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1717.2652" y="536.0579"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="107.3652" x="1606.9" y="558.1897">Infrastructure.Jwt</text></g><!--entity PwdHash--><g class="entity" data-qualified-name="Infrastructure Layer.PwdHash" data-source-line="62" id="ent0031"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="239.1992" x="1773.98" y="525.0579"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1993.1792" y="530.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1991.1792" y="532.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1991.1792" y="536.0579"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="199.1992" x="1788.98" y="558.1897">Infrastructure.PasswordHashing</text></g><!--entity Email--><g class="entity" data-qualified-name="Infrastructure Layer.Email" data-source-line="63" id="ent0032"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="161.3721" x="1129.9" y="525.0579"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1271.2721" y="530.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1269.2721" y="532.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1269.2721" y="536.0579"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="121.3721" x="1144.9" y="558.1897">Infrastructure.Email</text></g><!--entity EmailTemplates--><g class="entity" data-qualified-name="Infrastructure Layer.EmailTemplates" data-source-line="64" id="ent0033"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="230.6201" x="1326.27" y="525.0579"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="1536.8901" y="530.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1534.8901" y="532.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="1534.8901" y="536.0579"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="190.6201" x="1341.27" y="558.1897">Infrastructure.Email.Templates</text></g><!--entity Domain--><g class="entity" data-qualified-name="Domain Layer.Domain" data-source-line="68" id="ent0035"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="138.041" x="2286.56" y="693.7979"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="2404.601" y="698.7979"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2402.601" y="700.7979"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2402.601" y="704.7979"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="98.041" x="2301.56" y="726.9297">Domain.Entities</text></g><!--entity DomainExceptions--><g class="entity" data-qualified-name="Domain Layer.DomainExceptions" data-source-line="69" id="ent0036"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="160.6133" x="2091.27" y="693.7979"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="2231.8833" y="698.7979"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2229.8833" y="700.7979"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2229.8833" y="704.7979"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="120.6133" x="2106.27" y="726.9297">Domain.Exceptions</text></g><g class="entity" data-qualified-name="Domain Layer.GMN37" data-source-line="71" id="ent0038"><path d="M2459.45,674.4779 L2459.45,712.8479 L2424.98,716.8479 L2459.45,720.8479 L2459.45,759.2215 A0,0 0 0 0 2459.45,759.2215 L2755.7161,759.2215 A0,0 0 0 0 2755.7161,759.2215 L2755.7161,684.4779 L2745.7161,674.4779 L2459.45,674.4779 A0,0 0 0 0 2459.45,674.4779" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M2745.7161,674.4779 L2745.7161,684.4779 L2755.7161,684.4779 L2745.7161,674.4779" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="275.2661" x="2465.45" y="691.6717">- UserAccount, UserCredential, UserVerification</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="211.6943" x="2465.45" y="706.6204">- BreweryPost, BreweryPostLocation</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="171.228" x="2465.45" y="721.5692">- City, StateProvince, Country</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="124.998" x="2465.45" y="736.5179">- BeerPost, BeerStyle</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="226.1479" x="2472.6736" y="751.4666">(schema tables exist; no repository yet)</text></g><!--entity Tables--><g class="entity" data-qualified-name="SQL Server.Tables" data-source-line="80" id="ent0041"><rect fill="#FFFFFF" height="46.0986" rx="2.5" ry="2.5" style="stroke:#000000;stroke-width:1;" width="82.0205" x="2250.57" y="525.0579"/><rect fill="#FFFFFF" height="10" style="stroke:#000000;stroke-width:1;" width="15" x="2312.5905" y="530.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2310.5905" y="532.0579"/><rect fill="#FFFFFF" height="2" style="stroke:#000000;stroke-width:1;" width="4" x="2310.5905" y="536.0579"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="42.0205" x="2265.57" y="558.1897">Tables</text></g><!--entity Seed--><g class="entity" data-qualified-name="Seed" data-source-line="83" id="ent0042"><polygon fill="#FFFFFF" points="2068.98,122.0379,2078.98,112.0379,2252.1792,112.0379,2252.1792,164.2351,2242.1792,174.2351,2068.98,174.2351,2068.98,122.0379" style="stroke:#000000;stroke-width:1;"/><line style="stroke:#000000;stroke-width:1;" x1="2242.1792" x2="2252.1792" y1="122.0379" y2="112.0379"/><line style="stroke:#000000;stroke-width:1;" x1="2068.98" x2="2242.1792" y1="122.0379" y2="122.0379"/><line style="stroke:#000000;stroke-width:1;" x1="2242.1792" x2="2242.1792" y1="122.0379" y2="174.2351"/><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="96.5166" x="2083.98" y="145.1697">Database.Seed</text><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="59.1445" x="2083.98" y="161.2683">(separate</text><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="3.8896" x="2143.1245" y="161.2683">&#160;</text><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="48.2549" x="2147.0142" y="161.2683">console</text><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="3.8896" x="2195.2691" y="161.2683">&#160;</text><text fill="#000000" font-family="Arial" font-size="14" lengthAdjust="spacing" textLength="28.0205" x="2199.1587" y="161.2683">app)</text></g><g class="entity" data-qualified-name="GMN77" data-source-line="134" id="ent0078"><path d="M2438.66,513.2079 L2438.66,583.0028 L2656.5008,583.0028 L2656.5008,523.2079 L2646.5008,513.2079 L2438.66,513.2079" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M2646.5008,513.2079 L2646.5008,523.2079 L2656.5008,523.2079 L2646.5008,513.2079" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="109.1035" x="2444.66" y="530.4017">SQL-first approach</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="196.8408" x="2444.66" y="545.3504">Each slice's own repository issues</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="190.0234" x="2444.66" y="560.2992">inline SQL via Dapper - no ORM,</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="122.8525" x="2444.66" y="575.2479">no stored procedures</text></g><g class="entity" data-qualified-name="GMN80" data-source-line="141" id="ent0081"><path d="M78.39,520.6779 L78.39,575.524 L246.7826,575.524 L246.7826,530.6779 L236.7826,520.6779 L78.39,520.6779" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><path d="M236.7826,520.6779 L236.7826,530.6779 L246.7826,530.6779 L236.7826,520.6779" fill="#FFFFFF" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="122.8335" x="84.39" y="537.8717">No Features.* project</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="138.0234" x="84.39" y="552.8204">ever references another</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="147.3926" x="84.39" y="567.7692">Features.* project directly</text></g><!--link AuthSlice to GMN13--><g class="link" data-entity-1="ent0008" data-entity-2="ent0014" data-link-type="association" data-source-line="27" id="lnk15"><path d="M1502.34,326.2379 C1518.39,299.8079 1548.71,257.9579 1588.08,240.4879 C1630.94,221.4679 1754.72,225.3079 1799.08,240.4879 C1837.62,253.6679 1873.77,282.2079 1899.75,306.7579" fill="none" id="AuthSlice-GMN13" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/></g><!--link API to AuthSlice--><g class="link" data-entity-1="ent0003" data-entity-2="ent0008" data-link-type="dependency" data-source-line="86" id="lnk43"><path d="M1449.12,174.5579 C1425.98,186.4879 1404.18,202.8179 1390.58,224.9879 C1383.38,236.7279 1383.07,244.4379 1390.58,255.9879 C1395.35,263.3279 1401.42,258.9579 1408.58,263.9879 C1433.51,281.4879 1453.2298,302.8129 1468.4298,321.3029" fill="none" id="API-to-AuthSlice" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="1472.24,325.9379,1469.6146,316.4454,1469.0648,322.0754,1463.4348,321.5256,1472.24,325.9379" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="110.5698" x="1391.58" y="238.1817">AddApplicationPart</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="90.6826" x="1401.5236" y="253.1304">+ MediatR scan</text></g><!--link API to BrewerySlice--><g class="link" data-entity-1="ent0003" data-entity-2="ent0009" data-link-type="dependency" data-source-line="87" id="lnk44"><path d="M1591.82,174.5779 C1615.99,190.2579 1647.6,207.9379 1678.58,216.9879 C1710.2,226.2179 1793.84,221.4079 1826.58,224.9879 C1879.42,230.7679 2014.43,241.4779 2062.58,263.9879 C2093.9,278.6279 2118.9182,301.7902 2137.1382,321.4202" fill="none" id="API-to-BrewerySlice" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="2141.22,325.8179,2138.0291,316.5002,2137.8185,322.1532,2132.1656,321.9426,2141.22,325.8179" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="110.5698" x="2038.71" y="238.1817">AddApplicationPart</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="90.6826" x="2048.6536" y="253.1304">+ MediatR scan</text></g><!--link API to UserSlice--><g class="link" data-entity-1="ent0003" data-entity-2="ent0010" data-link-type="dependency" data-source-line="88" id="lnk45"><path d="M1601.51,174.6279 C1624.85,188.0279 1652.88,203.7079 1678.58,216.9879 C1686,220.8179 1691.11,217.9379 1695.58,224.9879 C1714.98,255.5579 1709.4402,293.7316 1702.3902,320.0916" fill="none" id="API-to-UserSlice" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="1700.84,325.8879,1707.0295,318.2269,1702.1318,321.0576,1699.3011,316.16,1700.84,325.8879" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="110.5698" x="1707.66" y="238.1817">AddApplicationPart</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="90.6826" x="1717.6036" y="253.1304">+ MediatR scan</text></g><!--link API to EmailsSlice--><g class="link" data-entity-1="ent0003" data-entity-2="ent0011" data-link-type="dependency" data-source-line="89" id="lnk46"><path d="M1437.42,163.9279 C1362.03,181.2179 1263.19,211.9479 1188.58,263.9879 C1162.71,282.0279 1141.2191,303.2385 1124.3591,321.5085" fill="none" id="API-to-EmailsSlice" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="1120.29,325.9179,1129.3332,322.0165,1123.6809,322.2434,1123.454,316.5911,1120.29,325.9179" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="106.939" x="1251.9" y="238.1817">MediatR scan only</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="80.1963" x="1265.2713" y="253.1304">(no controller)</text></g><!--link Seed to AuthSlice--><g class="link" data-entity-1="ent0042" data-entity-2="ent0008" data-link-type="dependency" data-source-line="91" id="lnk47"><path d="M2085.17,174.6879 C2042.12,190.5579 1986.7,208.3879 1935.58,216.9879 C1913.83,220.6479 1556.71,212.4179 1538.58,224.9879 C1505.44,247.9779 1495.4267,291.2308 1491.9567,320.0708" fill="none" id="Seed-to-AuthSlice" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="1491.24,326.0279,1496.2865,317.5701,1491.8373,321.0637,1488.3438,316.6145,1491.24,326.0279" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="109.8335" x="1558.3564" y="238.1817">AddFeaturesAuth()</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="147.3862" x="1539.58" y="253.1304">(DI only, no API/MediatR)</text></g><!--link Seed to BrewerySlice--><g class="link" data-entity-1="ent0042" data-entity-2="ent0009" data-link-type="dependency" data-source-line="92" id="lnk48"><path d="M2160.58,174.7179 C2160.58,216.2179 2160.58,283.4079 2160.58,320.0879" fill="none" id="Seed-to-BrewerySlice" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="2160.58,326.0879,2164.58,317.0879,2160.58,321.0879,2156.58,317.0879,2160.58,326.0879" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="140.8862" x="2161.58" y="245.6817">AddFeaturesBreweries()</text></g><!--link Seed to LocationsSlice--><g class="link" data-entity-1="ent0042" data-entity-2="ent0012" data-link-type="dependency" data-source-line="93" id="lnk49"><path d="M2241.11,174.6079 C2265.73,187.1279 2291.15,203.7679 2309.58,224.9879 C2335.07,254.3279 2345.8612,293.205 2350.9912,319.985" fill="none" id="Seed-to-LocationsSlice" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="2352.12,325.8779,2354.3553,316.286,2351.1793,320.9671,2346.4982,317.7911,2352.12,325.8779" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="138.7407" x="2330.1" y="245.6817">AddFeaturesLocations()</text></g><!--link AuthSlice to Sql--><g class="link" data-entity-1="ent0008" data-entity-2="ent0029" data-link-type="dependency" data-source-line="95" id="lnk50"><path d="M1509.91,372.7179 C1525.04,387.7379 1547.06,406.4079 1570.58,415.7279 C1592.54,424.4279 1653.31,419.6679 1676.58,423.7279 C1727.46,432.6079 1737.52,446.9379 1788.58,454.7279 C1815.18,458.7879 2005.7,452.4979 2030.58,462.7279 C2061.86,475.5979 2085.2487,499.7351 2101.5587,520.0951" fill="none" id="AuthSlice-to-Sql" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2105.31,524.7779,2102.8049,515.2529,2102.184,520.8756,2096.5613,520.2546,2105.31,524.7779" style="stroke:#000000;stroke-width:1;"/></g><!--link AuthSlice to JWT--><g class="link" data-entity-1="ent0008" data-entity-2="ent0030" data-link-type="dependency" data-source-line="96" id="lnk51"><path d="M1501.76,372.5279 C1514.82,394.9779 1537.13,429.8379 1562.58,454.7279 C1567.16,459.2079 1569.55,458.7579 1574.58,462.7279 C1599.45,482.3479 1621.4888,503.0339 1639.3888,520.6939" fill="none" id="AuthSlice-to-JWT" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="1643.66,524.9079,1640.0625,515.7395,1640.1007,521.3963,1634.444,521.4344,1643.66,524.9079" style="stroke:#000000;stroke-width:1;"/></g><!--link AuthSlice to PwdHash--><g class="link" data-entity-1="ent0008" data-entity-2="ent0031" data-link-type="dependency" data-source-line="97" id="lnk52"><path d="M1512.22,372.8879 C1527.55,386.9979 1548.89,404.5679 1570.58,415.7279 C1628.01,445.2879 1647.57,440.5079 1710.58,454.7279 C1730.82,459.2979 1737.18,455.3779 1756.58,462.7279 C1796.68,477.9179 1833.1043,501.6896 1859.7143,521.1196" fill="none" id="AuthSlice-to-PwdHash" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="1864.56,524.6579,1859.6502,516.12,1860.5219,521.7093,1854.9326,522.581,1864.56,524.6579" style="stroke:#000000;stroke-width:1;"/></g><!--link AuthSlice to SharedContracts--><g class="link" data-entity-1="ent0008" data-entity-2="ent0023" data-link-type="dependency" data-source-line="98" id="lnk53"><path d="M1468.34,372.7579 C1453.12,387.3879 1431.37,405.5979 1408.58,415.7279 C1287.27,469.6879 1235.99,411.3279 1113.58,462.7279 C1078.62,477.4079 1048.9168,500.9297 1027.2468,520.5597" fill="none" id="AuthSlice-to-SharedContracts" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="1022.8,524.5879,1032.1556,521.5101,1026.5057,521.231,1026.7847,515.5811,1022.8,524.5879" style="stroke:#000000;stroke-width:1;"/></g><!--link AuthSlice to SharedApp--><g class="link" data-entity-1="ent0008" data-entity-2="ent0024" data-link-type="dependency" data-source-line="99" id="lnk54"><path d="M1469.7,372.7379 C1454.63,387.9679 1432.48,406.9079 1408.58,415.7279 C1379.18,426.5879 1157.86,421.7479 1126.58,423.7279 C986.63,432.6179 952.49,445.2079 812.58,454.7279 C788.95,456.3379 621.6,454.0179 599.58,462.7279 C567,475.6179 541.83,499.9041 524.19,520.2541" fill="none" id="AuthSlice-to-SharedApp" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="520.26,524.7879,529.1775,520.6072,523.535,521.0097,523.1325,515.3672,520.26,524.7879" style="stroke:#000000;stroke-width:1;"/></g><!--link AuthSlice to SharedApp--><g class="link" data-entity-1="ent0008" data-entity-2="ent0024" data-link-type="dependency" data-source-line="116" id="lnk65"><path d="M1469.75,372.8679 C1454.7,388.1679 1432.56,407.1279 1408.58,415.7279 C1366.85,430.7079 650.86,405.2079 610.58,423.7279 C565.06,444.6579 534.0479,489.9033 517.2579,519.4033" fill="none" id="AuthSlice-to-SharedApp-1" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="514.29,524.6179,522.2182,518.7746,516.7632,520.2724,515.2654,514.8174,514.29,524.6179" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="34.6899" x="690.3354" y="436.9217">sends</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="192.2007" x="611.58" y="451.8704">SendRegistrationEmailCommand</text></g><!--link BrewerySlice to Sql--><g class="link" data-entity-1="ent0009" data-entity-2="ent0029" data-link-type="dependency" data-source-line="101" id="lnk55"><path d="M2156.18,372.5479 C2148.69,410.3279 2134.7066,480.8723 2127.1866,518.8123" fill="none" id="BrewerySlice-to-Sql" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2126.02,524.6979,2131.6935,516.6473,2126.9921,519.7933,2123.8462,515.0919,2126.02,524.6979" style="stroke:#000000;stroke-width:1;"/></g><!--link BrewerySlice to SharedContracts--><g class="link" data-entity-1="ent0009" data-entity-2="ent0023" data-link-type="dependency" data-source-line="102" id="lnk56"><path d="M2139.89,372.6679 C2114.6,398.1379 2069.14,438.3879 2020.58,454.7279 C1972.82,470.8079 1161.37,446.7379 1113.58,462.7279 C1076.69,475.0779 1046.2171,500.1507 1024.9971,520.7307" fill="none" id="BrewerySlice-to-SharedContracts" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="1020.69,524.9079,1029.9354,521.5135,1024.2792,521.4269,1024.3658,515.7707,1020.69,524.9079" style="stroke:#000000;stroke-width:1;"/></g><!--link BrewerySlice to SharedApp--><g class="link" data-entity-1="ent0009" data-entity-2="ent0024" data-link-type="dependency" data-source-line="103" id="lnk57"><path d="M2133.58,372.7979 C2114.8,387.2479 2088.56,405.2279 2062.58,415.7279 C2044.15,423.1779 2036.91,416.0479 2018.58,423.7279 C1996.52,432.9779 1997.36,447.4379 1974.58,454.7279 C1938.2,466.3779 635.32,449.2479 599.58,462.7279 C566.8,475.0979 541.6424,499.4803 524.0724,520.0003" fill="none" id="BrewerySlice-to-SharedApp" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="520.17,524.5579,529.0619,520.3231,523.422,520.7599,522.9852,515.1199,520.17,524.5579" style="stroke:#000000;stroke-width:1;"/></g><!--link UserSlice to Sql--><g class="link" data-entity-1="ent0010" data-entity-2="ent0029" data-link-type="dependency" data-source-line="105" id="lnk58"><path d="M1726.7,372.7479 C1750.6,387.6779 1784.21,406.2279 1816.58,415.7279 C1858.09,427.9079 1871.2,415.0479 1913.58,423.7279 C1967.28,434.7279 1983.42,434.7979 2030.58,462.7279 C2058.76,479.4179 2081.5284,501.5942 2098.6484,520.4642" fill="none" id="UserSlice-to-Sql" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2102.68,524.9079,2099.5951,515.5546,2099.3203,521.2048,2093.6702,520.93,2102.68,524.9079" style="stroke:#000000;stroke-width:1;"/></g><!--link UserSlice to SharedContracts--><g class="link" data-entity-1="ent0010" data-entity-2="ent0023" data-link-type="dependency" data-source-line="106" id="lnk59"><path d="M1694.97,372.8679 C1695.11,397.3479 1691.01,435.5379 1666.58,454.7279 C1654.5,464.2279 1128.14,457.8179 1113.58,462.7279 C1076.96,475.1079 1046.6981,499.872 1025.4281,520.392" fill="none" id="UserSlice-to-SharedContracts" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="1021.11,524.5579,1030.3643,521.1878,1024.7084,521.0863,1024.8099,515.4304,1021.11,524.5579" style="stroke:#000000;stroke-width:1;"/></g><!--link UserSlice to SharedApp--><g class="link" data-entity-1="ent0010" data-entity-2="ent0024" data-link-type="dependency" data-source-line="107" id="lnk60"><path d="M1660.5,372.8979 C1636.63,387.8879 1603.02,406.4679 1570.58,415.7279 C1324.28,486.0579 1251.48,443.4779 995.58,454.7279 C973.6,455.6979 620.11,454.8179 599.58,462.7279 C566.89,475.3379 541.7239,499.6702 524.1239,520.1202" fill="none" id="UserSlice-to-SharedApp" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="520.21,524.6679,529.1126,520.4556,523.4716,520.8781,523.0491,515.2371,520.21,524.6679" style="stroke:#000000;stroke-width:1;"/></g><!--link LocationsSlice to Sql--><g class="link" data-entity-1="ent0012" data-entity-2="ent0029" data-link-type="dependency" data-source-line="109" id="lnk61"><path d="M2349.91,372.8379 C2342.47,397.2879 2326.91,435.4479 2298.58,454.7279 C2274.88,470.8579 2261.17,452.0179 2234.58,462.7279 C2199.38,476.9079 2169.7752,500.7635 2148.4252,520.6035" fill="none" id="LocationsSlice-to-Sql" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2144.03,524.6879,2153.3457,521.4915,2147.6927,521.2842,2147.8999,515.6312,2144.03,524.6879" style="stroke:#000000;stroke-width:1;"/></g><!--link EmailsSlice to Email--><g class="link" data-entity-1="ent0011" data-entity-2="ent0032" data-link-type="dependency" data-source-line="111" id="lnk62"><path d="M1112.99,372.5479 C1134.11,410.3279 1173.9122,481.5207 1195.1222,519.4607" fill="none" id="EmailsSlice-to-Email" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="1198.05,524.6979,1197.1498,514.8902,1195.6102,520.3335,1190.1669,518.7939,1198.05,524.6979" style="stroke:#000000;stroke-width:1;"/></g><!--link EmailsSlice to EmailTemplates--><g class="link" data-entity-1="ent0011" data-entity-2="ent0033" data-link-type="dependency" data-source-line="112" id="lnk63"><path d="M1126.67,372.6279 C1143.55,386.2979 1166.44,403.4579 1188.58,415.7279 C1238.68,443.4979 1257.5,436.8279 1308.58,462.7279 C1344.8,481.0979 1378.8082,503.1451 1405.1382,521.2751" fill="none" id="EmailsSlice-to-EmailTemplates" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="1410.08,524.6779,1404.9358,516.2792,1405.9618,521.8422,1400.3988,522.8682,1410.08,524.6779" style="stroke:#000000;stroke-width:1;"/></g><!--link EmailsSlice to SharedApp--><g class="link" data-entity-1="ent0011" data-entity-2="ent0024" data-link-type="dependency" data-source-line="113" id="lnk64"><path d="M1029.66,349.7079 C800.29,348.3479 89.35,350.1379 20.58,423.7279 C11.17,433.7979 11,444.8279 20.58,454.7279 C30.01,464.4779 251.25,460.2079 264.58,462.7279 C332.21,475.5079 400.8907,501.9714 448.1607,522.3614" fill="none" id="EmailsSlice-to-SharedApp" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="453.67,524.7379,446.9903,517.5003,449.0789,522.7575,443.8217,524.846,453.67,524.7379" style="stroke:#000000;stroke-width:1;"/></g><!--link EmailsSlice to SharedApp--><g class="link" data-entity-1="ent0011" data-entity-2="ent0024" data-link-type="dependency" data-source-line="117" id="lnk66"><path d="M1029.75,352.2179 C855.54,357.5279 417.1,375.5979 373.58,423.7279 C339.81,461.0879 395.4978,498.0965 444.6178,522.0665" fill="none" id="EmailsSlice-to-SharedApp-1" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#000000" points="450.01,524.6979,443.6759,517.156,445.5165,522.5051,440.1674,524.3457,450.01,524.6979" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="45.5381" x="447.9113" y="436.9217">handles</text><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="192.2007" x="374.58" y="451.8704">SendRegistrationEmailCommand</text></g><!--link Sql to Domain--><g class="link" data-entity-1="ent0029" data-entity-2="ent0035" data-link-type="dependency" data-source-line="119" id="lnk67"><path d="M2158.87,571.5779 C2180.71,584.5179 2208.96,600.8679 2234.58,614.4779 C2249.85,622.5879 2255.45,621.5079 2269.58,631.4779 C2295.05,649.4279 2316.0031,670.6019 2332.4131,688.8919" fill="none" id="Sql-to-Domain" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2336.42,693.3579,2333.3869,683.9877,2333.0809,689.6362,2327.4323,689.3302,2336.42,693.3579" style="stroke:#000000;stroke-width:1;"/></g><!--link AuthSlice to Tables--><g class="link" data-entity-1="ent0008" data-entity-2="ent0041" data-link-type="dependency" data-source-line="121" id="lnk68"><path d="M1509.5,372.6379 C1524.59,387.8179 1546.74,406.7379 1570.58,415.7279 C1609.7,430.4879 1717.08,418.5979 1758.58,423.7279 C1826.14,432.0979 1840.96,446.9079 1908.58,454.7279 C1941.93,458.5879 2179.88,449.1579 2210.58,462.7279 C2239.74,475.6179 2260.1856,499.4941 2274.2756,519.8541" fill="none" id="AuthSlice-to-Tables" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2277.69,524.7879,2275.8576,515.111,2274.8447,520.6764,2269.2793,519.6635,2277.69,524.7879" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="72.2617" x="1909.58" y="444.4217">Dapper SQL</text></g><!--link BrewerySlice to Tables--><g class="link" data-entity-1="ent0009" data-entity-2="ent0041" data-link-type="dependency" data-source-line="122" id="lnk69"><path d="M2187.64,372.6479 C2206.45,387.0379 2232.7,405.0079 2258.58,415.7279 C2275.33,422.6679 2287.09,409.7079 2298.58,423.7279 C2321.84,452.0979 2312.9441,491.9768 2303.1241,519.0768" fill="none" id="BrewerySlice-to-Tables" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2301.08,524.7179,2307.9069,517.619,2302.7834,520.017,2300.3854,514.8935,2301.08,524.7179" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="72.2617" x="2312.22" y="444.4217">Dapper SQL</text></g><!--link UserSlice to Tables--><g class="link" data-entity-1="ent0010" data-entity-2="ent0041" data-link-type="dependency" data-source-line="123" id="lnk70"><path d="M1726.09,372.7979 C1749.96,387.9479 1783.81,406.7579 1816.58,415.7279 C1853.92,425.9479 1952.11,419.4579 1990.58,423.7279 C2089.28,434.6979 2122.99,415.9379 2210.58,462.7279 C2238.16,477.4679 2258.2984,500.1867 2272.8184,519.7867" fill="none" id="UserSlice-to-Tables" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2276.39,524.6079,2274.2467,514.995,2273.4137,520.5902,2267.8185,519.7572,2276.39,524.6079" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="72.2617" x="2191.53" y="444.4217">Dapper SQL</text></g><!--link LocationsSlice to Tables--><g class="link" data-entity-1="ent0012" data-entity-2="ent0041" data-link-type="dependency" data-source-line="124" id="lnk71"><path d="M2382.28,372.7679 C2400.86,387.2079 2426.83,405.1879 2452.58,415.7279 C2470.57,423.0979 2483.16,408.7779 2495.58,423.7279 C2504.39,434.3279 2504.79,444.4779 2495.58,454.7279 C2484.53,467.0379 2437.17,457.1879 2421.58,462.7279 C2382.29,476.7079 2347.6107,500.9466 2322.6607,520.8466" fill="none" id="LocationsSlice-to-Tables" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2317.97,524.5879,2327.5003,522.1031,2321.8789,521.4701,2322.5119,515.8488,2317.97,524.5879" style="stroke:#000000;stroke-width:1;"/><text fill="#000000" font-family="Arial" font-size="13" lengthAdjust="spacing" textLength="72.2617" x="2503.34" y="444.4217">Dapper SQL</text></g><!--link AuthSlice to Domain--><g class="link" data-entity-1="ent0008" data-entity-2="ent0035" data-link-type="dependency" data-source-line="126" id="lnk72"><path d="M1469.75,372.8779 C1454.7,388.1779 1432.57,407.1279 1408.58,415.7279 C1364.18,431.6479 606.35,417.5979 559.58,423.7279 C490.43,432.7879 450.33,407.6279 407.58,462.7279 C366.24,516.0179 360.11,566.5679 407.58,614.4779 C443.99,651.2179 2221.79,611.6879 2269.58,631.4779 C2299.92,644.0379 2321.8176,668.0973 2336.9876,688.5673" fill="none" id="AuthSlice-to-Domain" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2340.56,693.3879,2338.415,683.7754,2337.583,689.3707,2331.9876,688.5386,2340.56,693.3879" style="stroke:#000000;stroke-width:1;"/></g><!--link BrewerySlice to Domain--><g class="link" data-entity-1="ent0009" data-entity-2="ent0035" data-link-type="dependency" data-source-line="127" id="lnk73"><path d="M2185.99,372.8479 C2204.67,387.9279 2231.44,406.6179 2258.58,415.7279 C2287.07,425.2879 2363.68,420.7679 2393.58,423.7279 C2409.21,425.2779 2663.27,450.8879 2673.58,462.7279 C2717.86,513.5979 2719.49,565.0779 2673.58,614.4779 C2638.39,652.3379 2488.69,610.1879 2441.58,631.4779 C2411.85,644.9079 2390.0552,668.5344 2374.7252,688.6644" fill="none" id="BrewerySlice-to-Domain" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2371.09,693.4379,2379.7251,688.7012,2374.1193,689.46,2373.3605,683.8543,2371.09,693.4379" style="stroke:#000000;stroke-width:1;"/></g><!--link UserSlice to Domain--><g class="link" data-entity-1="ent0010" data-entity-2="ent0035" data-link-type="dependency" data-source-line="128" id="lnk74"><path d="M1725.51,372.7479 C1749.37,388.0979 1783.45,407.1679 1816.58,415.7279 C1865.64,428.4179 2223.43,411.3979 2272.58,423.7279 C2277.18,424.8879 2346.05,458.7179 2348.58,462.7279 C2394.74,536.0079 2375.4858,640.5847 2363.4758,687.5747" fill="none" id="UserSlice-to-Domain" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2361.99,693.3879,2368.0941,685.6587,2363.2281,688.5436,2360.3432,683.6776,2361.99,693.3879" style="stroke:#000000;stroke-width:1;"/></g><!--link LocationsSlice to Domain--><g class="link" data-entity-1="ent0012" data-entity-2="ent0035" data-link-type="dependency" data-source-line="129" id="lnk75"><path d="M2380.7,372.8379 C2399.17,387.9079 2425.66,406.6079 2452.58,415.7279 C2480.41,425.1679 2555.55,419.1479 2584.58,423.7279 C2653.05,434.5479 2693.14,407.9279 2735.58,462.7279 C2779.33,519.2079 2785.41,572.2779 2735.58,623.4779 C2724.19,635.1879 2456.62,625.0879 2441.58,631.4779 C2411.35,644.3079 2389.4483,668.3291 2374.2483,688.6991" fill="none" id="LocationsSlice-to-Domain" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2370.66,693.5079,2379.2483,688.6869,2373.6502,689.5005,2372.8366,683.9025,2370.66,693.5079" style="stroke:#000000;stroke-width:1;"/></g><!--link AuthSlice to DomainExceptions--><g class="link" data-entity-1="ent0008" data-entity-2="ent0036" data-link-type="dependency" data-source-line="130" id="lnk76"><path d="M1469.76,372.8879 C1454.71,388.1979 1432.57,407.1579 1408.58,415.7279 C1394.67,420.7079 354.99,413.2379 344.58,423.7279 C282.04,486.7379 283.41,559.1379 344.58,623.4779 C405.55,687.5979 1739.1406,709.9954 2084.7906,714.7454" fill="none" id="AuthSlice-to-DomainExceptions" style="stroke:#000000;stroke-width:1;"/><polygon fill="#000000" points="2090.79,714.8279,2081.8458,710.7046,2085.7905,714.7591,2081.7359,718.7038,2090.79,714.8279" style="stroke:#000000;stroke-width:1;"/></g><!--link GMN77 to Sql--><g class="link" data-entity-1="ent0078" data-entity-2="ent0029" data-link-type="association" data-source-line="134" id="lnk79"><path d="M2511.79,512.8279 C2482.77,486.8579 2439.38,453.6579 2394.08,439.2279 C2333.95,420.0679 2311.42,417.7279 2252.08,439.2279 C2205.2,456.2179 2163.34,498.2279 2140.31,524.8079" fill="none" id="GMN77-Sql" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/></g><!--link AuthSlice to GMN80--><g class="link" data-entity-1="ent0008" data-entity-2="ent0081" data-link-type="association" data-source-line="141" id="lnk82"><path d="M1469.76,372.8979 C1454.71,388.1979 1432.58,407.1579 1408.58,415.7279 C1350.35,436.5379 353.06,400.9379 295.58,423.7279 C270.11,433.8279 217.82,487.1979 186.94,520.3479" fill="none" id="AuthSlice-GMN80" style="stroke:#000000;stroke-width:1;stroke-dasharray:7,7;"/></g><?plantuml-src bLTDSzis4BtpLsoJGwH6KZfDCzCcCvcHRQbs9auKIqqETGyG2OceAO018CkQJlzx7m2IWYWwdVWaxXTs5mzlrp_Od6yubGKJCZ5tGfPCimqjMNgtrcehitDLA4rFf_uliiZuYcqBErNIVc28CDQ25P6-H0IsvdExBpXfdbecrmLF4Yii18kSqvdWUisqvPB6PKb3-ior5IahQ5w8r4LCSs7XkTNmg-BHa_7iQdIkDAUUpOMaN1dRVq9F9o-dFqylw9-4wC_Qv5QEvxFHXyc2lCCNlYIelCjVn8pxWBbK5XcATMv9hIeP8PsPLcjKCaeDxOGMSarA5ljAUSqpmUmDFQTnbY5zPCoiK7A6Whm9KSarJJbpwPlHZpZFyCso2J3ViVMQwnSVIovnve1opWgRKvhpzCvKHk--B8Xj8Ts8B2luZg4JGVDxePRe57z8UUdEfPKeBDVGSfdvaf9_ewPLUOJE6hHh-dBoQlegjAj9SeoZGcFmmviUgSyqtt4jkF4suMj_QlQ7uVgQIPoxuJ8qsebE3ISR9eeGBFmyDNclGbk3LVDL6xQkBigQQCBId8n7ajf9Gy8Qz-DrrS1p05Uhei0bzIuNYvdhMwc4jARVM6msJ6RcnQUjhnftVOblUDJNyvaL8cDMDO8RNYeZ8DXJxuALG40_lf5smiTL4trGb1xI6fAGz-gEPmVStGlM82-jqYE3BdljIgiDAJne7Uestphxf9Vjuvdth8_p1dbLFbBZ3wWKFRcgunnAzwQPS6--sBjhOu5UwE8AkUfx73UgphDWXhKm02wgNEwfUTlC4YkADzxCLKyNpB8bCtmqvp3DE5uDIWVkXIMhChP_3Fdp78ycSuW_d_moEG-83yAHnm1BRK1N43Qoo2vwtv4bf2TTZJKHGk1undDsBvIkoA5NNsCfIbu8oHlKQMNCCE2MEtmq5rrxpb7dZM-Rzf4zYYgm3YgZO0Dak7fMGczEBFkFDUnAhZH3xAqdNdhFzbm_yGFWvUIdqBvZaz7yQn7QyhNeqBxR1Gu0btLePw35dTBP9JDWyxMtdEtyPuUrB-1068zPZ1Py0yXP7f5B8pegza9j73Bh8YU_JbzDVWj51jLe8Z6nQiuBibXRys-iFmXFyD44S5VjM76SffYoTk0_pe4TF6OCql2DWIXM5V0gdufrPnXXW_YZVeOrYeJT3sXkKJ36sBsGAH-0q72IhWVG6UUw2eDVVaxN23COGXj6U745dZX_0DJUE2xG1thRSzj6K5OzKRovJ--1G7tlUkfxkV2HV8l2JsSlLUPk87xOjx9dkDiVq3DGaL5O5K2ZcEyENZ18acSWsI8mLovAaxY1FXgzFP0_lUuOoRVoUKES9cMoSOkdsFTvDZFj-zmYpeTZREA93XsGCGttayGpNaU9zP1qubuVZk1N5sBWhWh7lQY2zmyHJgkjWpIplHUP7y-8OuT61uVaaDbm-DRpmB74lVrZIVt6Mvuj2kxIEd9DZWgfppmLji8zOk0Z7gwp3jUIj69rQNsWLjFgQ4byyKuO6AnJUk2gjZewvvbVefN4Iub61XOOxX940g_fP5a8m8BQOSIwmQpLtvZJUDahp62mHDHoXu3cF5o-coRcLdvh_1pbs_OFsyF_HKZGB5zgHPKj30JwGBImNX6evlHQkuoExwlBekFYkinQ2LLfdkHGoO-FhOGT9rMQhjZHEC6bOsa2BlnKAVWgB1r-xYAt8IQziOunjSBscyHBy3E__SOKBepP-XrHIBU0k01-mQng7JeQ-NXpFV13Z0nyG0k8dFBC4K3IS7_8PgciTVldyHwEdJBw7wY2cniS60u7r657b7vnXURKl5ac3qV-1m00?></g></svg>