|
| 1 | +# PropChain Database Schema Documentation |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This document describes the complete PostgreSQL database schema for PropChain, including all entities, relationships, constraints, and indexes. |
| 6 | + |
| 7 | +## Technology Stack |
| 8 | + |
| 9 | +- **Database**: PostgreSQL 15+ |
| 10 | +- **ORM**: Prisma 5.x |
| 11 | +- **Connection Pooling**: Prisma connection pool (configured via DATABASE_URL) |
| 12 | + |
| 13 | +## Entity Relationship Diagram |
| 14 | + |
| 15 | +``` |
| 16 | +┌─────────────┐ ┌─────────────────┐ ┌───────────────┐ |
| 17 | +│ User │───────│ Property │───────│ Transaction │ |
| 18 | +└─────────────┘ └─────────────────┘ └───────────────┘ |
| 19 | + │ │ │ |
| 20 | + │ │ │ |
| 21 | + ▼ ▼ ▼ |
| 22 | +┌─────────────┐ ┌─────────────────┐ ┌───────────────┐ |
| 23 | +│ Role │ │ Document │◄──────│ Document │ |
| 24 | +└─────────────┘ └─────────────────┘ └───────────────┘ |
| 25 | + │ |
| 26 | + ▼ |
| 27 | +┌─────────────┐ ┌─────────────────┐ |
| 28 | +│ Permission │◄──────│ RolePermission │ |
| 29 | +└─────────────┘ └─────────────────┘ |
| 30 | +``` |
| 31 | + |
| 32 | +## Models |
| 33 | + |
| 34 | +### User |
| 35 | + |
| 36 | +Primary user entity representing all platform participants. |
| 37 | + |
| 38 | +| Field | Type | Constraints | Description | |
| 39 | +| ------------- | -------- | ----------------- | ------------------------ | |
| 40 | +| id | String | Primary Key, CUID | Unique identifier | |
| 41 | +| email | String | Unique, Not Null | User email address | |
| 42 | +| walletAddress | String | Unique, Nullable | Ethereum wallet address | |
| 43 | +| role | UserRole | Not Null, Default | User role enum | |
| 44 | +| roleId | String | Foreign Key | Reference to Role entity | |
| 45 | +| createdAt | DateTime | Default: now() | Creation timestamp | |
| 46 | +| updatedAt | DateTime | Auto-updated | Last update timestamp | |
| 47 | + |
| 48 | +**Indexes**: `email`, `walletAddress`, `role`, `createdAt` |
| 49 | + |
| 50 | +**Relations**: |
| 51 | + |
| 52 | +- One-to-Many: Properties (as owner) |
| 53 | +- One-to-Many: Transactions (as recipient via walletAddress) |
| 54 | +- One-to-Many: Documents (as uploader) |
| 55 | +- Many-to-One: Role |
| 56 | +- One-to-Many: RoleChangeLog |
| 57 | + |
| 58 | +### Property |
| 59 | + |
| 60 | +Real estate property listings. |
| 61 | + |
| 62 | +| Field | Type | Constraints | Description | |
| 63 | +| ----------- | -------------- | ----------------- | ------------------------- | |
| 64 | +| id | String | Primary Key, CUID | Unique identifier | |
| 65 | +| title | String | Not Null | Property title | |
| 66 | +| description | String | Nullable | Property description | |
| 67 | +| location | String | Not Null | Property address/location | |
| 68 | +| price | Decimal | Not Null | Property price | |
| 69 | +| status | PropertyStatus | Not Null, Default | Listing status | |
| 70 | +| ownerId | String | Foreign Key | Reference to User (owner) | |
| 71 | +| createdAt | DateTime | Default: now() | Creation timestamp | |
| 72 | +| updatedAt | DateTime | Auto-updated | Last update timestamp | |
| 73 | + |
| 74 | +**Indexes**: `ownerId`, `status`, `createdAt`, `location` |
| 75 | + |
| 76 | +**Relations**: |
| 77 | + |
| 78 | +- Many-to-One: User (owner) - CASCADE delete |
| 79 | +- One-to-Many: Transactions |
| 80 | +- One-to-Many: Documents |
| 81 | + |
| 82 | +### Transaction |
| 83 | + |
| 84 | +Blockchain transactions for property purchases and transfers. |
| 85 | + |
| 86 | +| Field | Type | Constraints | Description | |
| 87 | +| ----------- | ----------------- | --------------------- | --------------------------- | |
| 88 | +| id | String | Primary Key, CUID | Unique identifier | |
| 89 | +| fromAddress | String | Not Null | Sender wallet address | |
| 90 | +| toAddress | String | Not Null | Recipient wallet address | |
| 91 | +| amount | Decimal | Not Null | Transaction amount | |
| 92 | +| txHash | String | Nullable | Blockchain transaction hash | |
| 93 | +| status | TransactionStatus | Not Null, Default | Transaction status | |
| 94 | +| type | TransactionType | Not Null | Transaction type | |
| 95 | +| propertyId | String | Foreign Key, Nullable | Reference to Property | |
| 96 | +| createdAt | DateTime | Default: now() | Creation timestamp | |
| 97 | +| updatedAt | DateTime | Auto-updated | Last update timestamp | |
| 98 | + |
| 99 | +**Indexes**: `fromAddress`, `toAddress`, `status`, `createdAt`, `propertyId` |
| 100 | + |
| 101 | +**Relations**: |
| 102 | + |
| 103 | +- Many-to-One: Property - SET NULL on delete |
| 104 | +- Many-to-One: User (recipient via walletAddress) |
| 105 | +- One-to-Many: Documents |
| 106 | + |
| 107 | +### Document |
| 108 | + |
| 109 | +Document storage for property verification and transactions. |
| 110 | + |
| 111 | +| Field | Type | Constraints | Description | |
| 112 | +| ------------- | -------------- | --------------------- | ----------------------------- | |
| 113 | +| id | String | Primary Key, CUID | Unique identifier | |
| 114 | +| name | String | Not Null | Document name | |
| 115 | +| type | DocumentType | Not Null | Document type enum | |
| 116 | +| status | DocumentStatus | Not Null, Default | Verification status | |
| 117 | +| fileUrl | String | Not Null | Storage URL | |
| 118 | +| fileHash | String | Nullable | Content hash for verification | |
| 119 | +| mimeType | String | Nullable | File MIME type | |
| 120 | +| fileSize | Int | Nullable | File size in bytes | |
| 121 | +| description | String | Nullable | Document description | |
| 122 | +| propertyId | String | Foreign Key, Nullable | Reference to Property | |
| 123 | +| transactionId | String | Foreign Key, Nullable | Reference to Transaction | |
| 124 | +| uploadedById | String | Foreign Key | Reference to User (uploader) | |
| 125 | +| verifiedAt | DateTime | Nullable | Verification timestamp | |
| 126 | +| expiresAt | DateTime | Nullable | Expiration date | |
| 127 | +| createdAt | DateTime | Default: now() | Creation timestamp | |
| 128 | +| updatedAt | DateTime | Auto-updated | Last update timestamp | |
| 129 | + |
| 130 | +**Indexes**: `propertyId`, `transactionId`, `uploadedById`, `type`, `status`, `createdAt` |
| 131 | + |
| 132 | +**Relations**: |
| 133 | + |
| 134 | +- Many-to-One: Property - SET NULL on delete |
| 135 | +- Many-to-One: Transaction - SET NULL on delete |
| 136 | +- Many-to-One: User (uploader) - CASCADE delete |
| 137 | + |
| 138 | +### Role |
| 139 | + |
| 140 | +RBAC role definitions. |
| 141 | + |
| 142 | +| Field | Type | Constraints | Description | |
| 143 | +| ----------- | -------- | ----------------- | ------------------------ | |
| 144 | +| id | String | Primary Key, CUID | Unique identifier | |
| 145 | +| name | String | Unique, Not Null | Role name | |
| 146 | +| description | String | Nullable | Role description | |
| 147 | +| level | Int | Default: 0 | Role hierarchy level | |
| 148 | +| isSystem | Boolean | Default: false | System-defined role flag | |
| 149 | +| createdAt | DateTime | Default: now() | Creation timestamp | |
| 150 | +| updatedAt | DateTime | Auto-updated | Last update timestamp | |
| 151 | + |
| 152 | +**Relations**: |
| 153 | + |
| 154 | +- One-to-Many: Users |
| 155 | +- One-to-Many: RolePermissions |
| 156 | +- One-to-Many: RoleChangeLog |
| 157 | + |
| 158 | +### Permission |
| 159 | + |
| 160 | +Granular permission definitions. |
| 161 | + |
| 162 | +| Field | Type | Constraints | Description | |
| 163 | +| ----------- | -------- | ----------------- | ---------------------- | |
| 164 | +| id | String | Primary Key, CUID | Unique identifier | |
| 165 | +| resource | String | Not Null | Resource name | |
| 166 | +| action | String | Not Null | Action name (CRUD) | |
| 167 | +| description | String | Nullable | Permission description | |
| 168 | +| createdAt | DateTime | Default: now() | Creation timestamp | |
| 169 | +| updatedAt | DateTime | Auto-updated | Last update timestamp | |
| 170 | + |
| 171 | +**Unique Constraint**: `[resource, action]` |
| 172 | + |
| 173 | +**Relations**: |
| 174 | + |
| 175 | +- One-to-Many: RolePermissions |
| 176 | + |
| 177 | +### ApiKey |
| 178 | + |
| 179 | +API key management for external integrations. |
| 180 | + |
| 181 | +| Field | Type | Constraints | Description | |
| 182 | +| ------------ | -------- | ----------------- | ----------------------------- | |
| 183 | +| id | String | Primary Key, CUID | Unique identifier | |
| 184 | +| name | String | Not Null | Key name/description | |
| 185 | +| key | String | Unique, Not Null | Hashed API key | |
| 186 | +| keyPrefix | String | Not Null | Key prefix for identification | |
| 187 | +| scopes | String[] | Not Null | Allowed scopes | |
| 188 | +| requestCount | BigInt | Default: 0 | Total request count | |
| 189 | +| lastUsedAt | DateTime | Nullable | Last usage timestamp | |
| 190 | +| isActive | Boolean | Default: true | Active status | |
| 191 | +| rateLimit | Int | Nullable | Requests per minute limit | |
| 192 | +| createdAt | DateTime | Default: now() | Creation timestamp | |
| 193 | +| updatedAt | DateTime | Auto-updated | Last update timestamp | |
| 194 | + |
| 195 | +**Indexes**: `keyPrefix`, `isActive`, `createdAt` |
| 196 | + |
| 197 | +## Enums |
| 198 | + |
| 199 | +### UserRole |
| 200 | + |
| 201 | +- `ADMIN` - Full system access |
| 202 | +- `AGENT` - Property management |
| 203 | +- `SELLER` - Can list properties |
| 204 | +- `BUYER` - Can purchase properties |
| 205 | +- `VIEWER` - Read-only access |
| 206 | +- `USER` - Standard user |
| 207 | +- `VERIFIED_USER` - Verified standard user |
| 208 | + |
| 209 | +### PropertyStatus |
| 210 | + |
| 211 | +- `DRAFT` - Not yet submitted |
| 212 | +- `PENDING` - Awaiting approval |
| 213 | +- `APPROVED` - Approved for listing |
| 214 | +- `LISTED` - Currently listed |
| 215 | +- `SOLD` - Property sold |
| 216 | +- `REMOVED` - Removed from listing |
| 217 | + |
| 218 | +### TransactionStatus |
| 219 | + |
| 220 | +- `PENDING` - Awaiting processing |
| 221 | +- `PROCESSING` - Currently processing |
| 222 | +- `COMPLETED` - Successfully completed |
| 223 | +- `FAILED` - Transaction failed |
| 224 | +- `CANCELLED` - Transaction cancelled |
| 225 | + |
| 226 | +### TransactionType |
| 227 | + |
| 228 | +- `PURCHASE` - Property purchase |
| 229 | +- `TRANSFER` - Property transfer |
| 230 | +- `ESCROW` - Escrow deposit |
| 231 | +- `REFUND` - Refund transaction |
| 232 | + |
| 233 | +### DocumentType |
| 234 | + |
| 235 | +- `TITLE_DEED` - Property title deed |
| 236 | +- `OWNERSHIP_CERTIFICATE` - Ownership certificate |
| 237 | +- `INSPECTION_REPORT` - Property inspection |
| 238 | +- `APPRAISAL` - Property appraisal |
| 239 | +- `INSURANCE` - Insurance documents |
| 240 | +- `TAX_DOCUMENT` - Tax-related documents |
| 241 | +- `CONTRACT` - Contracts and agreements |
| 242 | +- `IDENTITY` - Identity verification |
| 243 | +- `OTHER` - Other documents |
| 244 | + |
| 245 | +### DocumentStatus |
| 246 | + |
| 247 | +- `PENDING` - Awaiting verification |
| 248 | +- `VERIFIED` - Verified and valid |
| 249 | +- `REJECTED` - Rejected/invalid |
| 250 | +- `EXPIRED` - Document expired |
| 251 | + |
| 252 | +## Database Configuration |
| 253 | + |
| 254 | +### Connection String Format |
| 255 | + |
| 256 | +``` |
| 257 | +postgresql://[user]:[password]@[host]:[port]/[database]?connection_limit=10&pool_timeout=30 |
| 258 | +``` |
| 259 | + |
| 260 | +### Connection Pooling Parameters |
| 261 | + |
| 262 | +- `connection_limit` - Maximum number of connections (default: 10) |
| 263 | +- `pool_timeout` - Connection pool timeout in seconds (default: 30) |
| 264 | + |
| 265 | +### Recommended Production Settings |
| 266 | + |
| 267 | +```env |
| 268 | +DATABASE_URL="postgresql://postgres:password@localhost:5432/propchain?connection_limit=20&pool_timeout=30&statement_cache_size=100" |
| 269 | +``` |
| 270 | + |
| 271 | +## Migrations |
| 272 | + |
| 273 | +### Running Migrations |
| 274 | + |
| 275 | +```bash |
| 276 | +# Development |
| 277 | +npm run migrate |
| 278 | + |
| 279 | +# Production |
| 280 | +npm run migrate:deploy |
| 281 | + |
| 282 | +# Reset database (development only) |
| 283 | +npm run migrate:reset |
| 284 | +``` |
| 285 | + |
| 286 | +### Creating New Migrations |
| 287 | + |
| 288 | +```bash |
| 289 | +npx prisma migrate dev --name <migration_name> |
| 290 | +``` |
| 291 | + |
| 292 | +## Seeding |
| 293 | + |
| 294 | +### Run Seed Data |
| 295 | + |
| 296 | +```bash |
| 297 | +npm run db:seed |
| 298 | +``` |
| 299 | + |
| 300 | +The seed script creates: |
| 301 | + |
| 302 | +- System roles (Administrator, Agent, User) |
| 303 | +- Default permissions for each role |
| 304 | +- Sample users (admin, agent, buyer, seller) |
| 305 | +- Sample properties |
| 306 | +- Sample transactions |
| 307 | +- Sample documents |
| 308 | +- Development API keys |
| 309 | + |
| 310 | +## Performance Considerations |
| 311 | + |
| 312 | +### Index Strategy |
| 313 | + |
| 314 | +All foreign keys and frequently queried fields are indexed. The following query patterns are optimized: |
| 315 | + |
| 316 | +- User lookup by email/wallet |
| 317 | +- Property search by owner, status, location |
| 318 | +- Transaction lookup by addresses, status, property |
| 319 | +- Document retrieval by property, transaction, uploader |
| 320 | + |
| 321 | +### Query Optimization Tips |
| 322 | + |
| 323 | +1. Use `select` to limit returned fields |
| 324 | +2. Use `include` sparingly - prefer explicit joins |
| 325 | +3. Use pagination for large result sets |
| 326 | +4. Use transactions for related operations |
0 commit comments