Split dbo schema into per-domain SQL schemas (#274)

This commit is contained in:
Aaron Po
2026-09-02 22:23:30 -04:00
committed by GitHub
parent 8d59f09cb2
commit d6e176bcd9
37 changed files with 275 additions and 227 deletions

View File

@@ -15,33 +15,51 @@ convention. For the vertical-slice code that talks to this schema, see
## Schema
The full schema lives under
`web/backend/Database/Database.Migrations/scripts/01-schema/` with one numbered
script per table.
The schema is split into six SQL Server schemas, one per feature area, each
living in its own numbered folder under
`web/backend/Database/Database.Migrations/scripts/`. The folder numbers
(`01`-`06`) fix the load order so foreign-key dependencies resolve — DbUp runs
embedded scripts in alphabetical-by-resource-name order, so e.g. `04-Brewery`
(which references `Auth`, `Geolocation`, and `Media`) runs after all three.
Each folder's `00-Schema.sql` creates its SQL schema before its tables run:
```
01-schema
├── 01-UserAccount.sql
├── 02-Photo.sql
├── 03-UserVerification.sql
├── 04-UserCredential.sql
├── 05-UserFollow.sql
├── 06-Country.sql
├── 07-StateProvince.sql
├── 08-City.sql
├── 09-BreweryPost.sql
├── 10-BreweryPostLocation.sql
├── 11-BreweryPostPhoto.sql
├── 12-BeerStyle.sql
├── 13-BeerPost.sql
├── 14-BeerPostPhoto.sql
├── 15-BeerPostComment.sql
├── 16-UserProfile.sql
└── 17-UserAvatar.sql
scripts
├── 01-Auth (UserAccount, UserVerification, UserCredential)
│ ├── 00-Schema.sql
│ ├── 01-UserAccount.sql
│ ├── 02-UserVerification.sql
│ └── 03-UserCredential.sql
├── 02-Geolocation (Country, StateProvince, City)
│ ├── 00-Schema.sql
│ ├── 01-Country.sql
│ ├── 02-StateProvince.sql
│ └── 03-City.sql
├── 03-Media (Photo)
│ ├── 00-Schema.sql
│ └── 01-Photo.sql
├── 04-Brewery (BreweryPost, BreweryPostLocation, BreweryPostPhoto)
│ ├── 00-Schema.sql
│ ├── 01-BreweryPost.sql
│ ├── 02-BreweryPostLocation.sql
│ └── 03-BreweryPostPhoto.sql
├── 05-Beer (BeerStyle, BeerPost, BeerPostPhoto, BeerPostComment)
│ ├── 00-Schema.sql
│ ├── 01-BeerStyle.sql
│ ├── 02-BeerPost.sql
│ ├── 03-BeerPostPhoto.sql
│ └── 04-BeerPostComment.sql
└── 06-Social (UserFollow, UserProfile, UserAvatar)
├── 00-Schema.sql
├── 01-UserFollow.sql
├── 02-UserProfile.sql
└── 03-UserAvatar.sql
```
Each script is run in order by `Database.Migrations` (DbUp) on startup. Each
table is queried directly by the owning feature slice's repository (see
Each script is run in order by `Database.Migrations` (DbUp) on startup. Tables
are referenced schema-qualified in code (e.g. `Auth.UserAccount`), not under
the default `dbo` schema. Each table is queried directly by the owning
feature slice's repository (see
[Direct SQL via Dapper/ADO.NET](architecture.md#direct-sql-via-dapperadonet)).
A generated DBML copy of the schema (for import into a diagramming tool such as

View File

@@ -1,39 +1,46 @@
# Database Schema
Five diagrams, one per schema category — mirrors the `identity` / `media` /
`location` / `brewery` / `beer` schema split. Every table also has a
Six diagrams, one per SQL Server schema — mirrors the `Auth` / `Geolocation` /
`Media` / `Brewery` / `Beer` / `Social` schema split under
`web/backend/Database/Database.Migrations/scripts/`. Every table also has a
`RowVersion ROWVERSION` column (optimistic concurrency), omitted below for
brevity. Tables marked `(*)` have no repository/feature slice yet.
Boxes with no attribute list (e.g. `Photo` in the identity diagram) are owned by
a different schema and shown only to keep the relationship visible; see the
Boxes with no attribute list (e.g. `Photo` in the auth diagram) are owned by a
different schema and shown only to keep the relationship visible; see the
diagram named after that schema for its full definition.
## Identity (`identity`)
## Auth (`Auth`)
```mermaid
--8<-- "web/diagrams/database-schema/identity.mmd"
--8<-- "web/diagrams/database-schema/auth.mmd"
```
## Media (`media`)
## Social (`Social`)
```mermaid
--8<-- "web/diagrams/database-schema/social.mmd"
```
## Media (`Media`)
```mermaid
--8<-- "web/diagrams/database-schema/media.mmd"
```
## Location (`location`)
## Geolocation (`Geolocation`)
```mermaid
--8<-- "web/diagrams/database-schema/location.mmd"
--8<-- "web/diagrams/database-schema/geolocation.mmd"
```
## Brewery (`brewery`)
## Brewery (`Brewery`)
```mermaid
--8<-- "web/diagrams/database-schema/brewery.mmd"
```
## Beer (`beer`)
## Beer (`Beer`)
```mermaid
--8<-- "web/diagrams/database-schema/beer.mmd"

View File

@@ -6,12 +6,11 @@ config:
erDiagram
User ||--o{ Cred : "has credentials"
User ||--o| Verify : "has verification"
%% Cross-schema: defined in full in social.mmd / media.mmd / brewery.mmd / beer.mmd
User ||--o| Profile : "has profile"
User ||--o{ Follow : "follows"
User ||--o{ Follow : "followed by"
%% Cross-schema: defined in full in media.mmd / brewery.mmd / beer.mmd
Profile ||--o{ Avatar : "has avatar history"
User ||--o{ Photo : "uploads"
User ||--o{ Brewery : "posts"
User ||--o{ Beer : "posts"
@@ -43,16 +42,3 @@ erDiagram
uniqueidentifier UserAccountID "FK, UK"
datetime VerificationDateTime
}
Profile {
uniqueidentifier UserProfileID PK
uniqueidentifier UserAccountID "FK, UK"
nvarchar Biography
}
Follow {
uniqueidentifier UserFollowID PK
uniqueidentifier UserAccountID FK
uniqueidentifier FollowingID FK
datetime CreatedAt
}

View File

@@ -8,10 +8,11 @@ erDiagram
Beer ||--o{ BeerPhoto : "has photos"
Beer ||--o{ BeerComment : "has comments"
%% Cross-schema: defined in full in identity.mmd / brewery.mmd / media.mmd
%% Cross-schema: defined in full in auth.mmd / brewery.mmd / media.mmd
User ||--o{ Beer : "posts"
Brewery ||--o{ Beer : "brews"
User ||--o{ BeerComment : "comments"
BeerPhoto }o--|| Photo : "uses photo"
BeerStyle {
uniqueidentifier BeerStyleID PK
@@ -41,3 +42,10 @@ erDiagram
datetime CreatedAt
datetime UpdatedAt "nullable"
}
BeerPhoto {
uniqueidentifier BeerPostPhotoID PK
uniqueidentifier BeerPostID FK
uniqueidentifier PhotoID FK
datetime LinkedAt
}

View File

@@ -9,8 +9,10 @@ erDiagram
Brewery ||--o{ BreweryPhoto : "has photos"
Brewery ||--o{ Beer : "brews"
%% Cross-schema: defined in full in identity.mmd / location.mmd / media.mmd / beer.mmd
%% Cross-schema: defined in full in auth.mmd / geolocation.mmd / media.mmd / beer.mmd
User ||--o{ Brewery : "posts"
BreweryLocation }o--|| City : "located in"
BreweryPhoto }o--|| Photo : "uses photo"
Brewery {
uniqueidentifier BreweryPostID PK
@@ -20,3 +22,20 @@ erDiagram
datetime CreatedAt
datetime UpdatedAt "nullable"
}
BreweryLocation {
uniqueidentifier BreweryPostLocationID PK
uniqueidentifier BreweryPostID "FK, UK"
nvarchar AddressLine1
nvarchar AddressLine2 "nullable"
nvarchar PostalCode
uniqueidentifier CityID FK
geography Coordinates "nullable"
}
BreweryPhoto {
uniqueidentifier BreweryPostPhotoID PK
uniqueidentifier BreweryPostID FK
uniqueidentifier PhotoID FK
datetime LinkedAt
}

View File

@@ -1,10 +1,9 @@
erDiagram
Country ||--o{ StateProvince : "has"
StateProvince ||--o{ City : "has"
City ||--o{ BreweryLocation : "located in"
%% Cross-schema: defined in full in brewery.mmd
Brewery ||--o| BreweryLocation : "has location"
City ||--o{ BreweryLocation : "located in"
Country {
uniqueidentifier CountryID PK
@@ -24,13 +23,3 @@ erDiagram
nvarchar CityName
uniqueidentifier StateProvinceID FK
}
BreweryLocation {
uniqueidentifier BreweryPostLocationID PK
uniqueidentifier BreweryPostID "FK, UK"
nvarchar AddressLine1
nvarchar AddressLine2 "nullable"
nvarchar PostalCode
uniqueidentifier CityID FK
geography Coordinates "nullable"
}

View File

@@ -4,41 +4,16 @@ config:
---
erDiagram
User ||--o{ Photo : "uploads"
%% Cross-schema: defined in full in auth.mmd / social.mmd / brewery.mmd / beer.mmd
Avatar }o--|| Photo : "uses photo"
Photo ||--o{ BreweryPhoto : "used in"
Photo ||--o{ BeerPhoto : "used in"
%% Cross-schema: defined in full in identity.mmd / brewery.mmd / beer.mmd
Profile ||--o{ Avatar : "has avatar history"
User ||--o{ Photo : "uploads"
Brewery ||--o{ BreweryPhoto : "has photos"
Beer ||--o{ BeerPhoto : "has photos"
Photo {
uniqueidentifier PhotoID PK
nvarchar Hyperlink "nullable"
uniqueidentifier UploadedByID FK
datetime UploadedAt
}
Avatar {
uniqueidentifier UserAvatarID PK
uniqueidentifier UserProfileID FK
uniqueidentifier PhotoID FK
datetime2 ValidFrom
datetime2 ValidTo "nullable; at most one row per UserProfileID has ValidTo IS NULL (active avatar)"
}
BreweryPhoto {
uniqueidentifier BreweryPostPhotoID PK
uniqueidentifier BreweryPostID FK
uniqueidentifier PhotoID FK
datetime LinkedAt
}
BeerPhoto {
uniqueidentifier BeerPostPhotoID PK
uniqueidentifier BeerPostID FK
uniqueidentifier PhotoID FK
datetime LinkedAt
}

View File

@@ -0,0 +1,34 @@
---
config:
layout: elk
---
erDiagram
User ||--o| Profile : "has profile"
User ||--o{ Follow : "follows"
User ||--o{ Follow : "followed by"
Profile ||--o{ Avatar : "has avatar history"
%% Cross-schema: defined in full in auth.mmd / media.mmd
Avatar }o--|| Photo : "uses photo"
Profile {
uniqueidentifier UserProfileID PK
uniqueidentifier UserAccountID "FK, UK"
nvarchar Biography
}
Follow {
uniqueidentifier UserFollowID PK
uniqueidentifier UserAccountID FK
uniqueidentifier FollowingID FK
datetime CreatedAt
}
Avatar {
uniqueidentifier UserAvatarID PK
uniqueidentifier UserProfileID FK
uniqueidentifier PhotoID FK
datetime2 ValidFrom
datetime2 ValidTo "nullable; at most one row per UserProfileID has ValidTo IS NULL (active avatar)"
}

View File

@@ -0,0 +1,2 @@
IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'Auth')
EXEC('CREATE SCHEMA Auth');

View File

@@ -1,4 +1,4 @@
CREATE TABLE dbo.UserAccount
CREATE TABLE Auth.UserAccount
(
UserAccountID UNIQUEIDENTIFIER
CONSTRAINT DF_UserAccountID DEFAULT NEWID(),

View File

@@ -1,4 +1,4 @@
CREATE TABLE UserVerification -- delete verification data when user account is deleted
CREATE TABLE Auth.UserVerification -- delete verification data when user account is deleted
(
UserVerificationID UNIQUEIDENTIFIER
CONSTRAINT DF_UserVerificationID DEFAULT NEWID(),
@@ -15,7 +15,7 @@ CREATE TABLE UserVerification -- delete verification data when user account is d
CONSTRAINT FK_UserVerification_UserAccount
FOREIGN KEY (UserAccountID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE CASCADE,
CONSTRAINT AK_UserVerification_UserAccountID
@@ -24,4 +24,4 @@ CREATE TABLE UserVerification -- delete verification data when user account is d
CREATE
NONCLUSTERED INDEX IX_UserVerification_UserAccount
ON UserVerification(UserAccountID);
ON Auth.UserVerification(UserAccountID);

View File

@@ -1,4 +1,4 @@
CREATE TABLE UserCredential -- delete credentials when user account is deleted
CREATE TABLE Auth.UserCredential -- delete credentials when user account is deleted
(
UserCredentialID UNIQUEIDENTIFIER
CONSTRAINT DF_UserCredentialID DEFAULT NEWID(),
@@ -26,15 +26,15 @@ CREATE TABLE UserCredential -- delete credentials when user account is deleted
CONSTRAINT FK_UserCredential_UserAccount
FOREIGN KEY (UserAccountID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE CASCADE
);
CREATE
NONCLUSTERED INDEX IX_UserCredential_UserAccount
ON UserCredential(UserAccountID);
ON Auth.UserCredential(UserAccountID);
CREATE
NONCLUSTERED INDEX IX_UserCredential_Account_Active
ON UserCredential(UserAccountID, IsRevoked, Expiry)
ON Auth.UserCredential(UserAccountID, IsRevoked, Expiry)
INCLUDE (Hash);

View File

@@ -0,0 +1,2 @@
IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'Geolocation')
EXEC('CREATE SCHEMA Geolocation');

View File

@@ -1,4 +1,4 @@
CREATE TABLE Country
CREATE TABLE Geolocation.Country
(
CountryID UNIQUEIDENTIFIER
CONSTRAINT DF_CountryID DEFAULT NEWID(),

View File

@@ -1,4 +1,4 @@
CREATE TABLE StateProvince
CREATE TABLE Geolocation.StateProvince
(
StateProvinceID UNIQUEIDENTIFIER
CONSTRAINT DF_StateProvinceID DEFAULT NEWID(),
@@ -20,9 +20,9 @@ CREATE TABLE StateProvince
CONSTRAINT FK_StateProvince_Country
FOREIGN KEY (CountryID)
REFERENCES Country (CountryID)
REFERENCES Geolocation.Country (CountryID)
);
CREATE
NONCLUSTERED INDEX IX_StateProvince_Country
ON StateProvince(CountryID);
ON Geolocation.StateProvince(CountryID);

View File

@@ -1,4 +1,4 @@
CREATE TABLE City
CREATE TABLE Geolocation.City
(
CityID UNIQUEIDENTIFIER
CONSTRAINT DF_CityID DEFAULT NEWID(),
@@ -14,9 +14,9 @@ CREATE TABLE City
CONSTRAINT FK_City_StateProvince
FOREIGN KEY (StateProvinceID)
REFERENCES StateProvince (StateProvinceID)
REFERENCES Geolocation.StateProvince (StateProvinceID)
);
CREATE
NONCLUSTERED INDEX IX_City_StateProvince
ON City(StateProvinceID);
ON Geolocation.City(StateProvinceID);

View File

@@ -0,0 +1,2 @@
IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'Media')
EXEC('CREATE SCHEMA Media');

View File

@@ -1,4 +1,4 @@
CREATE TABLE Photo -- All photos must be linked to a user account, you cannot delete a user account if they have uploaded photos
CREATE TABLE Media.Photo -- All photos must be linked to a user account, you cannot delete a user account if they have uploaded photos
(
PhotoID UNIQUEIDENTIFIER
CONSTRAINT DF_PhotoID DEFAULT NEWID(),
@@ -18,10 +18,10 @@ CREATE TABLE Photo -- All photos must be linked to a user account, you cannot de
CONSTRAINT FK_Photo_UploadedBy
FOREIGN KEY (UploadedByID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE NO ACTION
);
CREATE
NONCLUSTERED INDEX IX_Photo_UploadedByID
ON Photo(UploadedByID);
ON Media.Photo(UploadedByID);

View File

@@ -0,0 +1,2 @@
IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'Brewery')
EXEC('CREATE SCHEMA Brewery');

View File

@@ -1,4 +1,4 @@
CREATE TABLE BreweryPost -- A user cannot be deleted if they have a post
CREATE TABLE Brewery.BreweryPost -- A user cannot be deleted if they have a post
(
BreweryPostID UNIQUEIDENTIFIER
CONSTRAINT DF_BreweryPostID DEFAULT NEWID(),
@@ -21,10 +21,10 @@ CREATE TABLE BreweryPost -- A user cannot be deleted if they have a post
CONSTRAINT FK_BreweryPost_UserAccount
FOREIGN KEY (PostedByID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE NO ACTION
);
CREATE
NONCLUSTERED INDEX IX_BreweryPost_PostedByID
ON BreweryPost(PostedByID);
ON Brewery.BreweryPost(PostedByID);

View File

@@ -1,4 +1,4 @@
CREATE TABLE BreweryPostLocation
CREATE TABLE Brewery.BreweryPostLocation
(
BreweryPostLocationID UNIQUEIDENTIFIER
CONSTRAINT DF_BreweryPostLocationID DEFAULT NEWID(),
@@ -25,24 +25,24 @@ CREATE TABLE BreweryPostLocation
CONSTRAINT FK_BreweryPostLocation_BreweryPost
FOREIGN KEY (BreweryPostID)
REFERENCES BreweryPost (BreweryPostID)
REFERENCES Brewery.BreweryPost (BreweryPostID)
ON DELETE CASCADE,
CONSTRAINT FK_BreweryPostLocation_City
FOREIGN KEY (CityID)
REFERENCES City (CityID)
REFERENCES Geolocation.City (CityID)
);
CREATE
NONCLUSTERED INDEX IX_BreweryPostLocation_BreweryPost
ON BreweryPostLocation(BreweryPostID);
ON Brewery.BreweryPostLocation(BreweryPostID);
CREATE
NONCLUSTERED INDEX IX_BreweryPostLocation_City
ON BreweryPostLocation(CityID);
ON Brewery.BreweryPostLocation(CityID);
CREATE SPATIAL INDEX SIDX_BreweryPostLocation_Coordinates
ON BreweryPostLocation(Coordinates)
ON Brewery.BreweryPostLocation(Coordinates)
USING GEOGRAPHY_GRID
WITH (
GRIDS = (LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM),

View File

@@ -1,4 +1,4 @@
CREATE TABLE BreweryPostPhoto -- All photos linked to a post are deleted if the post is deleted
CREATE TABLE Brewery.BreweryPostPhoto -- All photos linked to a post are deleted if the post is deleted
(
BreweryPostPhotoID UNIQUEIDENTIFIER
CONSTRAINT DF_BreweryPostPhotoID DEFAULT NEWID(),
@@ -17,19 +17,19 @@ CREATE TABLE BreweryPostPhoto -- All photos linked to a post are deleted if the
CONSTRAINT FK_BreweryPostPhoto_BreweryPost
FOREIGN KEY (BreweryPostID)
REFERENCES BreweryPost (BreweryPostID)
REFERENCES Brewery.BreweryPost (BreweryPostID)
ON DELETE CASCADE,
CONSTRAINT FK_BreweryPostPhoto_Photo
FOREIGN KEY (PhotoID)
REFERENCES Photo (PhotoID)
REFERENCES Media.Photo (PhotoID)
ON DELETE CASCADE
);
CREATE
NONCLUSTERED INDEX IX_BreweryPostPhoto_Photo_BreweryPost
ON BreweryPostPhoto(PhotoID, BreweryPostID);
ON Brewery.BreweryPostPhoto(PhotoID, BreweryPostID);
CREATE
NONCLUSTERED INDEX IX_BreweryPostPhoto_BreweryPost_Photo
ON BreweryPostPhoto(BreweryPostID, PhotoID);
ON Brewery.BreweryPostPhoto(BreweryPostID, PhotoID);

View File

@@ -0,0 +1,2 @@
IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'Beer')
EXEC('CREATE SCHEMA Beer');

View File

@@ -1,4 +1,4 @@
CREATE TABLE BeerStyle
CREATE TABLE Beer.BeerStyle
(
BeerStyleID UNIQUEIDENTIFIER
CONSTRAINT DF_BeerStyleID DEFAULT NEWID(),

View File

@@ -1,4 +1,4 @@
CREATE TABLE BeerPost
CREATE TABLE Beer.BeerPost
(
BeerPostID UNIQUEIDENTIFIER
CONSTRAINT DF_BeerPostID DEFAULT NEWID(),
@@ -31,16 +31,16 @@ CREATE TABLE BeerPost
CONSTRAINT FK_BeerPost_PostedBy
FOREIGN KEY (PostedByID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE NO ACTION,
CONSTRAINT FK_BeerPost_BeerStyle
FOREIGN KEY (BeerStyleID)
REFERENCES BeerStyle (BeerStyleID),
REFERENCES Beer.BeerStyle (BeerStyleID),
CONSTRAINT FK_BeerPost_Brewery
FOREIGN KEY (BrewedByID)
REFERENCES BreweryPost (BreweryPostID),
REFERENCES Brewery.BreweryPost (BreweryPostID),
CONSTRAINT CHK_BeerPost_ABV
CHECK (ABV >= 0 AND ABV <= 67),
@@ -51,12 +51,12 @@ CREATE TABLE BeerPost
CREATE
NONCLUSTERED INDEX IX_BeerPost_PostedBy
ON BeerPost(PostedByID);
ON Beer.BeerPost(PostedByID);
CREATE
NONCLUSTERED INDEX IX_BeerPost_BeerStyle
ON BeerPost(BeerStyleID);
ON Beer.BeerPost(BeerStyleID);
CREATE
NONCLUSTERED INDEX IX_BeerPost_BrewedBy
ON BeerPost(BrewedByID);
ON Beer.BeerPost(BrewedByID);

View File

@@ -1,4 +1,4 @@
CREATE TABLE BeerPostPhoto -- All photos linked to a beer post are deleted if the post is deleted
CREATE TABLE Beer.BeerPostPhoto -- All photos linked to a beer post are deleted if the post is deleted
(
BeerPostPhotoID UNIQUEIDENTIFIER
CONSTRAINT DF_BeerPostPhotoID DEFAULT NEWID(),
@@ -17,19 +17,19 @@ CREATE TABLE BeerPostPhoto -- All photos linked to a beer post are deleted if th
CONSTRAINT FK_BeerPostPhoto_BeerPost
FOREIGN KEY (BeerPostID)
REFERENCES BeerPost (BeerPostID)
REFERENCES Beer.BeerPost (BeerPostID)
ON DELETE CASCADE,
CONSTRAINT FK_BeerPostPhoto_Photo
FOREIGN KEY (PhotoID)
REFERENCES Photo (PhotoID)
REFERENCES Media.Photo (PhotoID)
ON DELETE CASCADE
);
CREATE
NONCLUSTERED INDEX IX_BeerPostPhoto_Photo_BeerPost
ON BeerPostPhoto(PhotoID, BeerPostID);
ON Beer.BeerPostPhoto(PhotoID, BeerPostID);
CREATE
NONCLUSTERED INDEX IX_BeerPostPhoto_BeerPost_Photo
ON BeerPostPhoto(BeerPostID, PhotoID);
ON Beer.BeerPostPhoto(BeerPostID, PhotoID);

View File

@@ -1,4 +1,4 @@
CREATE TABLE BeerPostComment
CREATE TABLE Beer.BeerPostComment
(
BeerPostCommentID UNIQUEIDENTIFIER
CONSTRAINT DF_BeerPostComment DEFAULT NEWID(),
@@ -23,11 +23,11 @@ CREATE TABLE BeerPostComment
CONSTRAINT FK_BeerPostComment_BeerPost
FOREIGN KEY (BeerPostID)
REFERENCES BeerPost (BeerPostID),
REFERENCES Beer.BeerPost (BeerPostID),
CONSTRAINT FK_BeerPostComment_UserAccount
FOREIGN KEY (CommentedByID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE NO ACTION,
CONSTRAINT CHK_BeerPostComment_Rating
@@ -36,8 +36,8 @@ CREATE TABLE BeerPostComment
CREATE
NONCLUSTERED INDEX IX_BeerPostComment_BeerPost
ON BeerPostComment(BeerPostID);
ON Beer.BeerPostComment(BeerPostID);
CREATE
NONCLUSTERED INDEX IX_BeerPostComment_CommentedBy
ON BeerPostComment(CommentedByID);
ON Beer.BeerPostComment(CommentedByID);

View File

@@ -0,0 +1,2 @@
IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'Social')
EXEC('CREATE SCHEMA Social');

View File

@@ -1,4 +1,4 @@
CREATE TABLE UserFollow
CREATE TABLE Social.UserFollow
(
UserFollowID UNIQUEIDENTIFIER
CONSTRAINT DF_UserFollowID DEFAULT NEWID(),
@@ -17,12 +17,12 @@ CREATE TABLE UserFollow
CONSTRAINT FK_UserFollow_UserAccount
FOREIGN KEY (UserAccountID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE NO ACTION,
CONSTRAINT FK_UserFollow_UserAccountFollowing
FOREIGN KEY (FollowingID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE NO ACTION,
CONSTRAINT CK_CannotFollowOwnAccount
@@ -32,8 +32,8 @@ CREATE TABLE UserFollow
CREATE
NONCLUSTERED INDEX IX_UserFollow_UserAccount_FollowingID
ON UserFollow(UserAccountID, FollowingID);
ON Social.UserFollow(UserAccountID, FollowingID);
CREATE
NONCLUSTERED INDEX IX_UserFollow_FollowingID_UserAccount
ON UserFollow(FollowingID, UserAccountID);
ON Social.UserFollow(FollowingID, UserAccountID);

View File

@@ -1,4 +1,4 @@
CREATE TABLE dbo.UserProfile
CREATE TABLE Social.UserProfile
(
UserProfileID UNIQUEIDENTIFIER
CONSTRAINT DF_UserProfileID DEFAULT NEWID(),
@@ -17,6 +17,6 @@ CREATE TABLE dbo.UserProfile
CONSTRAINT FK_UserProfile_UserAccount
FOREIGN KEY (UserAccountID)
REFERENCES UserAccount (UserAccountID)
REFERENCES Auth.UserAccount (UserAccountID)
ON DELETE CASCADE
);

View File

@@ -1,4 +1,4 @@
CREATE TABLE dbo.UserAvatar
CREATE TABLE Social.UserAvatar
(
UserAvatarID UNIQUEIDENTIFIER
CONSTRAINT DF_UserAvatarID DEFAULT NEWID(),
@@ -18,12 +18,12 @@ CREATE TABLE dbo.UserAvatar
CONSTRAINT FK_UserAvatar_UserProfile
FOREIGN KEY (UserProfileID)
REFERENCES dbo.UserProfile (UserProfileID)
REFERENCES Social.UserProfile (UserProfileID)
ON DELETE CASCADE,
CONSTRAINT FK_UserAvatar_Photo
FOREIGN KEY (PhotoID)
REFERENCES dbo.Photo (PhotoID)
REFERENCES Media.Photo (PhotoID)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
@@ -33,11 +33,11 @@ CREATE TABLE dbo.UserAvatar
-- At most one active avatar per profile
CREATE UNIQUE NONCLUSTERED INDEX UX_UserAvatar_ActiveOne
ON dbo.UserAvatar (UserProfileID)
ON Social.UserAvatar (UserProfileID)
WHERE ValidTo IS NULL;
CREATE NONCLUSTERED INDEX IX_UserAvatar_Photo
ON dbo.UserAvatar (PhotoID);
ON Social.UserAvatar (PhotoID);
CREATE NONCLUSTERED INDEX ix_UserAvatar_Profile
ON dbo.UserAvatar (UserProfileID)
ON Social.UserAvatar (UserProfileID)

View File

@@ -62,11 +62,11 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
co.CountryID,
co.CountryName,
co.ISO3166_1 AS Iso31661
FROM dbo.BreweryPost bp
LEFT JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN dbo.City c ON bpl.CityID = c.CityID
LEFT JOIN dbo.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN dbo.Country co ON sp.CountryID = co.CountryID
FROM Brewery.BreweryPost bp
LEFT JOIN Brewery.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN Geolocation.City c ON bpl.CityID = c.CityID
LEFT JOIN Geolocation.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN Geolocation.Country co ON sp.CountryID = co.CountryID
WHERE bp.BreweryPostID = @BreweryPostId
""",
new { BreweryPostId = id },
@@ -85,7 +85,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
await using DbConnection connection = await CreateConnection();
return await connection.ExecuteScalarAsync<Guid?>(
new CommandDefinition(
"SELECT PostedByID FROM dbo.BreweryPost WHERE BreweryPostID = @BreweryPostId",
"SELECT PostedByID FROM Brewery.BreweryPost WHERE BreweryPostID = @BreweryPostId",
new { BreweryPostId = id },
cancellationToken: cancellationToken
)
@@ -126,11 +126,11 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
co.CountryID,
co.CountryName,
co.ISO3166_1 AS Iso31661
FROM dbo.BreweryPost bp
LEFT JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN dbo.City c ON bpl.CityID = c.CityID
LEFT JOIN dbo.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN dbo.Country co ON sp.CountryID = co.CountryID
FROM Brewery.BreweryPost bp
LEFT JOIN Brewery.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN Geolocation.City c ON bpl.CityID = c.CityID
LEFT JOIN Geolocation.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN Geolocation.Country co ON sp.CountryID = co.CountryID
ORDER BY bp.CreatedAt DESC
OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY
""",
@@ -173,11 +173,11 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
co.CountryName,
co.ISO3166_1 AS Iso31661,
bpl.Coordinates.STDistance(@Origin) AS DistanceMetres
FROM dbo.BreweryPost bp
INNER JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN dbo.City c ON bpl.CityID = c.CityID
LEFT JOIN dbo.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN dbo.Country co ON sp.CountryID = co.CountryID
FROM Brewery.BreweryPost bp
INNER JOIN Brewery.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN Geolocation.City c ON bpl.CityID = c.CityID
LEFT JOIN Geolocation.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN Geolocation.Country co ON sp.CountryID = co.CountryID
WHERE bpl.Coordinates IS NOT NULL
AND bpl.Coordinates.STDistance(@Origin) <= @RangeInMetres
ORDER BY bpl.Coordinates.STDistance(@Origin) ASC
@@ -223,11 +223,11 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
co.CountryID,
co.CountryName,
co.ISO3166_1 AS Iso31661
FROM dbo.BreweryPost bp
INNER JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN dbo.City c ON bpl.CityID = c.CityID
LEFT JOIN dbo.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN dbo.Country co ON sp.CountryID = co.CountryID
FROM Brewery.BreweryPost bp
INNER JOIN Brewery.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN Geolocation.City c ON bpl.CityID = c.CityID
LEFT JOIN Geolocation.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN Geolocation.Country co ON sp.CountryID = co.CountryID
WHERE bpl.Coordinates IS NOT NULL
ORDER BY bp.BreweryPostID ASC
""",
@@ -249,7 +249,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
new CommandDefinition(
"""
SELECT 1 FROM
dbo.BreweryPost
Brewery.BreweryPost
WHERE
BreweryPostID = @BreweryPostId
""",
@@ -268,7 +268,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
bool cityExists =
await connection.ExecuteScalarAsync<int?>(
new CommandDefinition(
"SELECT 1 FROM dbo.City WHERE CityID = @CityId",
"SELECT 1 FROM Geolocation.City WHERE CityID = @CityId",
new { brewery.Location.CityId },
transaction,
cancellationToken: cancellationToken
@@ -283,7 +283,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
int updatedRows = await connection.ExecuteAsync(
new CommandDefinition(
"""
UPDATE dbo.BreweryPost
UPDATE Brewery.BreweryPost
SET BreweryName = @BreweryName,
Description = @Description,
UpdatedAt = GETDATE()
@@ -315,7 +315,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
new CommandDefinition(
"""
DELETE FROM
dbo.BreweryPostLocation
Brewery.BreweryPostLocation
WHERE BreweryPostID = @BreweryPostId
""",
new { brewery.BreweryPostId },
@@ -331,7 +331,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
new CommandDefinition(
"""
SELECT 1
FROM dbo.BreweryPostLocation
FROM Brewery.BreweryPostLocation
WHERE BreweryPostID = @BreweryPostId
""",
new { brewery.BreweryPostId },
@@ -345,7 +345,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
UPDATE dbo.BreweryPostLocation
UPDATE Brewery.BreweryPostLocation
SET CityID = @CityId,
AddressLine1 = @AddressLine1,
AddressLine2 = @AddressLine2,
@@ -375,7 +375,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
INSERT INTO dbo.BreweryPostLocation
INSERT INTO Brewery.BreweryPostLocation
(BreweryPostLocationID,
BreweryPostID,
CityID,
@@ -434,7 +434,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
int rows = await connection.ExecuteAsync(
"""
DELETE FROM
dbo.BreweryPost
Brewery.BreweryPost
WHERE
BreweryPostID = @BreweryPostId
""",
@@ -462,7 +462,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
"""
SELECT 1
FROM
dbo.UserAccount
Auth.UserAccount
WHERE
UserAccountID = @PostedById
""",
@@ -481,7 +481,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
"""
SELECT 1
FROM
dbo.City
Geolocation.City
WHERE
CityID = @CityId
""",
@@ -497,7 +497,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
INSERT INTO dbo.BreweryPost
INSERT INTO Brewery.BreweryPost
(BreweryPostID, BreweryName, Description, PostedByID)
VALUES
(@BreweryPostId, @BreweryName, @Description, @PostedById)
@@ -516,7 +516,7 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
INSERT INTO dbo.BreweryPostLocation
INSERT INTO Brewery.BreweryPostLocation
(BreweryPostLocationID,
BreweryPostID,
CityID,

View File

@@ -46,7 +46,7 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteScalarAsync<int?>(
"""
SELECT 1
FROM dbo.City
FROM Geolocation.City
WHERE CityName = @CityName AND StateProvinceID = @StateProvinceId
""",
new { location.CityName, StateProvinceId = stateProvinceId }
@@ -56,7 +56,7 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
if (!cityExists)
await connection.ExecuteAsync(
"""
INSERT INTO dbo.City (StateProvinceID, CityName)
INSERT INTO Geolocation.City (StateProvinceID, CityName)
VALUES (@StateProvinceId, @CityName)
""",
new { StateProvinceId = stateProvinceId, location.CityName }
@@ -79,7 +79,7 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteScalarAsync<int?>(
"""
SELECT 1
FROM dbo.Country
FROM Geolocation.Country
WHERE ISO3166_1 = @ISO3166_1
""",
new { ISO3166_1 = isoCode }
@@ -91,7 +91,7 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
// race is tolerated (the resulting duplicate row, if any, is out of scope for this method).
await connection.ExecuteAsync(
"""
INSERT INTO dbo.Country (CountryName, ISO3166_1)
INSERT INTO Geolocation.Country (CountryName, ISO3166_1)
VALUES (@CountryName, @ISO3166_1)
""",
new { CountryName = countryName, ISO3166_1 = isoCode }
@@ -125,7 +125,7 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteScalarAsync<int?>(
"""
SELECT 1
FROM dbo.StateProvince
FROM Geolocation.StateProvince
WHERE ISO3166_2 = @ISO3166_2
""",
new { ISO3166_2 = isoCode }
@@ -135,7 +135,7 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
if (!exists)
await connection.ExecuteAsync(
"""
INSERT INTO dbo.StateProvince (StateProvinceName, ISO3166_2, CountryID)
INSERT INTO Geolocation.StateProvince (StateProvinceName, ISO3166_2, CountryID)
VALUES (@StateProvinceName, @ISO3166_2, @CountryId)
""",
new
@@ -153,7 +153,7 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
return await connection.ExecuteScalarAsync<Guid?>(
"""
SELECT CountryID
FROM dbo.Country
FROM Geolocation.Country
WHERE ISO3166_1 = @ISO3166_1
""",
new { ISO3166_1 = isoCode }
@@ -166,7 +166,7 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
return await connection.ExecuteScalarAsync<Guid?>(
"""
SELECT StateProvinceID
FROM dbo.StateProvince
FROM Geolocation.StateProvince
WHERE ISO3166_2 = @ISO3166_2
""",
new { ISO3166_2 = isoCode }
@@ -179,8 +179,8 @@ public class LocationRepository(ISqlConnectionFactory connectionFactory)
return await connection.ExecuteScalarAsync<Guid?>(
"""
SELECT c.CityID
FROM dbo.City c
INNER JOIN dbo.StateProvince sp ON sp.StateProvinceID = c.StateProvinceID
FROM Geolocation.City c
INNER JOIN Geolocation.StateProvince sp ON sp.StateProvinceID = c.StateProvinceID
WHERE c.CityName = @CityName AND sp.ISO3166_2 = @StateProvinceCode
""",
new { CityName = cityName, StateProvinceCode = stateProvinceIsoCode }

View File

@@ -28,7 +28,7 @@ public class PhotoUploadRepository(ISqlConnectionFactory connectionFactory)
"""
SELECT 1
FROM
dbo.UserAccount
Auth.UserAccount
WHERE
UserAccountID = @UploadedById
""",
@@ -44,7 +44,7 @@ public class PhotoUploadRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
INSERT INTO dbo.Photo (PhotoID, Hyperlink, UploadedByID)
INSERT INTO Media.Photo (PhotoID, Hyperlink, UploadedByID)
VALUES (@PhotoId, @Hyperlink, @UploadedById)
""",
new { photo.PhotoId, photo.Hyperlink, photo.UploadedById },

View File

@@ -8,8 +8,8 @@ namespace Features.Auth.Identity;
/// <summary>
/// Dapper-based implementation of the ASP.NET Core Identity store interfaces consumed by
/// <see cref="UserManager{TUser}" />, persisting against the existing <c>dbo.UserAccount</c>,
/// <c>dbo.UserCredential</c>, and <c>dbo.UserVerification</c> tables. No EF Core is used, and no schema
/// <see cref="UserManager{TUser}" />, persisting against the existing <c>Auth.UserAccount</c>,
/// <c>Auth.UserCredential</c>, and <c>Auth.UserVerification</c> tables. No EF Core is used, and no schema
/// changes were required: normalized name/email are computed on the fly rather than persisted (the
/// existing unique indexes are already case-insensitive under SQL Server's default collation).
/// </summary>
@@ -21,7 +21,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
{
private const string SelectUserSql = """
SELECT UserAccountID AS Id, Username AS UserName, FirstName, LastName, Email, DateOfBirth
FROM dbo.UserAccount
FROM Auth.UserAccount
""";
/// <remarks>No unmanaged resources to release; each method opens and disposes its own connection.</remarks>
@@ -87,7 +87,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
Guid userAccountId = await connection.ExecuteScalarAsync<Guid>(
new CommandDefinition(
"""
INSERT INTO dbo.UserAccount (Username, FirstName, LastName, DateOfBirth, Email)
INSERT INTO Auth.UserAccount (Username, FirstName, LastName, DateOfBirth, Email)
OUTPUT INSERTED.UserAccountID
VALUES (@UserName, @FirstName, @LastName, @DateOfBirth, @Email);
""",
@@ -107,7 +107,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
INSERT INTO dbo.UserCredential (UserAccountId, Hash)
INSERT INTO Auth.UserCredential (UserAccountId, Hash)
VALUES (@UserAccountId, @Hash);
""",
new { UserAccountId = userAccountId, Hash = user.PasswordHash },
@@ -141,7 +141,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
/// <inheritdoc />
/// <remarks>
/// Persists profile field changes and keeps <c>dbo.UserVerification</c> in sync with
/// Persists profile field changes and keeps <c>Auth.UserVerification</c> in sync with
/// <see cref="ApplicationUser.EmailConfirmed" />: a row is inserted (idempotently, matching the
/// previous <c>AuthRepository.ConfirmUserAccountAsync</c> behavior) when
/// <see langword="true" />, and removed when <see langword="false" /> -- notably after
@@ -157,7 +157,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
UPDATE dbo.UserAccount
UPDATE Auth.UserAccount
SET Username = @UserName, FirstName = @FirstName, LastName = @LastName,
Email = @Email, DateOfBirth = @DateOfBirth, UpdatedAt = GETDATE()
WHERE UserAccountID = @Id
@@ -185,9 +185,9 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
/// <inheritdoc />
/// <remarks>
/// A plain <c>DELETE</c> against <c>dbo.UserAccount</c>: its foreign keys from
/// <c>dbo.UserCredential</c>, <c>dbo.UserVerification</c>, and <c>dbo.UserProfile</c> cascade
/// (<c>dbo.UserAvatar</c> cascades transitively through <c>dbo.UserProfile</c>), so no manual
/// A plain <c>DELETE</c> against <c>Auth.UserAccount</c>: its foreign keys from
/// <c>Auth.UserCredential</c>, <c>Auth.UserVerification</c>, and <c>Social.UserProfile</c> cascade
/// (<c>Social.UserAvatar</c> cascades transitively through <c>Social.UserProfile</c>), so no manual
/// child cleanup is needed. Other tables (posts, comments, photos, follows) reference the
/// account with <c>ON DELETE NO ACTION</c> by design; deleting an account that still has any of
/// those fails with a foreign key violation, which is surfaced as a failed
@@ -204,7 +204,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
{
await connection.ExecuteAsync(
new CommandDefinition(
"DELETE FROM dbo.UserAccount WHERE UserAccountID = @Id",
"DELETE FROM Auth.UserAccount WHERE UserAccountID = @Id",
new { user.Id },
cancellationToken: cancellationToken
)
@@ -273,7 +273,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
/// <inheritdoc />
/// <remarks>
/// For an existing account (<see cref="ApplicationUser.Id" /> already assigned), rotates the
/// credential transactionally -- revoking the previous <c>dbo.UserCredential</c> row(s) and inserting
/// credential transactionally -- revoking the previous <c>Auth.UserCredential</c> row(s) and inserting
/// the new hash -- mirroring the previous <c>AuthRepository.RotateCredentialAsync</c> behavior. During
/// registration <see cref="UserManager{TUser}" /> calls this before the account has an ID (i.e. before
/// <see cref="CreateAsync" /> runs); in that case only the in-memory hash is set, and
@@ -301,7 +301,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
UPDATE dbo.UserCredential
UPDATE Auth.UserCredential
SET IsRevoked = 1, RevokedAt = GETDATE()
WHERE UserAccountId = @UserAccountId AND IsRevoked = 0
""",
@@ -314,7 +314,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
INSERT INTO dbo.UserCredential (UserAccountId, Hash)
INSERT INTO Auth.UserCredential (UserAccountId, Hash)
VALUES (@UserAccountId, @Hash);
""",
new { UserAccountId = user.Id, Hash = passwordHash },
@@ -423,7 +423,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
new CommandDefinition(
"""
SELECT Hash
FROM dbo.UserCredential
FROM Auth.UserCredential
WHERE UserAccountId = @UserAccountId AND IsRevoked = 0
""",
new { UserAccountId = user.Id },
@@ -435,7 +435,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
new CommandDefinition(
"""
SELECT TOP 1 1
FROM dbo.UserVerification
FROM Auth.UserVerification
WHERE UserAccountID = @UserAccountId
""",
new { UserAccountId = user.Id },
@@ -459,7 +459,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
new CommandDefinition(
"""
SELECT TOP 1 1
FROM dbo.UserVerification
FROM Auth.UserVerification
WHERE UserAccountID = @UserAccountId
""",
new { UserAccountId = userAccountId },
@@ -475,7 +475,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
INSERT INTO dbo.UserVerification (UserAccountId, VerificationDateTime)
INSERT INTO Auth.UserVerification (UserAccountId, VerificationDateTime)
VALUES (@UserAccountId, GETDATE())
""",
new { UserAccountId = userAccountId },
@@ -489,7 +489,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
}
}
/// <summary>Removes the <c>dbo.UserVerification</c> row for a user, if one exists.</summary>
/// <summary>Removes the <c>Auth.UserVerification</c> row for a user, if one exists.</summary>
private static Task RemoveVerificationRowAsync(
DbConnection connection,
Guid userAccountId,
@@ -497,7 +497,7 @@ public sealed class DapperUserStore(ISqlConnectionFactory connectionFactory)
) =>
connection.ExecuteAsync(
new CommandDefinition(
"DELETE FROM dbo.UserVerification WHERE UserAccountID = @UserAccountId",
"DELETE FROM Auth.UserVerification WHERE UserAccountID = @UserAccountId",
new { UserAccountId = userAccountId },
cancellationToken: cancellationToken
)

View File

@@ -22,7 +22,7 @@ public class UserListRepository(ISqlConnectionFactory connectionFactory)
UserAccountID,
Username,
FirstName, LastName, Email, CreatedAt, UpdatedAt, DateOfBirth, RowVersion
FROM dbo.UserAccount
FROM Auth.UserAccount
WHERE UserAccountID = @UserAccountId
""",
new { UserAccountId = id }
@@ -40,7 +40,7 @@ public class UserListRepository(ISqlConnectionFactory connectionFactory)
return await connection.QueryAsync<UserAccount>(
"""
SELECT UserAccountID, Username, FirstName, LastName, Email, CreatedAt, UpdatedAt, DateOfBirth, RowVersion
FROM dbo.UserAccount
FROM Auth.UserAccount
ORDER BY CreatedAt DESC
OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY
""",

View File

@@ -25,7 +25,7 @@ public class UserProfileRepository(ISqlConnectionFactory connectionFactory)
return await connection.ExecuteScalarAsync<Guid>(
new CommandDefinition(
"""
INSERT INTO dbo.UserProfile (UserAccountID, Biography)
INSERT INTO Social.UserProfile (UserAccountID, Biography)
OUTPUT INSERTED.UserProfileID
VALUES (@UserAccountId, @Biography)
""",
@@ -47,7 +47,7 @@ public class UserProfileRepository(ISqlConnectionFactory connectionFactory)
new CommandDefinition(
"""
SELECT UserProfileID
FROM dbo.UserProfile
FROM Social.UserProfile
WHERE UserAccountID = @UserAccountId
""",
new { UserAccountId = userAccountId },
@@ -73,7 +73,7 @@ public class UserProfileRepository(ISqlConnectionFactory connectionFactory)
int updatedRows = await connection.ExecuteAsync(
new CommandDefinition(
"""
UPDATE dbo.UserProfile
UPDATE Social.UserProfile
SET Biography = @Biography
WHERE UserAccountID = @UserAccountId
""",
@@ -101,7 +101,7 @@ public class UserProfileRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
UPDATE dbo.UserAvatar
UPDATE Social.UserAvatar
SET ValidTo = SYSUTCDATETIME()
WHERE UserProfileID = @UserProfileId AND ValidTo IS NULL
""",
@@ -114,7 +114,7 @@ public class UserProfileRepository(ISqlConnectionFactory connectionFactory)
await connection.ExecuteAsync(
new CommandDefinition(
"""
INSERT INTO dbo.UserAvatar (UserAvatarID, UserProfileID, PhotoID)
INSERT INTO Social.UserAvatar (UserAvatarID, UserProfileID, PhotoID)
VALUES (@UserAvatarId, @UserProfileId, @PhotoId)
""",
new