Перейти до вмісту

Зв’язки мають наслідки

Також доступно 28 мовами

English · Español · 日本語 · Português (Brasil) · Français · Deutsch · Italiano · 한국어 · 简体中文 · Português (Portugal) · 繁體中文 · Bahasa Indonesia · Türkçe · Polski · Tiếng Việt · ไทย · Nederlands · العربية · עברית · हिन्दी · Русский · Svenska · Dansk · Norsk bokmål · Suomi · Español (México) · Français (Canada) · Deutsch (Schweiz)

A brick house with a glass room built onto its side, and a separate timber garden studio across the lawn, joined to it only by a stepping-stone path.
Зображення згенеровано ШІ

Модуль 7 з 10 розділу Архітектура даних Salesforce, розділ 3 курсу Data Architect. Кожен модуль самостійний. Попередній: Поля — це теж архітектура · Наступний: Власність і життєвий цикл

Питання, на яке відповідає цей модуль: Що я насправді обираю, коли з’єдную два об’єкти Salesforce?

Намалювати зв’язок легко. У розділі 2 ми малювали лінії й давали їм назви: роздрібний партнер надсилає багато маніфестів, доставка містить багато позицій, бригади й вантажівки зустрічаються в Призначенні бригади. Ми говорили про кардинальність, обов’язковість і історію. Тепер ми реалізуємо ці лінії в Salesforce, і лінія на діаграмі починає нести набагато більшу вагу.

Коли я обираю тип зв’язку в Salesforce, я не просто кажу: ці два записи пов’язані. Можливо, я також вирішую, хто володіє дочірнім записом, чи може він існувати без батьківського, хто може його бачити, що станеться, коли батьківський запис видалять, чи можна перенести його до іншого батьківського запису, чи можна підсумувати його в батьківському і що станеться, коли дочірніх записів будуть сотні тисяч. Зв’язок — це не просто лінія. Він несе поведінку.

Почніть із бізнес-зв’язку

Section titled “Почніть із бізнес-зв’язку ”

Перш ніж думати про lookup чи master-detail, поверніться до бізнесу. Візьміть доставку та її позиції: холодильник, монтаж, вивезення старого. Чи може позиція існувати без своєї доставки? Ні. Якщо доставку видалять, чи мають зникнути і її позиції? Так. Чи потрібні позиціям власний власник чи власна безпека? Не дуже. Чи має хтось переносити позицію з однієї доставки в іншу? Сподіваюся, не випадково.

Тепер візьміть доставку і маршрут, на якому вона стоїть. Доставка існує ще до того, як її включили в маршрут, її можна перенести на інший маршрут, коли план змінюється, а видалення маршруту ніколи не повинно видаляти доставки на ньому. Дві лінії «один до багатьох», які на діаграмі виглядають однаково, із зовсім різною поведінкою. Зауважте: ми досі не сказали ні «lookup», ні «master-detail». Спершу опишіть поведінку. Конфігурація її реалізує.

Відкрийте екран New Field (нове поле), і Salesforce запропонує вам кілька типів зв’язків (Object Relationships Overview (opens in a new tab)):

  • Lookup: пов’язує два об’єкти. Хліб і масло зв’язків Salesforce.
  • Master-detail: зв’язок «батьківський–дочірній», у якому батьківський запис керує дочірнім.
  • Багато до багатьох: не окремий тип поля, а шаблон: об’єкт-з’єднувач Salesforce (Salesforce Junction Object), користувацький об’єкт із двома master-detail.
  • Зв’язок із собою (self-relationship): lookup з об’єкта на самого себе.
  • Ієрархічний (hierarchical): особливий lookup, що існує лише в об’єкті User, наприклад керівник користувача.
  • External lookup та indirect lookup: зв’язки до зовнішніх об’єктів і від них, об’єктів __x з модуля 4. External lookup вказує на батьківський зовнішній об’єкт; indirect lookup вказує із зовнішнього об’єкта на стандартний чи користувацький об’єкт, зіставляючи за унікальним External ID.

Більшість вашого часу на проєктування йде на перші три. Розгляньмо їх по одному.

Lookup: ці записи пов’язані

Section titled “Lookup: ці записи пов’язані ”

Lookup каже: цей запис пов’язаний із тим записом. Дочірній запис зберігає власну ідентичність, власного власника і власне життя. Доставка вказує на свій маршрут. Якщо маршрут скасують, доставки не зникнуть, а повернуться на повторне планування маршруту.

