1. Reuse core Dataverse tables
account, contact, systemuser, businessunit, team and transactioncurrency are used as-is. A custom customer table is an anti-pattern. Internal people are systemuser; external people are contact. Partner and supplier are a category on the account, not a separate table.
2. Power Pages portals anchor on Account and Contact
External identity attaches to contact, with account as the parent. Any table the portal touches must reach an account or contact by a lookup.
3. No Choice columns anywhere
Every dropdown is a lookup to a vocabulary table so administrators manage options in EPMS Admin without a deployment. Rows are deactivated, never deleted, so historic records keep resolving.
4. Security model
Global = the default (root) business unit. Regional = child business units, maximum depth 2. Country = Dataverse teams, never a third business-unit level, because Dataverse cannot give a user two business units.
5. Branch on seco_code, never on the row GUID
Lookup row GUIDs are generated per environment and differ between Dev, Test and Prod. A plugin or flow that branches on a GUID passes every test in Dev and fails silently in Prod, because the row it matched no longer exists under that id. Resolve the lookup, read seco_code, branch on the code. This applies to all 55 vocabulary tables, both tiers.
6. Portal access runs on contact and account
Every portal-facing table declares a path to an account or a contact through one of three chains — customer, partner, per-action. A table with no declared path is scope None and stays off the portal.