Кроки, відстань, калорії, пульс і сон мають бути отримані з платформи користувача (Android Health Connect на Android, Apple HealthKit на iOS) додатком для здоров'я та харчування — і представлені як один чистий, дедуплікований щоденний профіль. Між цими двома API дуже мало спільного: окремі механізми читання, різні схеми авторизації та різні структури даних. Ми створили мобільний шар синхронізації, який робить їх єдиним цілим.
Завдання
- Два нативні API, які ні в чому не узгоджуються. Apple HealthKit повертає щоденні агреговані дані через колбеки; Health Connect повертає необроблені, розбиті на сторінки зразки з багатими метаданими джерела. Однакові концепції, абсолютно різні форми — і додаток мав нормалізувати обидва в єдину схему.
- Подвійний підрахунок є типовим. Телефон, смарт-годинник і сторонній додаток можуть повідомляти про одні й ті ж 8 000 кроків. Просте підсумовування їх збільшує кожну метрику. Додаток повинен був розпізнавати джерела, що перекриваються, і враховувати кожну реальну активність лише один раз.
- Батарея та мережа не можуть розплачуватися за свіжість. Дані про здоров'я змінюються протягом дня, але читання повної історії при кожній синхронізації виснажило б батарею та перевантажило стільникові з'єднання. Частота синхронізації мала залишатися легкою, при цьому створюючи відчуття актуальності.
Дозволи можуть заплутувати. Десятки типів даних, дві моделі дозволів, обмеження на фонове читання та користувачі, які надають одні дозволи, але не інші — все це додаток повинен обробляти, не порушуючи роботу.
Наше рішення
Ми створили трирівневу синхронізацію: тонкий нативний шар зчитує API кожної платформи, шар нормалізації на пристрої дедуплікує та згортає дані у щоденні зведення, а бекенд зберігає їх та узгоджує в єдину довірену книгу обліку. Клієнт ніколи не відправляє необроблені зразки — він відправляє чисті, скориговані за часовим поясом щоденні зведення.

Архітектура
- iOS зчитує Apple HealthKit через react-native-health; Android зчитує Health Connect через react-native-health-connect.
- Нормалізація на пристрої перетворює вихідні дані обох платформ в єдину схему та дедуплікує їх, перш ніж щось покине телефон.
- Тригери синхронізації — 30 днів історії при першому запуску, потім легкі інкрементальні читання за 1 день з інтервалом 10 хвилин та при кожному відновленні роботи додатка.
- Транспорт — нормалізовані щоденні зведення надсилаються методом POST на головний сервер за адресою /user-health/system-activity, який пересилає їх (з ідентифікатором користувача + часовим поясом) до мікросервісу здоров'я.
- Зберігання даних — кожне джерело/день зберігається ідемпотентно як HealthSystemAggregate; потік змін MongoDB потім узгоджує його в щоденний HealthLedger.
Керований робочий процес Expo / React Native з нативними модулями HealthKit та Health Connect.
Ключові особливості
- Один договір нормалізації для двох API. Читачі, специфічні для платформи, передають дані одному formatHealthData кроку, який видає однакову форму незалежно від джерела — тому бекенд і UI ніколи не розгалужуються на iOS проти Android.
2. Дедуплікація на пристрої на основі максимального значення. У межах кожного джерела показання за день підсумовуються; потім щоденне значення є Math.max() між джерелами (калорії, ідентифіковані за source|deviceType), тому множинні показання з одного пристрою накопичуються, тоді як годинник і телефон, що повідомляють дані за той самий день, не подвоюються:
// Підсумувати показання в межах кожного джерела, потім взяти максимум МІЖ джерелами dateSourceMap[date][source] += entry.count; const totalSteps = Math.ceil(Math.max(...Object.values(dateSourceMap[date]))); |
3.Правильна стратегія читання для кожної платформи. Читання Health Connect розбиті на сторінки з циклом pageToken (1 000 записів/сторінка); щоденні агреговані дані HealthKit перебираються день за днем. Кожна особливість міститься у власному зчитувачі, невидимому для решти програми.
4. Частота синхронізації, що враховує заряд батареї. Повне 30-денне заповнення виконується лише один раз (захищено прапорцем першого виклику); після цього кожна синхронізація отримує лише один день — швидко на мобільному зв'язку, економно для енергії.
5. Щоденне групування з корекцією часового поясу. Зразки групуються за ключами YYYY-MM-DD у власному часовому поясі користувача, тому тренування о 23:00 припадає на потрібний день незалежно від місцезнаходження сервера.
6. Ідемпотентні записи на бекенді. Кожне зведення оновлюється/вставляється за (userId, deviceId, source, date) — повторне надсилання того ж дня не виконує жодних операцій, що робить повторні спроби та синхронізації, що перекриваються, безпечними.
7. Чесне зниження продуктивності. Відсутні дозволи, порожні читання та помилки обмеження частоти запитів Health Connect перехоплюються та чітко відображаються — порожній день ніколи не призводить до збою синхронізації, а користувач отримує чітке повідомлення замість тихої відмови.
Результати
- Єдиний, послідовний профіль здоров'я для iOS та Android — різниця між платформами невидима для решти програми.
- Джерела пристроїв/додатків, що перекриваються, дедуплікуються, тому кроки та калорії відображають реальність, а не завищені суми.
- Легкі інкрементальні синхронізації підтримують актуальність даних без виснаження батареї або витрати мобільних даних.
Записи з корекцією часового поясу та ідемпотентністю означають, що кожна метрика потрапляє в потрібний день, а повторні спроби ніколи не пошкоджують запис.
Стек технологій
React Native (Expo) · react-native-health (HealthKit) · react-native-health-connect · NestJS · MongoDB · MongoDB Change Streams · moment-timezone

