313 lines
5.6 KiB
Markdown
313 lines
5.6 KiB
Markdown
# Skinbase – User Schema Review & Upgrade Plan
|
||
|
||
**Database:** MySQL / Percona 8.x
|
||
**Project:** Skinbase (new system, no legacy dependencies)
|
||
**Reviewed tables:** users, user_profiles, user_social_links, user_statistics
|
||
|
||
---
|
||
|
||
## 1. Overview
|
||
|
||
The current user-related database schema is **well designed**, modern, and suitable
|
||
for long-term growth.
|
||
Key strengths include:
|
||
|
||
- Clear separation of concerns
|
||
- Proper use of foreign keys and cascading deletes
|
||
- BigInt primary keys
|
||
- Soft deletes on users
|
||
- Migration-friendly legacy password handling
|
||
|
||
This document summarizes:
|
||
- what is already good
|
||
- recommended optimizations
|
||
- future-proofing steps (non-breaking)
|
||
- performance considerations for scale
|
||
|
||
---
|
||
|
||
## 2. users Table
|
||
|
||
### 2.1 What’s Good
|
||
|
||
- Unique `username` and `email`
|
||
- `legacy_password_algo` allows smooth migration from old systems
|
||
- `needs_password_reset` improves security posture
|
||
- Role stored as string allows flexibility in early stages
|
||
- Soft deletes enabled
|
||
|
||
### 2.2 Recommended Indexes
|
||
|
||
Add indexes for common query patterns:
|
||
|
||
```sql
|
||
CREATE INDEX idx_users_active ON users (is_active);
|
||
CREATE INDEX idx_users_role ON users (role);
|
||
CREATE INDEX idx_users_last_visit ON users (last_visit_at);
|
||
````
|
||
|
||
These improve:
|
||
|
||
* active user filtering
|
||
* admin queries
|
||
* “last seen” or online user features
|
||
|
||
### 2.3 Future Role Normalization (Planned)
|
||
|
||
Current approach is fine short-term:
|
||
|
||
```text
|
||
role = 'user' | 'admin' | 'moderator'
|
||
```
|
||
|
||
Planned future upgrade:
|
||
|
||
* `roles` table
|
||
* `user_roles` pivot table (many-to-many)
|
||
|
||
This allows:
|
||
|
||
* multiple roles per user
|
||
* temporary or scoped roles
|
||
* better permission modeling
|
||
|
||
⚠️ No immediate action required — just avoid hard-coding role logic.
|
||
|
||
### 2.4 Optional Security Enhancements
|
||
|
||
Recommended additions (optional but advised):
|
||
|
||
```sql
|
||
last_password_change_at TIMESTAMP NULL,
|
||
failed_login_attempts INT UNSIGNED DEFAULT 0,
|
||
locked_until TIMESTAMP NULL
|
||
```
|
||
|
||
Enables:
|
||
|
||
* rate limiting
|
||
* temporary account locking
|
||
* better auditability
|
||
|
||
---
|
||
|
||
## 3. user_profiles Table
|
||
|
||
### 3.1 Strengths
|
||
|
||
* Clean one-to-one relationship with `users`
|
||
* Public profile data separated from auth data
|
||
* Nullable fields for progressive profile completion
|
||
* Inclusive gender enum with safe default
|
||
* Localization-ready (`language`, `country_code`)
|
||
|
||
### 3.2 Country Handling Recommendation
|
||
|
||
Prefer using only:
|
||
|
||
```text
|
||
country_code (ISO 3166-1 alpha-2)
|
||
```
|
||
|
||
Benefits:
|
||
|
||
* language-independent
|
||
* avoids inconsistent country naming
|
||
* easier frontend mapping
|
||
|
||
`country` text field can be deprecated later if needed.
|
||
|
||
### 3.3 Avatar & Media Metadata (Future)
|
||
|
||
Current:
|
||
|
||
```text
|
||
avatar VARCHAR(255)
|
||
```
|
||
|
||
Recommended future approach:
|
||
|
||
```text
|
||
avatar_hash CHAR(64)
|
||
avatar_ext VARCHAR(10)
|
||
avatar_updated_at TIMESTAMP
|
||
```
|
||
|
||
This aligns with:
|
||
|
||
* hash-based file storage
|
||
* CDN-friendly URLs
|
||
* cache invalidation control
|
||
|
||
---
|
||
|
||
## 4. user_social_links Table
|
||
|
||
### 4.1 Current Design
|
||
|
||
* One row per platform per user
|
||
* Unique constraint on `(user_id, platform)`
|
||
* Cascade delete enabled
|
||
|
||
This is solid.
|
||
|
||
### 4.2 Platform Normalization (Recommended)
|
||
|
||
Current:
|
||
|
||
```text
|
||
platform VARCHAR(32)
|
||
```
|
||
|
||
Risk:
|
||
|
||
* inconsistent values (`twitter`, `x`, `Twitter`, etc.)
|
||
|
||
Options:
|
||
|
||
**Option A – Enum (simple):**
|
||
|
||
```sql
|
||
ENUM('twitter','x','instagram','deviantart','artstation','github','website')
|
||
```
|
||
|
||
**Option B – Reference Table (best long-term):**
|
||
|
||
* `social_platforms`
|
||
* foreign key `platform_id`
|
||
|
||
Option B is preferred for Skinbase as platforms evolve.
|
||
|
||
---
|
||
|
||
## 5. user_statistics Table
|
||
|
||
### 5.1 Strengths
|
||
|
||
* Isolated counters
|
||
* One row per user
|
||
* Minimal row size
|
||
* Clean FK relationship
|
||
|
||
### 5.2 Performance Warning (Important)
|
||
|
||
Avoid frequent direct updates like:
|
||
|
||
```sql
|
||
UPDATE user_statistics SET downloads = downloads + 1;
|
||
```
|
||
|
||
At scale, this causes:
|
||
|
||
* row locking
|
||
* write contention
|
||
* degraded performance
|
||
|
||
### 5.3 Recommended Strategy
|
||
|
||
* Use **Redis** (or in-memory cache) for real-time increments
|
||
* Periodically flush aggregated values to MySQL
|
||
* Use jobs / cron for batch updates
|
||
|
||
This ensures:
|
||
|
||
* fast user interactions
|
||
* scalable statistics tracking
|
||
|
||
---
|
||
|
||
## 6. Charset & Collation
|
||
|
||
Current:
|
||
|
||
```text
|
||
utf8mb4_unicode_ci
|
||
```
|
||
|
||
This is correct and safe.
|
||
|
||
Optional upgrade (MySQL 8+):
|
||
|
||
```text
|
||
utf8mb4_0900_ai_ci
|
||
```
|
||
|
||
Benefits:
|
||
|
||
* newer Unicode rules
|
||
* slightly better performance
|
||
|
||
Not required immediately.
|
||
|
||
---
|
||
|
||
## 7. Tables Planned for Future Expansion
|
||
|
||
Not required now, but expected as Skinbase grows:
|
||
|
||
### 7.1 user_activity_log
|
||
|
||
* logins
|
||
* uploads
|
||
* profile edits
|
||
* moderation actions
|
||
|
||
### 7.2 user_followers
|
||
|
||
* artist-to-artist following
|
||
* social graph features
|
||
|
||
### 7.3 user_settings
|
||
|
||
* privacy preferences
|
||
* notification settings
|
||
* email subscriptions
|
||
|
||
These should remain **separate tables** to avoid bloating `users`.
|
||
|
||
---
|
||
|
||
## 8. Laravel Implementation Notes
|
||
|
||
Recommended model casting:
|
||
|
||
```php
|
||
protected $casts = [
|
||
'is_active' => 'boolean',
|
||
'needs_password_reset' => 'boolean',
|
||
'email_verified_at' => 'datetime',
|
||
'last_visit_at' => 'datetime',
|
||
];
|
||
```
|
||
|
||
Best practices:
|
||
|
||
* Eager load profiles (`with('profile')`)
|
||
* Cache public profile + statistics
|
||
* Keep statistics out of main user queries
|
||
|
||
---
|
||
|
||
## 9. Final Verdict
|
||
|
||
**Schema quality:** Excellent
|
||
**Scalability:** Very good
|
||
**Migration readiness:** Excellent
|
||
**Long-term Skinbase fit:** Excellent
|
||
|
||
The current schema is production-ready and significantly better than typical legacy
|
||
user databases. Most future improvements can be introduced **without breaking changes**.
|
||
|
||
Primary future wins:
|
||
|
||
* Redis-backed statistics
|
||
* normalized roles and social platforms
|
||
* hash-based media storage
|
||
|
||
---
|
||
|
||
**Status:** Approved for production
|
||
**Next steps:** API design, public profile queries, legacy user migration
|
||
|
||
```
|