User Management Service
The User Management Service handles user profiles, preferences, and account management for the OpenLift platform, providing comprehensive user data management beyond authentication.
Overviewβ
The User Management Service is responsible for:
- User profile management and customization
- Personal preferences and settings
- Unit system preferences (metric/imperial)
- Profile information updates and validation
- User account data retrieval and management
Key Featuresβ
π€ Profile Managementβ
- Personal Information: Name, bio, fitness goals, experience level
- Customizable Profiles: Avatar, display preferences, public visibility settings
- Account Settings: Email preferences, notification settings, privacy controls
βοΈ Unit Preferencesβ
- Flexible Unit Systems: Support for metric, imperial, and mixed unit preferences
- Exercise-Specific Units: Different units for weight, distance, and measurements
- Automatic Conversion: Seamless unit conversion across the platform
π§ User Preferencesβ
- Workout Preferences: Default rest times, preferred workout types, equipment access
- Display Settings: Theme preferences, dashboard customization, data visualization
- Notification Settings: Push notifications, email preferences, reminder settings
Architectureβ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Client App β β User Mgmt Svc β β Database β
β β β β β β
β β’ Profile Forms βββββΆβ β’ Data Valid. βββββΆβ β’ User Profiles β
β β’ Settings UI ββββββ β’ Preferences ββββββ β’ Preferences β
β β’ Unit Display β β β’ Unit Mgmt β β β’ Settings β
β β’ Customization β β β’ Profile Mgmt β β β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
Service Responsibilitiesβ
β User Management Service Handlesβ
- User profile information (name, bio, fitness goals)
- Personal preferences and settings
- Unit system management and conversion
- Profile visibility and privacy settings
- Account information updates and validation
- User preference synchronization across devices
β User Management Service Does NOT Handleβ
- Authentication and login (handled by Authentication Service)
- Password management and security
- Workout data and exercise history
- Program enrollment and tracking
- Analytics and performance metrics
Key Data Modelsβ
User Profileβ
interface UserProfile {
id: string;
email: string;
username: string;
displayName?: string;
bio?: string;
avatarUrl?: string;
// Fitness information
fitnessLevel: 'beginner' | 'intermediate' | 'advanced';
fitnessGoals: string[];
dateOfBirth?: Date;
height?: number;
weight?: number;
// System preferences
unitPreferences: UnitPreferences;
notificationSettings: NotificationSettings;
privacySettings: PrivacySettings;
createdAt: Date;
updatedAt: Date;
}
Unit Preferencesβ
interface UnitPreferences {
weightUnit: 'kg' | 'lbs';
distanceUnit: 'km' | 'miles';
heightUnit: 'cm' | 'inches';
temperatureUnit: 'celsius' | 'fahrenheit';
// Mixed unit system support
useMetricForWeight: boolean;
useImperialForDistance: boolean;
}
Key Operationsβ
Get User Profileβ
query GetUserProfile {
userProfile {
id
email
username
displayName
bio
avatarUrl
fitnessLevel
fitnessGoals
unitPreferences {
weightUnit
distanceUnit
heightUnit
}
notificationSettings {
pushNotifications
emailNotifications
workoutReminders
}
}
}
Update Profileβ
mutation UpdateUserProfile($input: UpdateUserProfileInput!) {
updateUserProfile(input: $input) {
id
displayName
bio
fitnessLevel
fitnessGoals
unitPreferences {
weightUnit
distanceUnit
}
}
}
Update Unit Preferencesβ
mutation UpdateUnitPreferences($input: UnitPreferencesInput!) {
updateUnitPreferences(input: $input) {
weightUnit
distanceUnit
heightUnit
temperatureUnit
}
}
Integration Patternsβ
Flutter Profile Managementβ
class UserProfileService {
final GraphQLClient _client;
// Get complete user profile
Future<UserProfile> getUserProfile() async {
const query = '''
query GetUserProfile {
userProfile {
id
email
username
displayName
bio
fitnessLevel
unitPreferences {
weightUnit
distanceUnit
heightUnit
}
}
}
''';
final result = await _client.query(QueryOptions(document: gql(query)));
return UserProfile.fromJson(result.data!['userProfile']);
}
// Update user preferences
Future<UserProfile> updateProfile(UpdateUserProfileInput input) async {
const mutation = '''
mutation UpdateUserProfile(\$input: UpdateUserProfileInput!) {
updateUserProfile(input: \$input) {
id
displayName
bio
fitnessLevel
fitnessGoals
}
}
''';
final result = await _client.mutate(MutationOptions(
document: gql(mutation),
variables: {'input': input.toJson()},
));
return UserProfile.fromJson(result.data!['updateUserProfile']);
}
}
Unit Conversion Integrationβ
class UnitConversionService {
final UserProfileService _userService;
// Convert weight based on user preference
Future<String> formatWeight(double weight) async {
final profile = await _userService.getUserProfile();
switch (profile.unitPreferences.weightUnit) {
case 'kg':
return '${weight.toStringAsFixed(1)} kg';
case 'lbs':
final lbs = weight * 2.20462;
return '${lbs.toStringAsFixed(1)} lbs';
default:
return '${weight.toStringAsFixed(1)} kg';
}
}
// Convert distance based on user preference
Future<String> formatDistance(double distance) async {
final profile = await _userService.getUserProfile();
switch (profile.unitPreferences.distanceUnit) {
case 'km':
return '${distance.toStringAsFixed(2)} km';
case 'miles':
final miles = distance * 0.621371;
return '${miles.toStringAsFixed(2)} mi';
default:
return '${distance.toStringAsFixed(2)} km';
}
}
}
User Preference Categoriesβ
Fitness Preferencesβ
interface FitnessPreferences {
fitnessLevel: 'beginner' | 'intermediate' | 'advanced';
fitnessGoals: ('weight_loss' | 'muscle_gain' | 'strength' | 'endurance')[];
preferredWorkoutTypes: string[];
availableEquipment: string[];
workoutDaysPerWeek: number;
sessionDurationPreference: number; // minutes
}
Display Preferencesβ
interface DisplayPreferences {
theme: 'light' | 'dark' | 'auto';
language: string;
timezone: string;
dateFormat: 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY-MM-DD';
firstDayOfWeek: 'monday' | 'sunday';
}
Privacy Settingsβ
interface PrivacySettings {
profileVisibility: 'public' | 'friends' | 'private';
shareWorkoutData: boolean;
shareProgressPhotos: boolean;
allowFriendRequests: boolean;
showOnlineStatus: boolean;
}
Event Systemβ
User Management Eventsβ
interface UserManagementEvents {
'user.profile_updated': {
userId: string;
changedFields: string[];
};
'user.preferences_changed': {
userId: string;
preferenceType: string;
};
'user.units_changed': {
userId: string;
oldUnits: UnitPreferences;
newUnits: UnitPreferences;
};
}
Data Validationβ
Profile Validationβ
class UserProfileValidator {
validateDisplayName(name: string): ValidationResult {
if (name.length < 2) return { valid: false, error: 'Too short' };
if (name.length > 50) return { valid: false, error: 'Too long' };
if (!/^[a-zA-Z0-9\s_-]+$/.test(name)) {
return { valid: false, error: 'Invalid characters' };
}
return { valid: true };
}
validateBio(bio: string): ValidationResult {
if (bio.length > 500) return { valid: false, error: 'Bio too long' };
return { valid: true };
}
validateFitnessGoals(goals: string[]): ValidationResult {
if (goals.length === 0) return { valid: false, error: 'At least one goal required' };
if (goals.length > 5) return { valid: false, error: 'Too many goals' };
return { valid: true };
}
}
Integration with Other Servicesβ
Workout Services Integrationβ
// Services that depend on user preferences
class WorkoutHistoryService {
async formatWorkoutData(workout: WorkoutData, userId: string) {
const userPrefs = await this.userService.getUnitPreferences(userId);
// Convert weights based on user preference
workout.exercises = workout.exercises.map(exercise => ({
...exercise,
sets: exercise.sets.map(set => ({
...set,
weight: this.convertWeight(set.weight, userPrefs.weightUnit)
}))
}));
return workout;
}
}
Error Handlingβ
Profile Update Errorsβ
try {
await userService.updateProfile(profileData);
} on GraphQLError catch (e) {
switch (e.extensions?['code']) {
case 'INVALID_DISPLAY_NAME':
showError('Display name contains invalid characters');
break;
case 'BIO_TOO_LONG':
showError('Bio must be less than 500 characters');
break;
case 'INVALID_FITNESS_LEVEL':
showError('Please select a valid fitness level');
break;
case 'UNAUTHORIZED':
showError('You can only update your own profile');
break;
default:
showError('Failed to update profile');
}
}
Performance Considerationsβ
Caching Strategyβ
- Profile Caching: Cache user profiles locally for offline access
- Preference Caching: Cache unit preferences for immediate conversion
- Selective Updates: Only sync changed fields to reduce bandwidth
Optimization Patternsβ
class CachedUserProfileService {
UserProfile? _cachedProfile;
DateTime? _lastFetch;
Future<UserProfile> getUserProfile({bool forceRefresh = false}) async {
if (!forceRefresh &&
_cachedProfile != null &&
_lastFetch != null &&
DateTime.now().difference(_lastFetch!).inMinutes < 15) {
return _cachedProfile!;
}
_cachedProfile = await _fetchUserProfile();
_lastFetch = DateTime.now();
return _cachedProfile!;
}
}
Testing Strategyβ
Unit Testsβ
- Profile validation logic
- Unit conversion accuracy
- Preference update handling
- Data serialization/deserialization
Integration Testsβ
- Profile CRUD operations
- Unit preference synchronization
- Cross-service data consistency
- Event emission verification
Security Considerationsβ
Data Privacyβ
- Personal Information Protection: Sensitive data encrypted at rest
- Profile Visibility Controls: Granular privacy settings
- Data Access Logging: Track profile data access for security
Validation & Sanitizationβ
class ProfileSanitizer {
sanitizeDisplayName(name: string): string {
return name
.trim()
.replace(/[<>]/g, '') // Remove HTML tags
.substring(0, 50); // Enforce length limit
}
sanitizeBio(bio: string): string {
return bio
.trim()
.replace(/<[^>]*>/g, '') // Strip HTML
.substring(0, 500); // Enforce length limit
}
}
Related Servicesβ
Dependenciesβ
- Authentication Service: User identity verification
- Database: Profile and preference storage
Consumersβ
- Workout History Service: Unit preferences for data display
- Program Services: Fitness level and goals for recommendations
- Analytics Services: User preferences for personalized insights
- Coaching Service: Profile data for personalized coaching
API Documentationβ
For detailed GraphQL schema and usage examples, see:
- User Management API documentation: coming soon
Supportβ
For user management questions:
- Review the User Management API documentation
- Test profile operations in GraphQL Playground
- Check unit conversion logic and preferences
- Contact the development team for preference system questions