Skip to content

Domain model

This page ships the conceptual model (UML class), the physical model (ER), and a glossary of each entity's attributes. Diagrams use Mermaid — rendered automatically by MkDocs Material.

Class diagram (UML)

classDiagram
    class User {
        +UUID id
        +str email
        +str password_hash
        +str name
        +str|None photo_url
        +bool is_active
        +datetime created_at
        +signup() User
        +login(password) tokens
    }

    class Organization {
        +UUID id
        +UUID owner_id
        +str name
        +str slug
        +bool is_active
        +datetime created_at
        +invite(email, role) Invitation
        +add_member(user, role) Membership
        +transfer_ownership(target) void
    }

    class Membership {
        +UUID id
        +UUID organization_id
        +UUID user_id
        +Role role
        +datetime joined_at
        +can(permission) bool
    }

    class Invitation {
        +UUID id
        +UUID organization_id
        +str email
        +Role role
        +str token_hash
        +InvitationStatus status
        +datetime expires_at
        +accept(user) Membership
        +revoke() void
    }

    class Product {
        +UUID id
        +UUID organization_id
        +str title
        +str description
        +bool is_active
        +list~str~ image_keys
        +datetime created_at
    }

    class ProductVariant {
        +UUID id
        +UUID product_id
        +str sku
        +dict attributes
        +int stock_balance() (derived)
        +int current_price_cents() (derived)
    }

    class PriceHistory {
        +UUID id
        +UUID variant_id
        +int amount_cents
        +str currency
        +datetime valid_from
    }

    class StockMovement {
        +UUID id
        +UUID variant_id
        +StockKind kind
        +int qty
        +str reason
        +str ref_type
        +UUID ref_id
        +datetime created_at
    }

    class Cart {
        +UUID id
        +UUID user_id
        +UUID organization_id
        +CartStatus status
        +datetime expires_at
    }

    class CartItem {
        +UUID id
        +UUID cart_id
        +UUID variant_id
        +int qty
        +int price_snapshot_cents
    }

    class Order {
        +UUID id
        +UUID buyer_id
        +UUID organization_id
        +OrderStatus status
        +int total_cents
        +str shipping_address
        +str idempotency_key
        +datetime created_at
        +transition_to(status) void
    }

    class OrderItem {
        +UUID id
        +UUID order_id
        +UUID variant_id
        +int qty
        +int unit_price_cents
    }

    class Review {
        +UUID id
        +UUID user_id
        +UUID variant_id
        +int score
        +str comment
        +datetime created_at
    }

    User "1" --o "0..2" Organization : owns
    User "1" --o "0..5" Membership : has
    Organization "1" --o "1..10" Membership : has
    Organization "1" --o "*" Invitation : sends
    Organization "1" --o "*" Product : sells
    Product "1" --* "1..*" ProductVariant : composed of
    ProductVariant "1" --o "*" PriceHistory : priced over time
    ProductVariant "1" --o "*" StockMovement : ledgered by
    User "1" --o "*" Cart : owns
    Cart "1" --o "*" CartItem : holds
    User "1" --o "*" Order : places
    Order "1" --* "1..*" OrderItem : itemized as
    User "1" --o "*" Review : writes
    ProductVariant "1" --o "*" Review : reviewed by

ER diagram (physical model)

