Modeles de donnees

Diagrammes entite-relation et description des schemas de base de donnees pour chaque module de Place API.

Modeles de donnees

Place API utilise PostgreSQL avec une strategie schema-per-module. Chaque module possede son propre DbContext et son propre schema de base de donnees, tout en partageant le meme serveur PostgreSQL.

Bases de donnees

L'application utilise plusieurs bases de donnees PostgreSQL distinctes :

Base de donneesDescriptionConnection string key
identityModule Identity (utilisateurs, auth)PostgresOptions:ConnectionString
auditModule Audit (journaux d'audit)ConnectionStrings:audit
messagingModule Messaging (messages envoyes)ConnectionStrings:messaging (via Aspire)
hangfireBackground jobs HangfireConnectionStrings:hangfire
persist-messageOutbox pattern / messages persistantsPersistMessageOptions:ConnectionString

Schema Identity

Le module Identity utilise IdentityDbContext qui herite de IdentityDbContext<User, Role, Guid> d'ASP.NET Identity. Les noms de tables sont convertis en snake_case.

mermaid
erDiagram
    User ||--o{ UserSession : "possede"
    User ||--o{ OtpRecord : "possede"
    User ||--o{ PasswordHistory : "possede"
    User ||--o{ PushToken : "possede"
    User ||--o{ TwoFactorChallenge : "possede"
    User ||--o{ UserSecuritySettings : "possede (1:1)"
    User }o--o{ Role : "via UserRole"
    User ||--o{ UserClaim : "possede"
    User ||--o{ UserLogin : "possede"
    User ||--o{ UserToken : "possede"
    User ||--o{ UserGroup : "appartient a"
    Group ||--o{ UserGroup : "contient"
    Group ||--o{ GroupRole : "associe a"
    Role ||--o{ GroupRole : "associe a"
    Role ||--o{ RoleClaim : "possede"

    User {
        Guid Id PK
        string Email
        string UserName
        string FirstName
        string LastName
        string PassPortNumber
        bool IsActive
        string ImageUrl
        DateTime LastPasswordChangeDate
        DateTime CreatedAt
        DateTime LastLoginAt
        long Version
        bool EmailConfirmed
        bool TwoFactorEnabled
        string SecurityStamp
        string PhoneNumber
    }

    Role {
        Guid Id PK
        string Name
        string NormalizedName
        string Description
        long Version
    }

    UserRole {
        Guid UserId FK
        Guid RoleId FK
        long Version
    }

    UserSession {
        Guid Id PK
        Guid UserId FK
        string RefreshTokenHash
        string PreviousRefreshTokenHash
        Guid TokenFamilyId
        string IpAddress
        string UserAgent
        string DeviceId
        string DeviceType
        string Browser
        string OperatingSystem
        DateTime CreatedAt
        DateTime LastActivityAt
        DateTime ExpiresAt
        DateTime RevokedAt
        string RevocationReason
    }

    OtpRecord {
        Guid Id PK
        Guid UserId FK
        string CodeHash
        string TokenFingerprint
        string Purpose
        DateTime ExpiresAt
        int Attempts
        bool IsUsed
        DateTime CreatedAt
    }

    PasswordHistory {
        Guid Id PK
        Guid UserId FK
        string PasswordHash
        DateTime CreatedAt
    }

    PushToken {
        Guid Id PK
        Guid UserId FK
        string Token
        string Platform
        string DeviceId
        DateTime CreatedAt
        DateTime UpdatedAt
    }

    TwoFactorChallenge {
        Guid Id PK
        Guid UserId FK
        string TokenFingerprint
        string TokenHash
        DateTime ExpiresAt
        int Attempts
        bool IsConsumed
        DateTime CreatedAt
    }

    UserSecuritySettings {
        Guid Id PK
        Guid UserId FK
        bool LoginNotificationsEnabled
        bool SessionAlertEnabled
    }

    UserClaim {
        int Id PK
        Guid UserId FK
        string ClaimType
        string ClaimValue
        long Version
    }

    UserLogin {
        string LoginProvider PK
        string ProviderKey PK
        Guid UserId FK
        string ProviderEmail
        DateTime LinkedAt
        long Version
    }

    UserToken {
        Guid UserId PK
        string LoginProvider PK
        string Name PK
        string Value
        long Version
    }

    Group {
        Guid Id PK
        string Name UK
        string Description
        DateTime CreatedAt
    }

    GroupRole {
        Guid Id PK
        Guid GroupId FK
        Guid RoleId FK
    }

    UserGroup {
        Guid Id PK
        Guid UserId FK
        Guid GroupId FK
        DateTime JoinedAt
    }

    IdempotencyRecord {
        Guid Id PK
        string Key
        string RequestIdentity
        string RequestBodyHash
        int StatusCode
        string ResponseBody
        DateTime CreatedAt
        DateTime ExpiresAt
    }

Tables Identity (snake_case dans PostgreSQL)

Table PostgreSQLEntite C#Description
userUserUtilisateurs (herite de IdentityUser<Guid>)
roleRoleRoles applicatifs (herite de IdentityRole<Guid>)
user_roleUserRoleTable de jointure User-Role
user_sessionUserSessionSessions actives avec refresh token hashe
otp_recordOtpRecordCodes OTP pour verification email et reset password
password_historyPasswordHistoryHistorique des mots de passe (eviter reutilisation)
push_tokenPushTokenTokens push notification (FCM, APNs)
two_factor_challengeTwoFactorChallengeChallenges 2FA en cours (TOTP)
user_security_settingsUserSecuritySettingsPreferences de securite par utilisateur
user_claimUserClaimClaims utilisateur (ASP.NET Identity)
user_loginUserLoginLogins externes (Google, Apple, Facebook)
user_tokenUserTokenTokens ASP.NET Identity
groupGroupGroupes d'utilisateurs
group_roleGroupRoleAssociation groupe-role
user_groupUserGroupAssociation utilisateur-groupe
idempotency_recordIdempotencyRecordCache d'idempotence pour les requetes

Index notables

  • user_session : index unique sur RefreshTokenHash, index composite sur (UserId, RevokedAt, ExpiresAt) pour recherche de sessions actives
  • otp_record : FK vers User
  • group : index unique sur Name
  • group_role : index unique sur (GroupId, RoleId)
  • user_group : index unique sur (UserId, GroupId)
  • user_security_settings : index unique sur UserId

Concurrency control

L'entite User utilise le champ Version comme concurrency token via EF Core. Toutes les entites implementant IVersion voient leur version incrementee automatiquement lors de la sauvegarde.


Schema Audit

Le module Audit utilise un AuditContext avec une seule table audit_log. Les noms de colonnes sont en snake_case.

mermaid
erDiagram
    AuditLog {
        Guid Id PK
        DateTime OccurredAtUtc
        DateTime ReceivedAtUtc
        string ModuleName
        string ActionName
        string EventType
        string EntityType
        string EntityId
        Guid ActorUserId
        string ActorType
        string Result
        string Reason
        string Severity
        string TenantId
        string IpAddress
        string UserAgent
        string CorrelationId
        string RequestId
        string TraceId
        string SpanId
        text_array Tags
        jsonb MetadataJson
        string TokenFingerprintSha256
        string Source
        jsonb PayloadJson
        int Version
    }

Colonnes et types speciaux

ColonneType PostgreSQLDescription
payload_jsonjsonbDonnees de l'evenement (corps de requete)
metadata_jsonjsonbMetadonnees additionnelles
tagstext[]Tags de categorisation (array PostgreSQL)

Index

La table audit_log possede de nombreux index pour supporter les requetes de filtrage et de recherche :

  • OccurredAtUtc -- tri chronologique
  • ReceivedAtUtc -- tri par reception
  • ModuleName -- filtrage par module
  • ActionName -- filtrage par action
  • EntityType -- filtrage par type d'entite
  • EntityId -- recherche par identifiant d'entite
  • ActorUserId -- recherche par utilisateur
  • CorrelationId -- tracabilite de correlation
  • TraceId -- tracabilite distribuee
  • Composite (ModuleName, OccurredAtUtc) -- filtrage module + temps
  • Composite (ActorUserId, OccurredAtUtc) -- filtrage utilisateur + temps

Schema Messaging

Le module Messaging utilise un MessagingContext avec le schema messaging et une table sent_messages.

mermaid
erDiagram
    SentMessage {
        Guid Id PK
        string Channel
        string Recipient
        string TemplateName
        string Subject
        string Status
        string ErrorMessage
        int Attempts
        DateTime CreatedAt
        DateTime SentAt
        string CorrelationId
    }

Statuts de message

L'enum MessageStatus definit les etats possibles :

StatutDescription
PendingMessage en attente d'envoi
SentMessage envoye avec succes
FailedEchec d'envoi
BouncedMessage rejete par le destinataire

Contraintes et index

ColonneContrainteDescription
Channelmax 20 charsCanal d'envoi (email, sms, push)
Recipientmax 500 charsDestinataire
TemplateNamemax 100 charsNom du template
Subjectmax 500 charsSujet du message
Statusmax 20 charsStocke comme string
ErrorMessagemax 2000 charsMessage d'erreur
CorrelationIdmax 64 charsID de correlation

Index : CreatedAt, Status, Channel, Recipient, composite (Channel, CreatedAt).


Migrations EF Core

Les migrations sont gerees separement par module :

# Identity
dotnet ef migrations add <NomMigration> \
  --project src/Modules/Identity/Identity \
  --startup-project src/Api \
  --context IdentityContext

# Audit
dotnet ef migrations add <NomMigration> \
  --project src/Modules/Audit/Audit \
  --startup-project src/Api \
  --context AuditContext

# Messaging
dotnet ef migrations add <NomMigration> \
  --project src/Modules/Messaging/Messaging \
  --startup-project src/Api \
  --context MessagingContext

Historique des migrations Identity

DateMigrationDescription
2023-03-31initialSchema initial ASP.NET Identity
2026-02-19AddAuthInfrastructureSessions, OTP, historique mots de passe
2026-02-19MakeUserProfileFieldsNullableChamps profil optionnels
2026-02-20AddSocialLoginFieldsSupport login social (Google, Apple, Facebook)
2026-02-21MakeRefreshTokenHashUniqueIndex unique sur RefreshTokenHash
2026-02-21AddSessionMetadataAndDddTypesMetadonnees device, value objects DDD
2026-02-21AddPreviousRefreshTokenHashDetection replay token
2026-02-21ApplyUs008ReviewRemediationsCorrections post-review
2026-02-22NormalizeIdentityDateTimeColumnsToTimestampWithTimeZoneNormalisation timestamps
2026-03-03AddTokenFingerprintToOtpRecordEmpreinte token sur OTP

Historique des migrations Audit

DateMigrationDescription
2026-03-03InitialAuditLogTable audit_log initiale

Historique des migrations Messaging

DateMigrationDescription
2026-03-03InitialMessagingTable sent_messages initiale