Кумедно: поле lookup розміщується в дочірньому об’єкті, але й батьківський дещо отримує — пов’язаний список (related list) своїх дочірніх записів. Тож, створюючи lookup, назвіть обидва кінці. Related List Label (мітка пов’язаного списку) — це те, що люди бачать у батьківському записі, а Child Relationship Name (назва дочірнього зв’язку) — те, чим розробники й інтеграції користуються, щоб пройти від батьківського запису до дочірніх. Не погоджуйтеся на значення за замовчуванням.

Lookup може бути обов’язковим або необов’язковим. Коли він необов’язковий, ви вирішуєте, що станеться, якщо запис, на який він вказує, видалять, і варіантів три (Considerations for Object Relationships (opens in a new tab)):

  • Clear the value of this field (очистити значення цього поля), за замовчуванням. Дочірній запис лишається, з порожнім lookup.
  • Don’t allow deletion (не дозволяти видалення) запису, на який вказує lookup. Спробуйте видалити маршрут — і Salesforce скаже «ні»: він є частиною зв’язку.
  • Delete this record also (видалити й цей запис), каскадне видалення, як у master-detail. Його пропонують лише для користувацького lookup на користувацькому об’єкті, воно вимкнене за замовчуванням, і Salesforce має ввімкнути його для вашої org, бо каскадне видалення обходить безпеку і спільний доступ: користувачі можуть видалити записи, яких навіть не бачили.

Це рішення про життєвий цикл, сховане в екрані налаштувань.

Фільтри lookup звужують те, що користувач може вибрати. Скажімо, у Delivery є lookup Installer (монтажник) на Contact, а монтажники — лише один тип запису контакту серед багатьох. Фільтр lookup показує лише монтажників, і він може фільтрувати за поточним записом, за записом, який шукають, і за користувачем: його профілем, роллю чи дозволами (Lookup Filters (opens in a new tab)). Це дешевий спосіб тримати lookup чесним.

Master-detail: цей дочірній запис належить тому батьківському

Section titled “Master-detail: цей дочірній запис належить тому батьківському ”

Master-detail — набагато сильніше твердження. На заняттях я називаю його дуже тісним зв’язуванням. Detail не може існувати без свого master; поле master обов’язкове. Detail втрачає власне поле Owner і успадковує власника батьківського запису. Окремої таблиці спільного доступу для detail немає, тож поділитися дочірнім записом окремо не можна; він успадковує спільний доступ батьківського. Ось чому в detail-об’єкта не може бути правил спільного доступу, ручного спільного доступу чи черг: усім їм потрібне поле Owner (Object Relationships Overview (opens in a new tab)). У Setup його налаштування спільного доступу показує Controlled by Parent (керується батьківським). А в самому полі master-detail ви обираєте Sharing Setting (налаштування спільного доступу): чи потрібен користувачеві доступ Read Only чи Read/Write до master, щоб створювати, редагувати чи видаляти його details. Видалення master видаляє його details. А master отримує зведені поля (roll-up summary).

Отже, якщо позиція доставки — detail доставки, я кажу: ця позиція належить цій доставці протягом усього свого життя. Саме на це питання я хочу почути відповідь, перш ніж хтось обере Master-Detail Relationship у Setup.

Ось мій улюблений спосіб з’ясувати, чи ми все продумали: що має статися з дочірніми записами, якщо хтось видалить батьківський? Усім стає ніяково. Добре. Тепер ми займаємося архітектурою.

Роздрібний партнер має сотні тисяч історичних доставок. Хтось видаляє партнера. Чи мають доставки зникнути? Майже напевно ні: від них залежать претензії, рахунки й історія. Можливо, видаляти партнера взагалі не слід дозволяти, а натомість деактивувати його. З master-detail видалення master каскадно видаляє details і їхні details; відновлення master повертає їх (Considerations for Object Relationships (opens in a new tab)). Тож коли хтось каже «master-detail дає нам зведення», я відповідаю: чудово. А на що ще ви щойно погодилися?

Власність іде в комплекті

Section titled “Власність іде в комплекті ”

З lookup дочірній запис може мати власного власника. З master-detail — не може; доступ іде за master. Це саме те, що треба, якщо detail справді є частиною батьківського запису. Але якщо дочірньому запису потрібен власний власник, власний спільний доступ чи власний життєвий цикл, модель каже вам, що master-detail — хибний вибір. Наступний модуль глибше розглядає власність, а курс Sharing and Visibility іде ще далі. Поки що: проєктування зв’язків і проєктування безпеки — не окремі розмови. Модель даних формує модель безпеки ще до того, як хтось напише правило спільного доступу.