erDiagram
    USERS ||--o{ ORGANIZATIONS : "owns"
    USERS ||--o{ MEMBERSHIPS : "joins"
    ORGANIZATIONS ||--o{ MEMBERSHIPS : "has"
    ORGANIZATIONS ||--o{ INVITATIONS : "sends"
    ORGANIZATIONS ||--o{ PRODUCTS : "sells"
    PRODUCTS ||--|{ PRODUCT_VARIANTS : "has"
    PRODUCT_VARIANTS ||--o{ PRICE_HISTORY : "priced"
    PRODUCT_VARIANTS ||--o{ STOCK_MOVEMENTS : "tracked"
    USERS ||--o{ CARTS : "owns"
    CARTS ||--o{ CART_ITEMS : "holds"
    CART_ITEMS }o--|| PRODUCT_VARIANTS : "refs"
    USERS ||--o{ ORDERS : "places"
    ORGANIZATIONS ||--o{ ORDERS : "fulfills"
    ORDERS ||--|{ ORDER_ITEMS : "items"
    ORDER_ITEMS }o--|| PRODUCT_VARIANTS : "refs"
    USERS ||--o{ REVIEWS : "writes"
    PRODUCT_VARIANTS ||--o{ REVIEWS : "rated"

    USERS {
        UUID id PK
        string email UK
        string password_hash
        string name
        string photo_url
        bool is_active
        datetime created_at
        datetime updated_at
    }
    ORGANIZATIONS {
        UUID id PK
        UUID owner_id FK
        string name
        string slug UK
        bool is_active
        datetime created_at
        datetime updated_at
    }
    MEMBERSHIPS {
        UUID id PK
        UUID organization_id FK
        UUID user_id FK
        string role
        datetime joined_at
    }
    INVITATIONS {
        UUID id PK
        UUID organization_id FK
        string email
        string role
        string token_hash UK
        string status
        datetime expires_at
        datetime created_at
    }
    PRODUCTS {
        UUID id PK
        UUID organization_id FK
        string title
        string description
        bool is_active
        json image_keys
        datetime created_at
        datetime updated_at
    }
    PRODUCT_VARIANTS {
        UUID id PK
        UUID product_id FK
        string sku UK
        json attributes
        datetime created_at
    }
    PRICE_HISTORY {
        UUID id PK
        UUID variant_id FK
        int amount_cents
        string currency
        datetime valid_from
    }
    STOCK_MOVEMENTS {
        UUID id PK
        UUID variant_id FK
        string kind
        int qty
        string reason
        string ref_type
        UUID ref_id
        UUID audit_user_id
        datetime created_at
    }
    CARTS {
        UUID id PK
        UUID user_id FK
        UUID organization_id FK
        string status
        datetime expires_at
        datetime created_at
    }
    CART_ITEMS {
        UUID id PK
        UUID cart_id FK
        UUID variant_id FK
        int qty
        int price_snapshot_cents
    }
    ORDERS {
        UUID id PK
        UUID buyer_id FK
        UUID organization_id FK
        string status
        int total_cents
        string shipping_address
        string idempotency_key UK
        datetime created_at
        datetime updated_at
    }
    ORDER_ITEMS {
        UUID id PK
        UUID order_id FK
        UUID variant_id FK
        int qty
        int unit_price_cents
    }
    REVIEWS {
        UUID id PK
        UUID user_id FK
        UUID variant_id FK
        int score
        string comment
        datetime created_at
    }

Enums

classDiagram
    class Role {
        <<enum>>
        OWNER
        ADMIN
        MEMBER
    }

    class InvitationStatus {
        <<enum>>
        PENDING
        ACCEPTED
        REVOKED
        EXPIRED
        SUPERSEDED
    }

    class StockKind {
        <<enum>>
        IN
        OUT
        ADJUST
        RESERVATION
        RELEASE
    }

    class CartStatus {
        <<enum>>
        OPEN
        CONVERTED
        EXPIRED
        ABANDONED
    }

    class OrderStatus {
        <<enum>>
        PENDING
        PAID
        SHIPPED
        DELIVERED
        CANCELLED
        RETURNED
    }

Invariants (executable summary)

Entity Invariant Where it's enforced
User unique email, active, bcrypt-hashed DB constraint + signup service
Organization owner is a member with role OWNER, unique slug, 1 OWNER per org constraint + trigger or service
Membership (org_id, user_id) unique, ≤10 per org, ≤5 per user, exactly 1 OWNER per org unique constraint + service guard
Invitation unique token_hash, expires in 7d, terminal status doesn't mutate constraint + state machine in service
Product ≥1 active variant to appear in the catalog catalog query
ProductVariant unique SKU within organization_id (via JOIN through product) composite constraint + service
PriceHistory append-only — no UPDATE/DELETE revoke DB permissions + service
StockMovement append-only, balance never negative service guard + check constraint
Order unique idempotency_key, transitions follow the state machine constraint + state machine

Entity → SDK primitive mapping

Entity Inherits from Repository Service base
User BaseUserModel BaseRepository[UserModel] BaseService
Organization BaseModel + AuditMixin + SoftDeleteMixin BaseRepository[OrganizationModel] BaseService
Membership BaseModel + AuditMixin BaseRepository[MembershipModel] BaseService
Invitation BaseModel + AuditMixin BaseRepository[InvitationModel] BaseService
Product BaseModel + AuditMixin + SoftDeleteMixin custom (JOINs variant+price) BaseService
ProductVariant BaseModel + AuditMixin custom BaseService
PriceHistory BaseModel (no updated_at) append-only BaseService
StockMovement BaseModel (no updated_at) append-only via bulk_create when batching BaseService
Cart/CartItem BaseModel + AuditMixin BaseRepository[CartModel] BaseService
Order/OrderItem BaseModel + AuditMixin custom BaseService
Review BaseModel + AuditMixin BaseRepository[ReviewModel] BaseService

PriceHistory and StockMovement are append-only, so their repository doesn't expose update or delete — only create/list/get. That prevents accidental ALTER on history.

Next step

Jump to Critical flows to see the sequence diagrams covering signup, invitation, product creation, checkout, and shipment.