Батьківський запис обов’язковий чи необов’язковий?

Section titled “Батьківський запис обов’язковий чи необов’язковий? ”

Чи може дочірній запис існувати, поки ми ще не знаємо його батьківського? Це важливіше, ніж здається. Якщо доставки надходять у маніфесті напередодні ввечері, а маршрути будуються пізніше, обов’язковий маршрут у доставці означав би, що планування маршруту має відбутися ще до того, як запис узагалі можна створити. Фізичний зв’язок має підтримувати реальний життєвий цикл. Тож я не проєктую зв’язки лише за статичною діаграмою. Я хочу знати: як вони народжуються, чи можуть існувати самі, чи можуть переміщатися і як вони помирають?

Чи може дочірній запис змінити батьківський?

Section titled “Чи може дочірній запис змінити батьківський? ”

За замовчуванням дочірній запис master-detail не можна перенести до іншого батьківського. Це можна дозволити, позначивши Allow reparenting у зв’язку (Considerations for Object Relationships (opens in a new tab)). Мій інстинкт консервативний: якщо бізнес каже, що дочірній запис належить одному батьківському на все життя, я не роблю його перенесення легким. Перенесення позиції з однієї доставки в іншу має бути свідомою бізнес-подією, а не редагуванням поля. Знову ж таки, налаштування — це не архітектура. Архітектура — це правило життєвого циклу, а налаштування його реалізує.

Ще дві речі, на яких команди спотикаються пізніше. Не можна створити зв’язок master-detail на об’єкті, у якому вже є дані; ви створюєте lookup, заповнюєте його в кожному записі, а потім конвертуєте. А повернути його назад у lookup можна, лише якщо на master немає зведених полів. Стежте й за тим, що конвертація робить із безпекою: перетворення lookup на master-detail встановлює для дочірнього об’єкта спільний доступ Controlled by Parent, а перетворення master-detail назад на lookup встановлює для дочірнього об’єкта налаштування за замовчуванням для організації Public Read/Write (Considerations for Object Relationships (opens in a new tab)). Записи, які вчора були приватними, сьогодні відкриті — доки хтось не помітить. Обирайте рано.

Зведення корисні, але не безкоштовні

Section titled “Зведення корисні, але не безкоштовні ”

Я люблю зведення. Порахувати позиції доставки, підсумувати суми рядків рахунку, знайти найпізнішу дату — нативно, без коду. Поле master-detail розміщується в дочірньому об’єкті, а зведення — в батьківському. Але в них є дрібний шрифт (Roll-Up Summary Field (opens in a new tab)):

  • Обчислення зведених значень після певних змін (нове зведене поле, змінена формула, від якої воно залежить, примусовий перерахунок) може тривати до 30 хвилин, залежно від того, скільки записів зачеплено. Я бачив, як люди дивувалися, чому число в батьківському записі досі не змінилося.
  • Вони не можуть зводити все. Поле автонумерації не може бути полем, яке ви агрегуєте. А зведення може підсумовувати поле-формулу, лише якщо ця формула не сягає іншого об’єкта і не використовує функцію, що змінюється сама по собі, як-от NOW() чи TODAY(). Формула «днів прострочення», побудована на TODAY(), змінюється щоночі без того, щоб хтось зберіг запис, тож зведенню нема від чого перераховуватися. Такому числу потрібен запланований flow або звіт.
  • Фільтр теж має обмеження: жодних довгих текстових полів, списків множинного вибору, Description, системних полів на кшталт Last Activity, міжоб’єктних формул чи полів lookup.
  • Зведень немає на сторінках редагування, тож правило перевірки може їх використовувати, але не може показати там свою помилку.

І не перевертайте рішення. Не кажіть: «Нам потрібне зведення, отже, це має бути master-detail». Кажіть: «Цей дочірній запис справді належить цьому батьківському, власність і життєвий цикл підходять, і одна з переваг — нативні зведення». Якщо семантика не підходить, розв’яжіть обчислення іншим способом. Не викривляйте модель даних заради функції.

То який інший спосіб? Ви будуєте зведення самі: поле в батьківському об’єкті, що зберігає обчислене значення, і автоматизацію, яка тримає його актуальним. Скажімо, роздрібний партнер пов’язаний із доставкою через lookup, як і має бути, а бізнес хоче бачити в партнера Open Deliveries (відкриті доставки). Додайте партнерові числове поле Open Deliveries. Потім record-triggered flow, або Apex trigger, коли обсяг чи логіка вимагають коду, перераховує його щоразу, коли змінюється доставка. Це працює через lookup, може фільтрувати за чим завгодно і може використовувати дати й формули, яких не вміє нативне зведення.

Але тепер правильність на вас, і саме тут такі рішення ламаються. Автоматизація має спрацьовувати на кожну важливу зміну: новий дочірній запис, відредагований, видалений, відновлений і дочірній запис, переміщений до іншого батьківського, а це означає перерахунок і старого батьківського, і нового. Вона має витримати завантаження даних по 200 записів за раз, не впираючись у ліміти. Перераховуйте заново, а не додавайте й віднімайте, щоб одна пропущена подія не лишила число хибним назавжди. Сплануйте спосіб перерахувати все — для першого завантаження і для дня, коли значення розійдуться. І пам’ятайте, що тепер кожне збереження дочірнього запису оновлює батьківський, тож батьківський запис із тисячами дочірніх повертає блокування, які ми зараз побачимо. Нативне зведення робить усе це за вас; власне — це маленька програма, якою володієте ви.

Три рівні вглиб: master-detail один на одному

Section titled “Три рівні вглиб: master-detail один на одному ”

Платформа має форму, і в master-detail вона має два виміри. Перший — глибина.

Master-detail можна вкладати на три користувацькі рівні detail углиб, і це дно (Considerations for Object Relationships (opens in a new tab)). На заняттях я малюю акаунт із замовленням (один), замовлення з позиціями (два) і позиції з їхніми деталями (три), а потім пробую додати четвертий, і Salesforce мені не дає. У Steve Trucking це могло б виглядати так: Delivery (доставка) — master; її Delivery Items (позиції доставки) — перший рівень; Install Tasks (завдання монтажу) позиції — другий; а Task Steps (кроки завдання) кожного завдання — третій. Четвертий рівень, скажімо Step Note (нотатка до кроку), мусить бути lookup.

Три рівні master-detail Три вкладені зв’язки master-detail у Steve Trucking, намальовані товстими лініями. Доставка — master. Позиція доставки — рівень detail 1, завдання встановлення — рівень 2, крок завдання — рівень 3. Нотатка кроку висить на кроці завдання на тонкій лінії lookup, бо четвертий рівень мусить бути lookup. Внизу: власність, спільний доступ і видалення йдуть униз від доставки до кожного рівня. рівень 1 рівень 2 рівень 3 четвертий рівень — lookup Доставка Delivery__c · master Позиція доставки Delivery_Item__c Завдання встановлення Install_Task__c Крок завдання Task_Step__c Нотатка кроку Step_Note__c Власність, спільний доступ і видалення йдуть униз від доставки до кожного рівня. рівно один нуль або один один або багато нуль або багато master-detail lookup © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

Разом зі стосом ідуть кілька правил:

  • Стандартні об’єкти не можуть бути на боці detail для користувацького об’єкта. Користувацький об’єкт може бути detail для Account; Account не може бути detail для вашого.
  • Об’єкт може з’явитися лише один раз у багаторівневому стосі. Salesforce не хоче, щоб ви замикали коло на собі: піддеталь не може водночас бути master для власного master.
  • Master не може зводити нижній рівень напряму. Якщо Delivery потрібна кількість Task Steps, Install Task зводить свої кроки, Delivery Item зводить ці зведення, а Delivery зводить позиції: по одному рівню за раз.
  • Власність і спільний доступ ідуть згори. Кожен рівень успадковує власника й доступ master.
  • Видалення master видаляє все під ним, а відновлення повертає все назад.

Трьох рівнів зазвичай досить. Якщо ваш дизайн усе хоче четвертий, запитайте, чи кожен рівень справді належить рівню над ним на все життя. І пам’ятайте, що ви самі обираєте, яка ланка розриває ланцюг. Четвертий рівень не мусить бути lookup: можливо, Install Task насправді не належить своїй Delivery Item, його планують і призначають окремо, тож ця ланка стає lookup, і тепер Install Task починає новий ланцюг, під яким Task Step і Step Note. Оберіть ланку, де дочірній запис справді має власне життя, і зробіть саме її lookup.

Розрив ланцюга через lookup Ті самі п’ять об’єктів Steve Trucking, що й у трирівневому стеку, з lookup посередині. Доставка — master, а позиція доставки — її detail на товстій лінії master-detail. Позиція доставки пов’язана із завданням встановлення тонкою лінією lookup, що розриває ланцюг. Завдання встановлення — новий master: крок завдання — його detail, а нотатка кроку — detail кроку завдання, обидва на товстих лініях master-detail. Внизу: тепер завдання встановлення має власного власника і спільний доступ, а видалення доставки більше не видаляє її завдань встановлення. рівень 1 lookup розриває ланцюг рівень 1 рівень 2 Доставка Delivery__c · master Позиція доставки Delivery_Item__c Завдання встановлення Install_Task__c · новий master Крок завдання Task_Step__c Нотатка кроку Step_Note__c Тепер завдання встановлення має власного власника і спільний доступ, а видалення доставки більше не видаляє її завдань встановлення. рівно один нуль або один один або багато нуль або багато master-detail lookup © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

Тепер Install Task має власного власника й власний спільний доступ, доставку можна видалити, не забираючи з нею завдання монтажу, а під Install Task можуть бути ще два рівні. Ось обмін: ланцюг коротший, а життя завдання монтажу — його власне.

Два батьківські вшир: об’єкт-з’єднувач Salesforce

Section titled “Два батьківські вшир: об’єкт-з’єднувач Salesforce ”

Спершу слово про слова, бо «об’єкт-з’єднувач» (junction object) означає дві речі, і я навчаю обох.

У розділі 2 ми знайшли логічний об’єкт-з’єднувач: поєднання двох ключових ідей, яке стає окремою річчю з власними атрибутами. Собака знає багато трюків, трюк знають багато собак, а Трюк собаки стоїть між ними й тримає рівень навички та дату. Це логічний об’єкт-з’єднувач, у логічній моделі, задовго до того, як хтось вирішить: lookup чи master-detail.

Salesforce вживає цей термін вужче. Коли Salesforce каже junction object, він має на увазі користувацький об’єкт із двома зв’язками master-detail (Create a Many-to-Many Object Relationship (opens in a new tab)). Я називатиму це об’єктом-з’єднувачем Salesforce (Salesforce Junction Object). Кожен об’єкт-з’єднувач Salesforce — логічний об’єкт-з’єднувач, але не кожен логічний об’єкт-з’єднувач стає об’єктом-з’єднувачем Salesforce: побудуйте його з одним master-detail і одним lookup або з двома lookup — і це все одно логічний об’єкт-з’єднувач, просто не того виду, що в Salesforce. (Сам Salesforce не цілком послідовний: його власний довідник об’єктів називає деякі стандартні об’єкти, побудовані на lookup, «junction objects». На іспиті виходьте з вузького визначення.)

Варто знати для іспиту. Об’єкт-з’єднувач Salesforce — це користувацький об’єкт із двома зв’язками master-detail, і саме це Salesforce має на увазі, коли каже «junction object». Логічний об’єкт-з’єднувач — ширша ідея моделювання: будь-який об’єкт, що поєднує два інші у зв’язку «багато до багатьох» і має власні атрибути, хоч би як його побудували.

Другий вимір — ширина. Чи може дочірній об’єкт мати двох батьків master-detail? Так. Трьох? Ні. Кожен користувацький об’єкт може мати щонайбільше два зв’язки master-detail (Considerations for Object Relationships (opens in a new tab)). Два батьківські master-detail — це те, як ви будуєте об’єкт-з’єднувач Salesforce для зв’язку «багато до багатьох». Бригада й вантажівка зустрічаються в призначенні бригади (Crew Assignment), і, як ми з’ясували в розділі 2, це не сантехніка: у нього є дата, маршрут, можливо, статус. З’єднувач — це місце, де зв’язок стає бізнес-концепцією. Поради Salesforce щодо його побудови: дайте йому назву, яка каже, що це таке, зробіть назву запису автонумерацією і перейменуйте пов’язаний список кожного master на інший master, щоб сторінка Crew показувала «Trucks», а сторінка Truck — «Crews» (Create a Many-to-Many Object Relationship (opens in a new tab)).

Ось де тонкощі. Один із двох батьківських — основний (primary), і це той, чиє поле master-detail ви створили першим. Візуально ніяк не видно, який саме, тож це класичне екзаменаційне запитання і класичний сюрприз.

Що визначає основний? Три речі (Create a Many-to-Many Object Relationship (opens in a new tab)):

  • Вигляд. Сторінки з’єднувача використовують колір та іконку основного master.
  • Власник. Записи з’єднувача успадковують власника основного запису master. Ви його ніколи не бачите, бо в detail немає поля Owner; це важить, лише якщо згодом ви приберете обидва зв’язки master-detail.
  • Division (підрозділ), якщо ваша org використовує підрозділи, — так само.

А інший — другорядний (secondary). Що це означає?

  • Він наступний у черзі, щоб стати основним. Видаліть основне поле master-detail або конвертуйте його на lookup — і другорядний стане основним.
  • Він задає обсяг звітів. Salesforce дає вам два типи звітів: «Primary master with junction object and secondary master» і «Secondary master with junction object and primary master». Master, указаний першим, визначає, які записи може показати звіт.
  • Для доступу він важить так само. Я кажу це так: власність — від основного, доступ — від обох, і перемагає найсуворіший. З’єднувач, як і будь-який detail, не має власної таблиці спільного доступу. Salesforce перевіряє доступ користувача до обох записів master через Sharing Setting у кожному полі master-detail. Якщо обидва встановлено на Read/Write, користувачеві потрібен Read/Write і до бригади, і до вантажівки, щоб редагувати Crew Assignment (Considerations for Object Relationships (opens in a new tab)).
  • Він так само видаляє. Видаліть будь-який master, бригаду чи вантажівку, — і його записи з’єднувача підуть у Recycle Bin (кошик). Видаліть обидва — і вони зникнуть назавжди.
Основний і другорядний master з’єднувача Об’єкт-з’єднувач під двома батьками master-detail, намальований товстими лініями. Бригада, вгорі ліворуч, — основний master, бо її поле master-detail створено першим; вона дає призначенню бригади власника і вигляд. Вантажівка, вгорі праворуч, — другорядний; вона визначає обсяг другого типу звіту і стає основною, якщо перший зв’язок видалити. Призначення бригади розташоване між ними й нижче. Внизу: для доступу потрібні обидва батьки, і перемагає найсуворіший; видаліть будь-якого з батьків, і призначення зникне теж. власник і вигляд обсяг звіту Бригада Crew__c · основний: створений першим Вантажівка Truck__c · другорядний Призначення бригади Crew_Assignment__c Для доступу потрібні обидва батьки: перемагає найсуворіший. Видаліть будь-якого з батьків, і призначення зникне теж. рівно один нуль або один один або багато нуль або багато master-detail © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

Ще кілька правил: обидва master можуть мати зведення із з’єднувача, а формули й правила перевірки з’єднувача можуть звертатися до полів обох. Але з’єднувач не може бути master для чогось іще, тож він не може стояти посередині стосу, і два master не можуть бути тим самим об’єктом; зв’язок «багато до багатьох» об’єкта з самим собою потребує іншого дизайну. А якщо батьківський запис має понад 200 записів з’єднувача і з’єднувач зводиться до іншого батьківського, Salesforce не дасть вам видалити цей батьківський запис, доки ви не приберете достатньо з них.

Отже: який батьківський має бути основним? Створіть першим той, що володіє змістом. Для Crew Assignment роботу виконує бригада, тож я створив би Crew першим. А потім запишіть це, бо ніщо на екрані не скаже цього наступній людині.

З усім тим, об’єктів-з’єднувачів Salesforce у мене зрештою небагато. Зазвичай об’єкт має одного батька master-detail, і це той самий вид даних: клієнтські дані під клієнтськими, транзакційні під транзакційними (згадайте види даних із розділу 2: клієнтські, транзакційні, довідкові). Коли інший батьківський — іншого виду, зазвичай довідковий об’єкт, цей зв’язок — lookup. Позиція доставки належить своїй доставці: master-detail, обидві транзакційні. На продукт, який вона везе, вона вказує через lookup, бо продукт — довідкові дані з власним життям, і ніхто не хоче, щоб видалення продукту забрало з собою роки історії доставок. Позиція доставки все одно лишається логічним об’єктом-з’єднувачем між доставкою і продуктами в ній; просто вона не об’єкт-з’єднувач Salesforce.

Зв’язки із собою: коли лінія повертається

Section titled “Зв’язки із собою: коли лінія повертається ”

Об’єкт може бути пов’язаний сам із собою: батьківський акаунт, керівник, розподільчий центр, підпорядкований регіональному хабу. Salesforce має два вбудовані, які ви зустрінете майже в кожній org: Account до Account, поле Parent Account, що будує ієрархію акаунтів, і Case до Case, Parent Case з модуля 2, що групує пов’язані звернення. Зв’язок із собою має бути lookup, і запис не може вказувати сам на себе, хоча два записи можуть вказувати один на одного (Considerations for Object Relationships (opens in a new tab)). В об’єкті User це окремий особливий тип — ієрархічний зв’язок, що використовується, наприклад, для керівника користувача. Механіка проста; важлива назва. Lookup з розподільчого центру на розподільчий центр під назвою Distribution_Center__c нікому не допомагає. Батьківський? Резервний? Для перевантаження? Називайте зв’язок за роллю, а не за об’єктом, на який він вказує.

Зв’язки із собою Три об’єкти, кожен із lookup на себе. Account через поле ParentId вказує на батьківський акаунт, що будує ієрархію акаунтів. Case через поле ParentId вказує на батьківське звернення. Розподільчий центр, користувацький об’єкт, вказує на свій регіональний хаб через поле з назвою Regional_Hub__c. Внизу: завжди lookup, ніколи не на той самий запис, і з назвою за роллю, яку він відіграє. батьківський акаунт батьківське звернення регіональний хаб Account Account · ParentId Case Case · ParentId Розподільчий центр Distribution_Center__c · Regional_Hub__c Завжди lookup, ніколи не на той самий запис, і з назвою за роллю, яку він відіграє. рівно один нуль або один один або багато нуль або багато lookup © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

Ієрархічний: власний зв’язок об’єкта User

Section titled “Ієрархічний: власний зв’язок об’єкта User ”

Цей зовсім невеликий, і це саме той маловідомий, унікальний факт, на який, я очікую, може спиратися іспит. Ієрархічний (hierarchical) — це особливий lookup, що існує лише в об’єкті User. Він пов’язує одного користувача з іншим, і Salesforce не дасть ланцюгові замкнутися: користувач не може вказувати на себе ні прямо, ні опосередковано (Object Relationships Overview (opens in a new tab)). Якщо керівник Maria — Dev, то керівником Dev не може бути Maria чи будь-хто, хто підпорядковується Maria.

У цьому й різниця зі зв’язками із собою вище. В Account чи користувацькому об’єкті два записи можуть вказувати один на одного. У User ієрархія мусить лишатися ієрархією. І це єдиний спосіб пов’язати User з User: звичайний lookup з User на себе не пропонується.

Один у вас уже є. Стандартне поле Manager у User — це ієрархічний зв’язок, і затвердження (approvals) можуть ним користуватися: надіслати цей запит керівникові того, хто подав. Можна створити й власний, скажімо Escalation Contact (контакт для ескалації) чи Backup Approver (резервний затверджувач), і затвердження може йти по ньому так само (ApprovalProcess, Metadata API (opens in a new tab)). Не плутайте його з ієрархією ролей, яка визначає доступ до записів. Ієрархічне поле — це просто зв’язок між користувачами; воно не надає доступу до жодного запису.

Варто знати для іспиту. Ієрархічний зв’язок існує лише в об’єкті User, як стандартне поле Manager, і користувач не може вказувати на себе ні прямо, ні опосередковано. Це не ієрархія ролей.

Зв’язки теж мають ліміти

Section titled “Зв’язки теж мають ліміти ”

Кожен об’єкт може мати щонайбільше два зв’язки master-detail. Поля lookup і master-detail разом рахуються як поля зв’язків: 40 на об’єкт за замовчуванням, щонайбільше 50 за запитом. Зведені поля: 25 на об’єкт за замовчуванням, щонайбільше 40 (Increase the Maximum Relationships Allowed per Object (opens in a new tab); Increase the Maximum Limit of Roll-Up Summary Fields (opens in a new tab)). Salesforce додає, що підвищення будь-якого з цих лімітів може вплинути на продуктивність.

Проєктувати під ці числа не треба; треба помічати, коли ви до них наближаєтеся. Об’єкт, на якому висить зв’язок за зв’язком, щось вам каже. Можливо, він справді такий центральний. А можливо, модель хибна. (І тут діє правило запасу з розділу 18: тримайтеся значно нижче ліміту, рахуючи на третій рік.)

Питання 10 000 дочірніх записів

Section titled “Питання 10 000 дочірніх записів ”

А ще буває батьківський запис із надто великою кількістю дочірніх. Власна рекомендація Salesforce — не перевищувати приблизно 10 000 дочірніх записів на один батьківський запис master-detail (Considerations for Object Relationships (opens in a new tab)), і її посібник з доступу до записів у масштабі каже те саме про зв’язки «батьківський–дочірній» загалом (Parent-Child Data Skew (opens in a new tab)). Запис 10 001 не зламає org. Але після цієї межі збереження дочірнього запису може блокувати батьківський, а зміни спільного доступу в батьківському прокочуються через кожен дочірній.

Тож коли я бачу один батьківський запис і величезну кількість дочірніх, я питаю: чому вони всі під тим самим батьківським, хто ними володіє, як їх запитують і яка автоматизація торкається батьківського запису? Усі доставки під одним акаунтом «Steve Trucking» перевищили б 10 000 за кілька днів. Невинний зв’язок «один до багатьох» стає проблемою перекосу. Наступний модуль коротко знайомить із перекосом, а частина 2 розглядає його глибоко.

Не проєктуйте лінію за функцією

Section titled “Не проєктуйте лінію за функцією ”

Ось звичка, якої я хочу від цього модуля. Не починайте з «мені потрібне зведення», «мені потрібна обов’язковість», «я хочу успадкований спільний доступ». Це підказки для реалізації. Починайте з бізнесу: чи має дочірній запис власне життя, хто ним володіє, чи може він існувати сам, чи може переміщатися, що станеться, коли батьківський зникне, чи несе зв’язок дані і наскільки великою може стати кожна сторона? Потім обирайте зв’язок, що реалізує ці відповіді.

Lookup, master-detail чи з’єднувач? Матриця трьох шаблонів зв’язків проти шести бізнес-запитань. Lookup: дочірній запис може існувати окремо, має власного власника, може змінювати батька і витримує понад 10 000 дочірніх на батька, хоча за перекосом усе ж треба стежити; за замовчуванням він не зникає разом із батьком (поле очищується або видалення блокується) і тримає одного батька на поле. Означає «пов’язано». Master-detail: батько обов’язковий, дочірній запис успадковує власника від батька, видаляється каскадно разом із батьком, змінює батька лише тоді, коли дозволено reparenting, а понад 10 000 дочірніх приносять блокування батька і перерахунки. Означає «належить». Об’єкт-з’єднувач Salesforce: йому потрібні обидва батьки, власника він бере від основного, видаляється разом із будь-яким із батьків і є єдиним шаблоном для «багато з обох боків». Означає бізнес-концепцію. Внизу: спершу відповідайте на бізнес-запитання; тип зв’язку випливає з них. ІСНУЄ ОКРЕМО СВІЙ ВЛАСНИК ІДЕ РАЗОМ ІЗ БАТЬКОМ ЗМІНЮЄ БАТЬКА БАГАТО З ОБОХ БОКІВ 10 000+ НА БАТЬКА ОЗНАЧАЄ Lookup Доставка → Маршрут очистити чи заблокувати, за замовчуванням один батько на поле за перекосом усе ж стежте ПОВ’ЯЗАНО Master-detail Доставка → Позиція доставки батько обов’язковий успадковує від батька каскадне видалення лише з увімкненим reparenting один master на поле блокування батька, перерахунки НАЛЕЖИТЬ Об’єкт-з’єднувач Salesforce Бригада + Вантажівка → Призначення бригади потрібні обидва батьки від основного видаляється з будь-яким батьком лише з увімкненим reparenting це його робота та сама межа 10 000 БІЗНЕС-КОНЦЕПЦІЯ Спершу відповідайте на бізнес-запитання. Тип зв’язку випливає з них; не починайте з функції, яку хочете. © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

За межами іспиту: що змінює ШІ. ШІ може запропонувати зв’язки за секунди. Він не скаже вам, чи бізнес колись видалятиме батьківський запис. Швидше малювання діаграм не робить хибний зв’язок дешевшим для скасування, коли від нього залежать мільйони записів і десятки автоматизацій.

Що тут вирішує архітектор

Section titled “Що тут вирішує архітектор ”
  • Чи має дочірній запис власний життєвий цикл, чи справді належить батьківському, і чи мають власність і доступ бути власними чи успадкованими.
  • Чи може дочірній запис існувати без батьківського, переходити між батьківськими чи пережити видалення батьківського.
  • Що робить кожен lookup, коли його батьківський запис видаляють: очищує, блокує чи видаляє каскадно.
  • Чи є зв’язок «багато до багатьох» сантехнікою чи бізнес-концепцією, чи стає логічний об’єкт-з’єднувач об’єктом-з’єднувачем Salesforce, який батьківський основний і що обидва батьківські означають для доступу.
  • Чи вписується модель у форму платформи: три користувацькі рівні detail углиб, два батьківські master-detail ушир, ліміти зв’язків і зведень.
  • Чи створюють очікувана кількість і розподіл дочірніх записів ризик перекосу, що має змінити дизайн.

Зв’язок — це набір обіцянок про те, як дві частини даних поводяться разом. Давайте їх свідомо.